diff --git a/WHATSNEW b/WHATSNEW index 3d68d1d5f..a6100d158 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -439,6 +439,14 @@ Fixed bugs: * The update attribute of the modified selector was ignored. Bugzilla Report 32597. + * and can now merge Class-Path attributes from + multiple sources and optionally flatten them into a single + attribute. + The default behaviour still is to keep multiple Class-Path + attributes if they have been specified and to only include the + attributes of the last merged manifest. + Bugzilla Report 39655. + Other changes: -------------- * The get task now also follows redirects from http to https diff --git a/docs/manual/CoreTasks/ear.html b/docs/manual/CoreTasks/ear.html index 9ef737778..73c4ca88a 100644 --- a/docs/manual/CoreTasks/ear.html +++ b/docs/manual/CoreTasks/ear.html @@ -245,6 +245,25 @@ to a value other than its default, "add".

zip task page No, default is false + + mergeClassPathAttributes + Whether to merge the Class-Path attributes found + in different manifests (if merging manifests). If false, only + the attribute of the last merged manifest will be preserved. + Since Ant 1.8.0. +
unless you also set flattenAttributes to true this may + result in manifests containing multiple Class-Path attributes + which violates the manifest specification. + No, default is false + + + flattenAttributes + Whether to merge attributes occuring more than + once in a section (this can only happen for the Class-Path + attribute) into a single attribute. + Since Ant 1.8.0. + No, default is false +

Nested elements

diff --git a/docs/manual/CoreTasks/jar.html b/docs/manual/CoreTasks/jar.html index 806b5acda..5cfe3745e 100644 --- a/docs/manual/CoreTasks/jar.html +++ b/docs/manual/CoreTasks/jar.html @@ -301,6 +301,25 @@ to a value other than its default, "add".

zip task page No, default is false + + mergeClassPathAttributes + Whether to merge the Class-Path attributes found + in different manifests (if merging manifests). If false, only + the attribute of the last merged manifest will be preserved. + Since Ant 1.8.0. +
unless you also set flattenAttributes to true this may + result in manifests containing multiple Class-Path attributes + which violates the manifest specification. + No, default is false + + + flattenAttributes + Whether to merge attributes occuring more than + once in a section (this can only happen for the Class-Path + attribute) into a single attribute. + Since Ant 1.8.0. + No, default is false +

Nested elements

diff --git a/docs/manual/CoreTasks/manifest.html b/docs/manual/CoreTasks/manifest.html index 23e323d72..2e182fec0 100644 --- a/docs/manual/CoreTasks/manifest.html +++ b/docs/manual/CoreTasks/manifest.html @@ -82,6 +82,25 @@ line.

manifest. No, defaults to UTF-8 encoding. + + mergeClassPathAttributes + Whether to merge the Class-Path attributes found + in different manifests (if updating). If false, only the + attribute of the most recent manifest will be preserved. + Since Ant 1.8.0. +
unless you also set flattenAttributes to true this may + result in manifests containing multiple Class-Path attributes + which violates the manifest specification. + No, default is false + + + flattenAttributes + Whether to merge attributes occuring more than + once in a section (this can only happen for the Class-Path + attribute) into a single attribute. + Since Ant 1.8.0. + No, default is false +

Nested elements

diff --git a/docs/manual/CoreTasks/war.html b/docs/manual/CoreTasks/war.html index 15d2430a9..fcd7cecc9 100644 --- a/docs/manual/CoreTasks/war.html +++ b/docs/manual/CoreTasks/war.html @@ -248,6 +248,25 @@ to a value other than its default, "add".

zip task page No, default is false + + mergeClassPathAttributes + Whether to merge the Class-Path attributes found + in different manifests (if merging manifests). If false, only + the attribute of the last merged manifest will be preserved. + Since Ant 1.8.0. +
unless you also set flattenAttributes to true this may + result in manifests containing multiple Class-Path attributes + which violates the manifest specification. + No, default is false + + + flattenAttributes + Whether to merge attributes occuring more than + once in a section (this can only happen for the Class-Path + attribute) into a single attribute. + Since Ant 1.8.0. + No, default is false +

