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> |