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
"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+