Browse Source

allow Class-Path attributes to be merged and optionally be flattened. PR 39655

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@806174 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 16 years ago
parent
commit
77024a43a2
9 changed files with 307 additions and 20 deletions
  1. +8
    -0
      WHATSNEW
  2. +19
    -0
      docs/manual/CoreTasks/ear.html
  3. +19
    -0
      docs/manual/CoreTasks/jar.html
  4. +19
    -0
      docs/manual/CoreTasks/manifest.html
  5. +19
    -0
      docs/manual/CoreTasks/war.html
  6. +37
    -7
      src/main/org/apache/tools/ant/taskdefs/Jar.java
  7. +109
    -10
      src/main/org/apache/tools/ant/taskdefs/Manifest.java
  8. +32
    -3
      src/main/org/apache/tools/ant/taskdefs/ManifestTask.java
  9. +45
    -0
      src/tests/antunit/taskdefs/manifest-test.xml

+ 8
- 0
WHATSNEW View File

@@ -439,6 +439,14 @@ Fixed bugs:
* The update attribute of the modified selector was ignored. * The update attribute of the modified selector was ignored.
Bugzilla Report 32597. 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: Other changes:
-------------- --------------
* The get task now also follows redirects from http to https * The get task now also follows redirects from http to https


+ 19
- 0
docs/manual/CoreTasks/ear.html View File

@@ -245,6 +245,25 @@ to a value other than its default, <code>&quot;add&quot;</code>.</b></p>
zip task page</a></td> zip task page</a></td>
<td align="center" valign="top">No, default is false</td> <td align="center" valign="top">No, default is false</td>
</tr> </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> </table>


<h3>Nested elements</h3> <h3>Nested elements</h3>


+ 19
- 0
docs/manual/CoreTasks/jar.html View File

@@ -301,6 +301,25 @@ to a value other than its default, <code>"add"</code>.</b></p>
zip task page</a></td> zip task page</a></td>
<td align="center" valign="top">No, default is false</td> <td align="center" valign="top">No, default is false</td>
</tr> </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> </table>


<h3>Nested elements</h3> <h3>Nested elements</h3>


+ 19
- 0
docs/manual/CoreTasks/manifest.html View File

@@ -82,6 +82,25 @@ line.</p>
manifest.</td> manifest.</td>
<td valign="top" align="center">No, defaults to UTF-8 encoding.</td> <td valign="top" align="center">No, defaults to UTF-8 encoding.</td>
</tr> </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> </table>


<h3>Nested elements</h3> <h3>Nested elements</h3>


+ 19
- 0
docs/manual/CoreTasks/war.html View File

@@ -248,6 +248,25 @@ to a value other than its default, <code>&quot;add&quot;</code>.</b></p>
zip task page</a></td> zip task page</a></td>
<td align="center" valign="top">No, default is false</td> <td align="center" valign="top">No, default is false</td>
</tr> </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> </table>


<h3>Nested elements</h3> <h3>Nested elements</h3>


+ 37
- 7
src/main/org/apache/tools/ant/taskdefs/Jar.java View File

