git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@806174 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -439,6 +439,14 @@ Fixed bugs: | |||
| * The update attribute of the modified selector was ignored. | |||
| Bugzilla Report 32597. | |||
| * <manifest> and <jar> 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 | |||
| @@ -245,6 +245,25 @@ to a value other than its default, <code>"add"</code>.</b></p> | |||
| zip task page</a></td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">mergeClassPathAttributes</td> | |||
| <td valign="top">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. | |||
| <em>Since Ant 1.8.0</em>. | |||
| <br/>unless you also set flattenAttributes to true this may | |||
| result in manifests containing multiple Class-Path attributes | |||
| which violates the manifest specification.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">flattenAttributes</td> | |||
| <td valign="top">Whether to merge attributes occuring more than | |||
| once in a section (this can only happen for the Class-Path | |||
| attribute) into a single attribute. | |||
| <em>Since Ant 1.8.0</em>.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| </table> | |||
| <h3>Nested elements</h3> | |||
| @@ -301,6 +301,25 @@ to a value other than its default, <code>"add"</code>.</b></p> | |||
| zip task page</a></td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">mergeClassPathAttributes</td> | |||
| <td valign="top">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. | |||
| <em>Since Ant 1.8.0</em>. | |||
| <br/>unless you also set flattenAttributes to true this may | |||
| result in manifests containing multiple Class-Path attributes | |||
| which violates the manifest specification.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">flattenAttributes</td> | |||
| <td valign="top">Whether to merge attributes occuring more than | |||
| once in a section (this can only happen for the Class-Path | |||
| attribute) into a single attribute. | |||
| <em>Since Ant 1.8.0</em>.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| </table> | |||
| <h3>Nested elements</h3> | |||
| @@ -82,6 +82,25 @@ line.</p> | |||
| manifest.</td> | |||
| <td valign="top" align="center">No, defaults to UTF-8 encoding.</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">mergeClassPathAttributes</td> | |||
| <td valign="top">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. | |||
| <em>Since Ant 1.8.0</em>. | |||
| <br/>unless you also set flattenAttributes to true this may | |||
| result in manifests containing multiple Class-Path attributes | |||
| which violates the manifest specification.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">flattenAttributes</td> | |||
| <td valign="top">Whether to merge attributes occuring more than | |||
| once in a section (this can only happen for the Class-Path | |||
| attribute) into a single attribute. | |||
| <em>Since Ant 1.8.0</em>.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| </table> | |||
| <h3>Nested elements</h3> | |||
| @@ -248,6 +248,25 @@ to a value other than its default, <code>"add"</code>.</b></p> | |||
| zip task page</a></td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">mergeClassPathAttributes</td> | |||
| <td valign="top">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. | |||
| <em>Since Ant 1.8.0</em>. | |||
| <br/>unless you also set flattenAttributes to true this may | |||
| result in manifests containing multiple Class-Path attributes | |||
| which violates the manifest specification.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| <tr> | |||
| <td valign="top">flattenAttributes</td> | |||
| <td valign="top">Whether to merge attributes occuring more than | |||
| once in a section (this can only happen for the Class-Path | |||
| attribute) into a single attribute. | |||
| <em>Since Ant 1.8.0</em>.</td> | |||
| <td align="center" valign="top">No, default is false</td> | |||
| </tr> | |||
| </table> | |||
| <h3>Nested elements</h3> | |||
| @@ -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 " | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -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"); | |||
| } | |||
| @@ -91,4 +91,49 @@ | |||
| resource="${file}" | |||
| value="Class-Path: bar "/> | |||
| </target> | |||
| <target name="testMergeClassPathAttributes" depends="setUp"> | |||
| <manifest file="${file}"> | |||
| <attribute name="Class-Path" value="foo"/> | |||
| <attribute name="Class-Path" value="bar"/> | |||
| </manifest> | |||
| <manifest file="${file}" mergeClassPathAttributes="true" mode="update"> | |||
| <attribute name="Class-Path" value="baz"/> | |||
| </manifest> | |||
| <au:assertResourceContains | |||
| resource="${file}" | |||
| value="Class-Path: foo "/> | |||
| <au:assertResourceContains | |||
| resource="${file}" | |||
| value="Class-Path: bar "/> | |||
| <au:assertResourceContains | |||
| resource="${file}" | |||
| value="Class-Path: baz "/> | |||
| </target> | |||
| <target name="testFlattenMultipleClassPathAttributes" depends="setUp"> | |||
| <manifest file="${file}" flattenAttributes="true"> | |||
| <attribute name="Class-Path" value="foo"/> | |||
| <attribute name="Class-Path" value="bar"/> | |||
| </manifest> | |||
| <au:assertResourceContains | |||
| resource="${file}" | |||
| value="Class-Path: foo bar "/> | |||
| </target> | |||
| <target name="testMergeAndFlattenClassPathAttributes" depends="setUp"> | |||
| <manifest file="${file}"> | |||
| <attribute name="Class-Path" value="foo"/> | |||
| <attribute name="Class-Path" value="bar"/> | |||
| </manifest> | |||
| <manifest file="${file}" | |||
| mergeClassPathAttributes="true" | |||
| flattenAttributes="true" | |||
| mode="update"> | |||
| <attribute name="Class-Path" value="baz"/> | |||
| </manifest> | |||
| <au:assertResourceContains | |||
| resource="${file}" | |||
| value="Class-Path: baz foo bar "/> | |||
| </target> | |||
| </project> | |||