@@ -349,6 +349,14 @@ names used for the archives depend on your manifest:</p> | |||
inside the <samp>META-INF</samp> directory unless the <var>indexmetainf</var> attribute has been set | |||
to <q>true</q>.</p> | |||
<h4 id="indexjarsmapper">indexjarsmapper</h4> | |||
<p><em>Since Ant 1.10.9</em></p> | |||
<p>The nested <code>indexjarsmapper</code> element can be used to perform custom filename | |||
transformations for the archives specified by <code>indexjars</code> if the | |||
<a href="#indexjars">default filename transformation</a> doesn't suffice. | |||
<h4 id="service">service</h4> | |||
<p><em>Since Ant 1.7.0</em></p> | |||
@@ -248,7 +248,22 @@ | |||
</indexjars> | |||
</jar> | |||
</target> | |||
<target name="testIndexJarsPlusJarMarkerWithMapping"> | |||
<mkdir dir="${tmp.dir}/a/b/c"/> | |||
<jar destfile="${tmp.jar}" basedir="${tmp.dir}"/> | |||
<delete dir="${tmp.dir}/a" quiet="true"/> | |||
<mkdir dir="${tmp.dir}/d/e/f"/> | |||
<jar destfile="${tmp.jar}2" basedir="${tmp.dir}" index="true"> | |||
<indexjars> | |||
<fileset file="${tmp.jar}"/> | |||
</indexjars> | |||
<indexjarsmapper> | |||
<globmapper from="${output}/*" to="foo/*" handledirsep="true"/> | |||
</indexjarsmapper> | |||
</jar> | |||
</target> | |||
<target name="testNoVersionInfoNoStrict"> | |||
<mkdir dir="${tmp.dir}"/> | |||
<jar destfile="${tmp.jar}" basedir="${tmp.dir}"/> | |||
@@ -50,11 +50,13 @@ import org.apache.tools.ant.taskdefs.Manifest.Section; | |||
import org.apache.tools.ant.types.ArchiveFileSet; | |||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||
import org.apache.tools.ant.types.FileSet; | |||
import org.apache.tools.ant.types.Mapper; | |||
import org.apache.tools.ant.types.Path; | |||
import org.apache.tools.ant.types.Resource; | |||
import org.apache.tools.ant.types.ResourceCollection; | |||
import org.apache.tools.ant.types.ZipFileSet; | |||
import org.apache.tools.ant.types.spi.Service; | |||
import org.apache.tools.ant.util.FileNameMapper; | |||
import org.apache.tools.ant.util.FileUtils; | |||
import org.apache.tools.ant.util.StreamUtils; | |||
import org.apache.tools.zip.JarMarker; | |||
@@ -151,6 +153,14 @@ public class Jar extends Zip { | |||
*/ | |||
private Path indexJars; | |||
/** | |||
* A mapper used to convert the jars to entries in the index. | |||
* | |||
* @since Ant 1.10.9 | |||
*/ | |||
private FileNameMapper indexJarsMapper = null; | |||
// CheckStyle:LineLength OFF - Link is too long. | |||
/** | |||
* Strict mode for checking rules of the JAR-Specification. | |||
@@ -405,6 +415,30 @@ public class Jar extends Zip { | |||
indexJars.append(p); | |||
} | |||
/** | |||
* Add a mapper used to convert the jars to entries in the index. | |||
* | |||
* @param mapper a mapper | |||
* @since Ant 1.10.9 | |||
*/ | |||
public void addConfiguredIndexJarsMapper(Mapper mapper) { | |||
if (indexJarsMapper != null) { | |||
throw new BuildException("Cannot define more than one indexjar-mapper", | |||
getLocation()); | |||
} | |||
indexJarsMapper = mapper.getImplementation(); | |||
} | |||
/** | |||
* Returns the mapper used to convert the jars to entries in the index. May be null. | |||
* | |||
* @since Ant 1.10.9 | |||
*/ | |||
public FileNameMapper getIndexJarsMapper() { | |||
return indexJarsMapper; | |||
} | |||
/** | |||
* A nested SPI service element. | |||
* @param service the nested element. | |||
@@ -597,27 +631,18 @@ public class Jar extends Zip { | |||
writer.println(); | |||
if (indexJars != null) { | |||
Manifest mf = createManifest(); | |||
Manifest.Attribute classpath = | |||
mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH); | |||
String[] cpEntries = null; | |||
if (classpath != null && classpath.getValue() != null) { | |||
StringTokenizer tok = new StringTokenizer(classpath.getValue(), | |||
" "); | |||
cpEntries = new String[tok.countTokens()]; | |||
int c = 0; | |||
while (tok.hasMoreTokens()) { | |||
cpEntries[c++] = tok.nextToken(); | |||
} | |||
FileNameMapper mapper = indexJarsMapper; | |||
if (mapper == null) { | |||
mapper = createDefaultIndexJarsMapper(); | |||
} | |||
for (String indexJarEntry : indexJars.list()) { | |||
String name = findJarName(indexJarEntry, cpEntries); | |||
if (name != null) { | |||
String[] names = mapper.mapFileName(indexJarEntry); | |||
if (names != null && names.length > 0) { | |||
ArrayList<String> dirs = new ArrayList<>(); | |||
ArrayList<String> files = new ArrayList<>(); | |||
grabFilesAndDirs(indexJarEntry, dirs, files); | |||
if (dirs.size() + files.size() > 0) { | |||
writer.println(name); | |||
writer.println(names[0]); | |||
writeIndexLikeList(dirs, files, writer); | |||
writer.println(); | |||
} | |||
@@ -636,6 +661,31 @@ public class Jar extends Zip { | |||
} | |||
} | |||
/** | |||
* Creates a mapper for the index based on the classpath attribute in the manifest. | |||
* See {@link #findJarName(String, String[])} for more details. | |||
* | |||
* @return a mapper | |||
* @since Ant 1.10.9 | |||
*/ | |||
private FileNameMapper createDefaultIndexJarsMapper() { | |||
Manifest mf = createManifest(); | |||
Manifest.Attribute classpath = | |||
mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH); | |||
String[] cpEntries = null; | |||
if (classpath != null && classpath.getValue() != null) { | |||
StringTokenizer tok = new StringTokenizer(classpath.getValue(), | |||
" "); | |||
cpEntries = new String[tok.countTokens()]; | |||
int c = 0; | |||
while (tok.hasMoreTokens()) { | |||
cpEntries[c++] = tok.nextToken(); | |||
} | |||
} | |||
return new IndexJarsFilenameMapper(cpEntries); | |||
} | |||
/** | |||
* Overridden from Zip class to deal with manifests and index lists. | |||
* @param is the stream to read data for the entry from. The | |||
@@ -1162,4 +1212,39 @@ public class Jar extends Zip { | |||
return "ignore".equals(getValue()) ? Project.MSG_VERBOSE : Project.MSG_WARN; | |||
} | |||
} | |||
/** | |||
* A mapper for the index based on the classpath attribute in the manifest. | |||
* See {@link #findJarName(String, String[])} for more details. | |||
* | |||
* @since Ant 1.10.9 | |||
*/ | |||
private static class IndexJarsFilenameMapper implements FileNameMapper { | |||
private String[] classpath; | |||
IndexJarsFilenameMapper(String[] classpath) { | |||
this.classpath = classpath; | |||
} | |||
/** | |||
* Empty implementation. | |||
*/ | |||
@Override | |||
public void setFrom(String from) { | |||
} | |||
/** | |||
* Empty implementation. | |||
*/ | |||
@Override | |||
public void setTo(String to) { | |||
} | |||
@Override | |||
public String[] mapFileName(String sourceFileName) { | |||
String result = findJarName(sourceFileName, classpath); | |||
return result == null ? null : new String[] {result}; | |||
} | |||
} | |||
} |
@@ -253,6 +253,7 @@ public class JarTest { | |||
ZipEntry ze = archive.getEntry("META-INF/INDEX.LIST"); | |||
InputStream is = archive.getInputStream(ze); | |||
BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); | |||
boolean foundArchive = false; | |||
boolean foundSub = false; | |||
boolean foundSubFoo = false; | |||
boolean foundFoo = false; | |||
@@ -260,6 +261,9 @@ public class JarTest { | |||
String line = r.readLine(); | |||
while (line != null) { | |||
switch (line) { | |||
case "tmp.jar": | |||
foundArchive = true; | |||
break; | |||
case "foo": | |||
foundFoo = true; | |||
break; | |||
@@ -273,6 +277,7 @@ public class JarTest { | |||
line = r.readLine(); | |||
} | |||
assertTrue(foundArchive); | |||
assertTrue(foundSub); | |||
assertFalse(foundSubFoo); | |||
assertTrue(foundFoo); | |||
@@ -287,8 +292,126 @@ public class JarTest { | |||
} | |||
@Test | |||
public void testIndexJarsPlusJarMarker() { | |||
public void testIndexJarsPlusJarMarker() throws IOException { | |||
buildRule.executeTarget("testIndexJarsPlusJarMarker"); | |||
try (ZipFile archive = new ZipFile(new File(getOutputDir(), tempJar + "2"))) { | |||
ZipEntry ze = archive.getEntry("META-INF/INDEX.LIST"); | |||
InputStream is = archive.getInputStream(ze); | |||
BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); | |||
// tmp.jar | |||
boolean foundTmp = false; | |||
boolean foundA = false; | |||
boolean foundAB = false; | |||
boolean foundABC = false; | |||
// tmp2.jar | |||
boolean foundTmp2 = false; | |||
boolean foundD = false; | |||
boolean foundDE = false; | |||
boolean foundDEF = false; | |||
String line = r.readLine(); | |||
while (line != null) { | |||
switch (line) { | |||
case "tmp.jar": | |||
foundTmp = true; | |||
break; | |||
case "a": | |||
foundA = true; | |||
break; | |||
case "a/b": | |||
foundAB = true; | |||
break; | |||
case "a/b/c": | |||
foundABC = true; | |||
break; | |||
case "tmp.jar2": | |||
foundTmp2 = true; | |||
break; | |||
case "d": | |||
foundD = true; | |||
break; | |||
case "d/e": | |||
foundDE = true; | |||
break; | |||
case "d/e/f": | |||
foundDEF = true; | |||
break; | |||
} | |||
line = r.readLine(); | |||
} | |||
assertTrue(foundTmp); | |||
assertTrue(foundA); | |||
assertTrue(foundAB); | |||
assertTrue(foundABC); | |||
assertTrue(foundTmp2); | |||
assertTrue(foundD); | |||
assertTrue(foundDE); | |||
assertTrue(foundDEF); | |||
} | |||
} | |||
@Test | |||
public void testIndexJarsPlusJarMarkerWithMapping() throws IOException { | |||
buildRule.executeTarget("testIndexJarsPlusJarMarkerWithMapping"); | |||
try (ZipFile archive = new ZipFile(new File(getOutputDir(), tempJar + "2"))) { | |||
ZipEntry ze = archive.getEntry("META-INF/INDEX.LIST"); | |||
InputStream is = archive.getInputStream(ze); | |||
BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); | |||
// tmp.jar | |||
boolean foundTmp = false; | |||
boolean foundA = false; | |||
boolean foundAB = false; | |||
boolean foundABC = false; | |||
// tmp2.jar | |||
boolean foundTmp2 = false; | |||
boolean foundD = false; | |||
boolean foundDE = false; | |||
boolean foundDEF = false; | |||
String line = r.readLine(); | |||
while (line != null) { | |||
System.out.println("line = " + line); | |||
switch (line) { | |||
case "foo/tmp.jar": | |||
foundTmp = true; | |||
break; | |||
case "a": | |||
foundA = true; | |||
break; | |||
case "a/b": | |||
foundAB = true; | |||
break; | |||
case "a/b/c": | |||
foundABC = true; | |||
break; | |||
case "tmp.jar2": | |||
foundTmp2 = true; | |||
break; | |||
case "d": | |||
foundD = true; | |||
break; | |||
case "d/e": | |||
foundDE = true; | |||
break; | |||
case "d/e/f": | |||
foundDEF = true; | |||
break; | |||
} | |||
line = r.readLine(); | |||
} | |||
assertTrue(foundTmp); | |||
assertTrue(foundA); | |||
assertTrue(foundAB); | |||
assertTrue(foundABC); | |||
assertTrue(foundTmp2); | |||
assertTrue(foundD); | |||
assertTrue(foundDE); | |||
assertTrue(foundDEF); | |||
} | |||
} | |||
@Test | |||