@@ -157,6 +157,16 @@ public class Jar extends Zip {


// CheckStyle:LineLength ON // 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. * 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) { if (configuredManifest == null) {
configuredManifest = newManifest; configuredManifest = newManifest;
} else { } else {
configuredManifest.merge(newManifest);
configuredManifest.merge(newManifest, false, mergeClassPaths);
} }
savedConfiguredManifest = configuredManifest; 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. * Initialize the zip output stream.
@@ -512,11 +540,13 @@ public class Jar extends Zip {
*/ */


if (isInUpdateMode()) { 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; return finalManifest;


@@ -540,7 +570,7 @@ public class Jar extends Zip {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(baos, Manifest.JAR_ENCODING); OutputStreamWriter osw = new OutputStreamWriter(baos, Manifest.JAR_ENCODING);
PrintWriter writer = new PrintWriter(osw); PrintWriter writer = new PrintWriter(osw);
manifest.write(writer);
manifest.write(writer, flattenClassPaths);
if (writer.checkError()) { if (writer.checkError()) {
throw new IOException("Encountered an error writing the manifest"); throw new IOException("Encountered an error writing the manifest");
} }
@@ -724,7 +754,7 @@ public class Jar extends Zip {
if (filesetManifest == null) { if (filesetManifest == null) {
filesetManifest = newManifest; filesetManifest = newManifest;
} else { } else {
filesetManifest.merge(newManifest);
filesetManifest.merge(newManifest, false, mergeClassPaths);
} }
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new BuildException("Unsupported encoding while reading " throw new BuildException("Unsupported encoding while reading "


+ 109
- 10
src/main/org/apache/tools/ant/taskdefs/Manifest.java View File

@@ -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 * @param writer the Writer to which the attribute is written
* *
* @throws IOException if the attribute value cannot be written * @throws IOException if the attribute value cannot be written
*/ */
public void write(PrintWriter writer) throws IOException { 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();) { for (Enumeration e = getValues(); e.hasMoreElements();) {
writeValue(writer, (String) e.nextElement()); 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. * @param section the section to be merged with this one.
* *
* @throws ManifestException if the sections cannot be merged. * @throws ManifestException if the sections cannot be merged.
*/ */
public void merge(Section section) throws ManifestException { 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 if (name == null && section.getName() != null
|| name != null || name != null
&& !(name.equalsIgnoreCase(section.getName()))) { && !(name.equalsIgnoreCase(section.getName()))) {
@@ -484,7 +519,16 @@ public class Manifest {
} }


if (classpathAttribute != null) { 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); 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 * @param writer the Writer to which the section is written
* *
* @throws IOException if the section cannot be written * @throws IOException if the section cannot be written
*/ */
public void write(PrintWriter writer) throws IOException { 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) { if (name != null) {
Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name); Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
nameAttr.write(writer); nameAttr.write(writer);
@@ -511,7 +572,7 @@ public class Manifest {
while (e.hasMoreElements()) { while (e.hasMoreElements()) {
String key = (String) e.nextElement(); String key = (String) e.nextElement();
Attribute attribute = getAttribute(key); Attribute attribute = getAttribute(key);
attribute.write(writer);
attribute.write(writer, flatten);
} }
writer.print(EOL); writer.print(EOL);
} }
@@ -863,6 +924,7 @@ public class Manifest {


/** /**
* Merge the contents of the given manifest into this 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 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 * 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 other the Manifest to be merged with this one.
* @param overwriteMain whether to overwrite the main section * @param overwriteMain whether to overwrite the main section
@@ -885,11 +948,31 @@ public class Manifest {
*/ */
public void merge(Manifest other, boolean overwriteMain) public void merge(Manifest other, boolean overwriteMain)
throws ManifestException { 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 (other != null) {
if (overwriteMain) { if (overwriteMain) {
mainSection = (Section) other.mainSection.clone(); mainSection = (Section) other.mainSection.clone();
} else { } else {
mainSection.merge(other.mainSection);
mainSection.merge(other.mainSection, mergeClassPaths);
} }


if (other.manifestVersion != null) { if (other.manifestVersion != null) {
@@ -907,20 +990,36 @@ public class Manifest {
addConfiguredSection((Section) otherSection.clone()); addConfiguredSection((Section) otherSection.clone());
} }
} else { } 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. * Write the manifest out to a print writer.
* *
* @param writer the Writer to which the manifest is written * @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 * @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); writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL);
String signatureVersion String signatureVersion
= mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION); = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
@@ -929,7 +1028,7 @@ public class Manifest {
+ signatureVersion + EOL); + signatureVersion + EOL);
mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION); mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
} }
mainSection.write(writer);
mainSection.write(writer, flatten);


// add it back // add it back
if (signatureVersion != null) { if (signatureVersion != null) {
@@ -946,7 +1045,7 @@ public class Manifest {
while (e.hasMoreElements()) { while (e.hasMoreElements()) {
String sectionName = (String) e.nextElement(); String sectionName = (String) e.nextElement();
Section section = getSection(sectionName); Section section = getSection(sectionName);
section.write(writer);
section.write(writer, flatten);
} }
} }




+ 32
- 3
src/main/org/apache/tools/ant/taskdefs/ManifestTask.java View File

@@ -72,6 +72,16 @@ public class ManifestTask extends Task {
*/ */
private String encoding; 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. * Helper class for Manifest's mode attribute.
*/ */
@@ -183,6 +193,25 @@ public class ManifestTask extends Task {
mode = m; 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. * Create or update the Manifest when used as a task.
* *
@@ -228,13 +257,13 @@ public class ManifestTask extends Task {
try { try {
if (mode.getValue().equals("update") && manifestFile.exists()) { if (mode.getValue().equals("update") && manifestFile.exists()) {
if (current != null) { if (current != null) {
toWrite.merge(current);
toWrite.merge(current, false, mergeClassPaths);
} else if (error != null) { } else if (error != null) {
throw error; throw error;
} }
} }


toWrite.merge(nestedManifest);
toWrite.merge(nestedManifest, false, mergeClassPaths);
} catch (ManifestException m) { } catch (ManifestException m) {
throw new BuildException("Manifest is invalid", m, getLocation()); throw new BuildException("Manifest is invalid", m, getLocation());
} }
@@ -250,7 +279,7 @@ public class ManifestTask extends Task {
FileOutputStream fos = new FileOutputStream(manifestFile); FileOutputStream fos = new FileOutputStream(manifestFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, Manifest.JAR_ENCODING); OutputStreamWriter osw = new OutputStreamWriter(fos, Manifest.JAR_ENCODING);
w = new PrintWriter(osw); w = new PrintWriter(osw);
toWrite.write(w);
toWrite.write(w, flattenClassPaths);
if (w.checkError()) { if (w.checkError()) {
throw new IOException("Encountered an error writing manifest"); throw new IOException("Encountered an error writing manifest");
} }


+ 45
- 0
src/tests/antunit/taskdefs/manifest-test.xml View File

@@ -91,4 +91,49 @@
resource="${file}" resource="${file}"
value="Class-Path: bar&#13;&#10;"/> value="Class-Path: bar&#13;&#10;"/>
</target> </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&#13;&#10;"/>
<au:assertResourceContains
resource="${file}"
value="Class-Path: bar&#13;&#10;"/>
<au:assertResourceContains
resource="${file}"
value="Class-Path: baz&#13;&#10;"/>
</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&#13;&#10;"/>
</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&#13;&#10;"/>
</target>
</project> </project>

Loading…
Cancel
Save