Nested elements

diff --git a/src/main/org/apache/tools/ant/taskdefs/Jar.java b/src/main/org/apache/tools/ant/taskdefs/Jar.java index 670cee860..6b73ac154 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Jar.java +++ b/src/main/org/apache/tools/ant/taskdefs/Jar.java @@ -157,6 +157,16 @@ public class Jar extends Zip { // CheckStyle:LineLength ON + /** + * whether to merge Class-Path attributes. + */ + private boolean mergeClassPaths = false; + + /** + * whether to flatten Class-Path attributes into a single one. + */ + private boolean flattenClassPaths = false; + /** * Extra fields needed to make Solaris recognize the archive as a jar file. * @@ -268,7 +278,7 @@ public class Jar extends Zip { if (configuredManifest == null) { configuredManifest = newManifest; } else { - configuredManifest.merge(newManifest); + configuredManifest.merge(newManifest, false, mergeClassPaths); } savedConfiguredManifest = configuredManifest; } @@ -473,6 +483,24 @@ public class Jar extends Zip { } } + /** + * Whether to merge Class-Path attributes. + * + * @since Ant 1.8.0 + */ + public void setMergeClassPathAttributes(boolean b) { + mergeClassPaths = b; + } + + /** + * Whether to flatten multi-valued attributes (i.e. Class-Path) + * into a single one. + * + * @since Ant 1.8.0 + */ + public void setFlattenAttributes(boolean b) { + flattenClassPaths = b; + } /** * Initialize the zip output stream. @@ -512,11 +540,13 @@ public class Jar extends Zip { */ if (isInUpdateMode()) { - finalManifest.merge(originalManifest); + finalManifest.merge(originalManifest, false, mergeClassPaths); } - finalManifest.merge(filesetManifest); - finalManifest.merge(configuredManifest, !mergeManifestsMain); - finalManifest.merge(manifest, !mergeManifestsMain); + finalManifest.merge(filesetManifest, false, mergeClassPaths); + finalManifest.merge(configuredManifest, !mergeManifestsMain, + mergeClassPaths); + finalManifest.merge(manifest, !mergeManifestsMain, + mergeClassPaths); return finalManifest; @@ -540,7 +570,7 @@ public class Jar extends Zip { ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(baos, Manifest.JAR_ENCODING); PrintWriter writer = new PrintWriter(osw); - manifest.write(writer); + manifest.write(writer, flattenClassPaths); if (writer.checkError()) { throw new IOException("Encountered an error writing the manifest"); } @@ -724,7 +754,7 @@ public class Jar extends Zip { if (filesetManifest == null) { filesetManifest = newManifest; } else { - filesetManifest.merge(newManifest); + filesetManifest.merge(newManifest, false, mergeClassPaths); } } catch (UnsupportedEncodingException e) { throw new BuildException("Unsupported encoding while reading " diff --git a/src/main/org/apache/tools/ant/taskdefs/Manifest.java b/src/main/org/apache/tools/ant/taskdefs/Manifest.java index 378dcfbdb..7d164cda0 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Manifest.java +++ b/src/main/org/apache/tools/ant/taskdefs/Manifest.java @@ -304,16 +304,37 @@ public class Manifest { } /** - * Write the attribute out to a print writer. + * Write the attribute out to a print writer without + * flattening multi-values attributes (i.e. Class-Path). * * @param writer the Writer to which the attribute is written * * @throws IOException if the attribute value cannot be written */ public void write(PrintWriter writer) throws IOException { + write(writer, false); + } + + /** + * Write the attribute out to a print writer. + * + * @param writer the Writer to which the attribute is written + * @param flatten whether to collapse multi-valued attributes + * (i.e. potentially Class-Path) Class-Path into a + * single attribute. + * + * @throws IOException if the attribute value cannot be written + * @since Ant 1.8.0 + */ + public void write(PrintWriter writer, boolean flatten) + throws IOException { + if (!flatten) { for (Enumeration e = getValues(); e.hasMoreElements();) { writeValue(writer, (String) e.nextElement()); } + } else { + writeValue(writer, getValue()); + } } /** @@ -448,13 +469,27 @@ public class Manifest { } /** - * Merge in another section + * Merge in another section without merging Class-Path attributes. * * @param section the section to be merged with this one. * * @throws ManifestException if the sections cannot be merged. */ public void merge(Section section) throws ManifestException { + merge(section, false); + } + + /** + * Merge in another section + * + * @param section the section to be merged with this one. + * @param mergeClassPaths whether Class-Path attributes should + * be merged. + * + * @throws ManifestException if the sections cannot be merged. + */ + public void merge(Section section, boolean mergeClassPaths) + throws ManifestException { if (name == null && section.getName() != null || name != null && !(name.equalsIgnoreCase(section.getName()))) { @@ -484,7 +519,16 @@ public class Manifest { } if (classpathAttribute != null) { - // the merge file *always* wins, even for Class-Path + if (mergeClassPaths) { + Attribute currentCp = getAttribute(ATTRIBUTE_CLASSPATH); + if (currentCp != null) { + for (Enumeration attribEnum = currentCp.getValues(); + attribEnum.hasMoreElements(); ) { + String value = (String) attribEnum.nextElement(); + classpathAttribute.addValue(value); + } + } + } storeAttribute(classpathAttribute); } @@ -496,13 +540,30 @@ public class Manifest { } /** - * Write the section out to a print writer. + * Write the section out to a print writer without flattening + * multi-values attributes (i.e. Class-Path). * * @param writer the Writer to which the section is written * * @throws IOException if the section cannot be written */ public void write(PrintWriter writer) throws IOException { + write(writer, false); + } + + /** + * Write the section out to a print writer. + * + * @param writer the Writer to which the section is written + * @param flatten whether to collapse multi-valued attributes + * (i.e. potentially Class-Path) Class-Path into a + * single attribute. + * + * @throws IOException if the section cannot be written + * @since Ant 1.8.0 + */ + public void write(PrintWriter writer, boolean flatten) + throws IOException { if (name != null) { Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name); nameAttr.write(writer); @@ -511,7 +572,7 @@ public class Manifest { while (e.hasMoreElements()) { String key = (String) e.nextElement(); Attribute attribute = getAttribute(key); - attribute.write(writer); + attribute.write(writer, flatten); } writer.print(EOL); } @@ -863,6 +924,7 @@ public class Manifest { /** * Merge the contents of the given manifest into this manifest + * without merging Class-Path attributes. * * @param other the Manifest to be merged with this one. * @@ -875,6 +937,7 @@ public class Manifest { /** * Merge the contents of the given manifest into this manifest + * without merging Class-Path attributes. * * @param other the Manifest to be merged with this one. * @param overwriteMain whether to overwrite the main section @@ -885,11 +948,31 @@ public class Manifest { */ public void merge(Manifest other, boolean overwriteMain) throws ManifestException { + merge(other, overwriteMain, false); + } + + /** + * Merge the contents of the given manifest into this manifest + * + * @param other the Manifest to be merged with this one. + * @param overwriteMain whether to overwrite the main section + * of the current manifest + * @param mergeClassPaths whether Class-Path attributes should be + * merged. + * + * @throws ManifestException if there is a problem merging the + * manifest according to the Manifest spec. + * + * @since Ant 1.8.0 + */ + public void merge(Manifest other, boolean overwriteMain, + boolean mergeClassPaths) + throws ManifestException { if (other != null) { if (overwriteMain) { mainSection = (Section) other.mainSection.clone(); } else { - mainSection.merge(other.mainSection); + mainSection.merge(other.mainSection, mergeClassPaths); } if (other.manifestVersion != null) { @@ -907,20 +990,36 @@ public class Manifest { addConfiguredSection((Section) otherSection.clone()); } } else { - ourSection.merge(otherSection); + ourSection.merge(otherSection, mergeClassPaths); } } } } + /** + * Write the manifest out to a print writer without flattening + * multi-values attributes (i.e. Class-Path). + * + * @param writer the Writer to which the manifest is written + * + * @throws IOException if the manifest cannot be written + */ + public void write(PrintWriter writer) throws IOException { + write(writer, false); + } + /** * Write the manifest out to a print writer. * * @param writer the Writer to which the manifest is written + * @param flatten whether to collapse multi-valued attributes + * (i.e. potentially Class-Path) Class-Path into a single + * attribute. * * @throws IOException if the manifest cannot be written + * @since Ant 1.8.0 */ - public void write(PrintWriter writer) throws IOException { + public void write(PrintWriter writer, boolean flatten) throws IOException { writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL); String signatureVersion = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION); @@ -929,7 +1028,7 @@ public class Manifest { + signatureVersion + EOL); mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION); } - mainSection.write(writer); + mainSection.write(writer, flatten); // add it back if (signatureVersion != null) { @@ -946,7 +1045,7 @@ public class Manifest { while (e.hasMoreElements()) { String sectionName = (String) e.nextElement(); Section section = getSection(sectionName); - section.write(writer); + section.write(writer, flatten); } } diff --git a/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java b/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java index 10bde8b6e..b30c5c5ff 100644 --- a/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java @@ -72,6 +72,16 @@ public class ManifestTask extends Task { */ private String encoding; + /** + * whether to merge Class-Path attributes. + */ + private boolean mergeClassPaths = false; + + /** + * whether to flatten Class-Path attributes into a single one. + */ + private boolean flattenClassPaths = false; + /** * Helper class for Manifest's mode attribute. */ @@ -183,6 +193,25 @@ public class ManifestTask extends Task { mode = m; } + /** + * Whether to merge Class-Path attributes. + * + * @since Ant 1.8.0 + */ + public void setMergeClassPathAttributes(boolean b) { + mergeClassPaths = b; + } + + /** + * Whether to flatten multi-valued attributes (i.e. Class-Path) + * into a single one. + * + * @since Ant 1.8.0 + */ + public void setFlattenAttributes(boolean b) { + flattenClassPaths = b; + } + /** * Create or update the Manifest when used as a task. * @@ -228,13 +257,13 @@ public class ManifestTask extends Task { try { if (mode.getValue().equals("update") && manifestFile.exists()) { if (current != null) { - toWrite.merge(current); + toWrite.merge(current, false, mergeClassPaths); } else if (error != null) { throw error; } } - toWrite.merge(nestedManifest); + toWrite.merge(nestedManifest, false, mergeClassPaths); } catch (ManifestException m) { throw new BuildException("Manifest is invalid", m, getLocation()); } @@ -250,7 +279,7 @@ public class ManifestTask extends Task { FileOutputStream fos = new FileOutputStream(manifestFile); OutputStreamWriter osw = new OutputStreamWriter(fos, Manifest.JAR_ENCODING); w = new PrintWriter(osw); - toWrite.write(w); + toWrite.write(w, flattenClassPaths); if (w.checkError()) { throw new IOException("Encountered an error writing manifest"); } diff --git a/src/tests/antunit/taskdefs/manifest-test.xml b/src/tests/antunit/taskdefs/manifest-test.xml index bebca3c7c..41d88b443 100644 --- a/src/tests/antunit/taskdefs/manifest-test.xml +++ b/src/tests/antunit/taskdefs/manifest-test.xml @@ -91,4 +91,49 @@ resource="${file}" value="Class-Path: bar "/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +