Merge "Implement support for filtering class paths."
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index cbd8dd6..1e5fea9 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import java.io.IOException;
import java.nio.file.Path;
+import java.util.Arrays;
import java.util.Collection;
/**
@@ -86,13 +89,13 @@
/** Add program file resources. */
public B addProgramFiles(Path... files) throws IOException {
- app.addProgramFiles(files);
+ addProgramFiles(Arrays.asList(files));
return self();
}
/** Add program file resources. */
public B addProgramFiles(Collection<Path> files) throws IOException {
- app.addProgramFiles(files);
+ app.addProgramFiles(ListUtils.map(files, FilteredClassPath::unfiltered));
return self();
}
@@ -104,13 +107,13 @@
/** Add library file resources. */
public B addLibraryFiles(Path... files) throws IOException {
- app.addLibraryFiles(files);
+ addLibraryFiles(Arrays.asList(files));
return self();
}
/** Add library file resources. */
public B addLibraryFiles(Collection<Path> files) throws IOException {
- app.addLibraryFiles(files);
+ app.addLibraryFiles(ListUtils.map(files, FilteredClassPath::unfiltered));
return self();
}
diff --git a/src/main/java/com/android/tools/r8/PrintClassList.java b/src/main/java/com/android/tools/r8/PrintClassList.java
index bc274a8..896a66a 100644
--- a/src/main/java/com/android/tools/r8/PrintClassList.java
+++ b/src/main/java/com/android/tools/r8/PrintClassList.java
@@ -14,12 +14,12 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidApp.Builder;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Timing;
-
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Arrays;
@@ -37,7 +37,7 @@
builder.setProguardMapFile(Paths.get(args[0]));
dexFiles = dexFiles.subList(1, dexFiles.size());
}
- builder.addProgramFiles(ListUtils.map(dexFiles, Paths::get));
+ builder.addProgramFiles(ListUtils.map(dexFiles, FilteredClassPath::unfiltered));
ExecutorService executorService = Executors.newCachedThreadPool();
DexApplication application =
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 890d570..ab9155b 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -178,7 +178,8 @@
protected void validate() throws CompilationException {
super.validate();
- if (mainDexListOutput != null && mainDexRules.isEmpty() && !getAppBuilder().hasMainDexList()) {
+ if (mainDexListOutput != null && mainDexRules.isEmpty() && !getAppBuilder()
+ .hasMainDexList()) {
throw new CompilationException(
"Option --main-dex-list-output require --main-dex-rules and/or --main-dex-list");
}
@@ -224,8 +225,8 @@
proguardConfigurationConsumer.accept(configurationBuilder);
}
configuration = configurationBuilder.build();
- addProgramFiles(configuration.getInjars());
- addLibraryFiles(configuration.getLibraryjars());
+ getAppBuilder().addProgramFiles(configuration.getInjars());
+ getAppBuilder().addLibraryFiles(configuration.getLibraryjars());
}
// TODO(b/64802420): setProguardMapFile if configuration.hasApplyMappingFile
diff --git a/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java b/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
new file mode 100644
index 0000000..755ac6c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/FilteredClassPath.java
@@ -0,0 +1,140 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Implements class path filtering as per
+ * <a href="https://www.guardsquare.com/en/proguard/manual/usage#classpath">ProGuards classpath</a>
+ * documentation.
+ * <p>
+ * Some of the implementation details are derived from the examples. For example the implict
+ * catch-all positive filter after a trailing negative filter.
+ */
+public class FilteredClassPath {
+
+ private final Path path;
+ private final ImmutableList<String> pattern;
+
+ FilteredClassPath(Path path, ImmutableList<String> pattern) {
+ this.path = path;
+ this.pattern = pattern;
+ }
+
+ private FilteredClassPath(Path path) {
+ this(path, ImmutableList.of());
+ }
+
+ public static FilteredClassPath unfiltered(File file) {
+ return new FilteredClassPath(file.toPath());
+ }
+
+ public static FilteredClassPath unfiltered(Path path) {
+ return new FilteredClassPath(path);
+ }
+
+ public static FilteredClassPath unfiltered(String path) {
+ return new FilteredClassPath(Paths.get(path));
+ }
+
+ public Path getPath() {
+ return path;
+ }
+
+ public boolean matchesFile(Path file) {
+ if (isUnfiltered()) {
+ return true;
+ }
+ boolean isNegated = false;
+ for (String pattern : pattern) {
+ isNegated = pattern.charAt(0) == '!';
+ boolean matches = matchAgainstFileName(file.toString(), 0, pattern, isNegated ? 1 : 0);
+ if (matches) {
+ return !isNegated;
+ }
+ }
+ // If the last filter was a negated one, we do catch all positive thereafter.
+ return isNegated;
+ }
+
+ private boolean containsFileSeparator(String string) {
+ return string.indexOf(File.separatorChar) != -1;
+ }
+
+ private boolean matchAgainstFileName(String fileName, int namePos, String pattern,
+ int patternPos) {
+ if (patternPos >= pattern.length()) {
+ // We have exhausted the pattern before the filename.
+ return namePos == fileName.length();
+ }
+ char currentPattern = pattern.charAt(patternPos);
+ if (currentPattern == '*') {
+ boolean includeFileSeparators =
+ pattern.length() > patternPos + 1 && pattern.charAt(patternPos + 1) == '*';
+ if (includeFileSeparators) {
+ patternPos++;
+ }
+ // Common case where the end is a file name suffix without further wildcards.
+ String remainingPattern = pattern.substring(patternPos + 1);
+ if (remainingPattern.indexOf('*') == -1) {
+ // The pattern contains no multi-char wildcards, so only the postfix has to match.
+ int remaining = remainingPattern.length();
+ if (namePos + remaining > fileName.length()) {
+ // Exhausted the name too early.
+ return false;
+ }
+ if (includeFileSeparators
+ || !containsFileSeparator(fileName.substring(namePos, fileName.length() - remaining))) {
+ return matchAgainstFileName(fileName, fileName.length() - remaining, pattern,
+ patternPos + 1);
+ }
+ } else {
+ for (int i = namePos; i < fileName.length(); i++) {
+ if (!includeFileSeparators && fileName.charAt(i) == File.separatorChar) {
+ return false;
+ }
+ if (matchAgainstFileName(fileName, i, pattern, patternPos + 1)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ if (namePos >= fileName.length()) {
+ return false;
+ }
+ if (currentPattern == '?' || currentPattern == fileName.charAt(namePos)) {
+ return matchAgainstFileName(fileName, namePos + 1, pattern, patternPos + 1);
+ }
+ }
+ return false;
+ }
+
+ public boolean isUnfiltered() {
+ return pattern.isEmpty();
+ }
+
+ @Override
+ public String toString() {
+ if (isUnfiltered()) {
+ return path.toString();
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append(path);
+ builder.append('(');
+ boolean first = true;
+ for (String pattern : pattern) {
+ if (!first) {
+ builder.append(',');
+ }
+ builder.append(pattern);
+ first = false;
+ }
+ builder.append(')');
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index f47adbd..05fb09c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -18,8 +18,8 @@
public static class Builder {
- private final List<Path> injars = new ArrayList<>();
- private final List<Path> libraryjars = new ArrayList<>();
+ private final List<FilteredClassPath> injars = new ArrayList<>();
+ private final List<FilteredClassPath> libraryjars = new ArrayList<>();
private PackageObfuscationMode packageObfuscationMode = PackageObfuscationMode.NONE;
private String packagePrefix = "";
private boolean allowAccessModification = false;
@@ -47,11 +47,11 @@
this.dexItemFactory = dexItemFactory;
}
- public void addInjars(List<Path> injars) {
+ public void addInjars(List<FilteredClassPath> injars) {
this.injars.addAll(injars);
}
- public void addLibraryJars(List<Path> libraryJars) {
+ public void addLibraryJars(List<FilteredClassPath> libraryJars) {
this.libraryjars.addAll(libraryJars);
}
@@ -174,8 +174,8 @@
}
private final DexItemFactory dexItemFactory;
- private final ImmutableList<Path> injars;
- private final ImmutableList<Path> libraryjars;
+ private final ImmutableList<FilteredClassPath> injars;
+ private final ImmutableList<FilteredClassPath> libraryjars;
private final PackageObfuscationMode packageObfuscationMode;
private final String packagePrefix;
private final boolean allowAccessModification;
@@ -199,8 +199,8 @@
private ProguardConfiguration(
DexItemFactory factory,
- List<Path> injars,
- List<Path> libraryjars,
+ List<FilteredClassPath> injars,
+ List<FilteredClassPath> libraryjars,
PackageObfuscationMode packageObfuscationMode,
String packagePrefix,
boolean allowAccessModification,
@@ -261,11 +261,11 @@
return false;
}
- public ImmutableList<Path> getInjars() {
+ public ImmutableList<FilteredClassPath> getInjars() {
return injars;
}
- public ImmutableList<Path> getLibraryjars() {
+ public ImmutableList<FilteredClassPath> getLibraryjars() {
return libraryjars;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 1c3051d..a61017c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -772,7 +772,9 @@
int end = position;
while (!eof(end)) {
char current = contents.charAt(end);
- if (current != File.pathSeparatorChar && !Character.isWhitespace(current)) {
+ if (current != File.pathSeparatorChar
+ && !Character.isWhitespace(current)
+ && current != '(') {
end++;
} else {
break;
@@ -785,18 +787,60 @@
return baseDirectory.resolve(contents.substring(start, end));
}
- private List<Path> parseClassPath() throws ProguardRuleParserException {
- List<Path> classPath = new ArrayList<>();
+ private List<FilteredClassPath> parseClassPath() throws ProguardRuleParserException {
+ List<FilteredClassPath> classPath = new ArrayList<>();
skipWhitespace();
Path file = parseFileName();
- classPath.add(file);
+ ImmutableList<String> filters = parseClassPathFilters();
+ classPath.add(new FilteredClassPath(file, filters));
while (acceptChar(File.pathSeparatorChar)) {
file = parseFileName();
- classPath.add(file);
+ filters = parseClassPathFilters();
+ classPath.add(new FilteredClassPath(file, filters));
}
return classPath;
}
+ private ImmutableList<String> parseClassPathFilters() throws ProguardRuleParserException {
+ skipWhitespace();
+ if (acceptChar('(')) {
+ ImmutableList.Builder<String> filters = new ImmutableList.Builder<>();
+ filters.add(parseFileFilter());
+ skipWhitespace();
+ while (acceptChar(',')) {
+ filters.add(parseFileFilter());
+ skipWhitespace();
+ }
+ if (acceptChar(';')) {
+ throw parseError("Only class file filters are supported in classpath");
+ }
+ expectChar(')');
+ return filters.build();
+ } else {
+ return ImmutableList.of();
+ }
+ }
+
+ private String parseFileFilter() throws ProguardRuleParserException {
+ skipWhitespace();
+ int start = position;
+ int end = position;
+ while (!eof(end)) {
+ char current = contents.charAt(end);
+ if (current != ',' && current != ';' && current != ')' && !Character
+ .isWhitespace(current)) {
+ end++;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ throw parseError("file filter expected");
+ }
+ position = end;
+ return contents.substring(start, end);
+ }
+
private ProguardAssumeNoSideEffectRule parseAssumeNoSideEffectsRule()
throws ProguardRuleParserException {
ProguardAssumeNoSideEffectRule.Builder builder = ProguardAssumeNoSideEffectRule.builder();
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index ec53553..4a190ac 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.dex.VDexFile;
import com.android.tools.r8.dex.VDexFileReader;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
@@ -52,6 +53,7 @@
private final ImmutableList<Resource> programResources;
private final ImmutableList<ClassFileResourceProvider> classpathResourceProviders;
private final ImmutableList<ClassFileResourceProvider> libraryResourceProviders;
+
private final ImmutableList<ProgramFileArchiveReader> programFileArchiveReaders;
private final Resource deadCode;
private final Resource proguardMap;
@@ -112,7 +114,7 @@
* Create an app from program files @code{files}. See also Builder::addProgramFiles.
*/
public static AndroidApp fromProgramFiles(List<Path> files) throws IOException {
- return builder().addProgramFiles(files).build();
+ return builder().addProgramFiles(ListUtils.map(files, FilteredClassPath::unfiltered)).build();
}
/**
@@ -189,6 +191,10 @@
return libraryResourceProviders;
}
+ public List<ProgramFileArchiveReader> getProgramFileArchiveReaders() {
+ return programFileArchiveReaders;
+ }
+
private List<Resource> filter(List<Resource> resources, Resource.Kind kind) {
List<Resource> out = new ArrayList<>(resources.size());
for (Resource resource : resources) {
@@ -469,9 +475,9 @@
* @param directory Directory containing dex program files and optional proguard-map file.
*/
public Builder addProgramDirectory(Path directory) throws IOException {
- List<Path> resources =
+ List<FilteredClassPath> resources =
Arrays.asList(directory.toFile().listFiles(file -> isDexFile(file.toPath()))).stream()
- .map(file -> file.toPath()).collect(Collectors.toList());
+ .map(file -> FilteredClassPath.unfiltered(file)).collect(Collectors.toList());
addProgramFiles(resources);
File mapFile = new File(directory.toFile(), DEFAULT_PROGUARD_MAP_FILE);
if (mapFile.exists()) {
@@ -483,15 +489,15 @@
/**
* Add program file resources.
*/
- public Builder addProgramFiles(Path... files) throws IOException {
+ public Builder addProgramFiles(FilteredClassPath... files) throws IOException {
return addProgramFiles(Arrays.asList(files));
}
/**
* Add program file resources.
*/
- public Builder addProgramFiles(Collection<Path> files) throws IOException {
- for (Path file : files) {
+ public Builder addProgramFiles(Collection<FilteredClassPath> files) throws IOException {
+ for (FilteredClassPath file : files) {
addProgramFile(file);
}
return this;
@@ -509,7 +515,7 @@
*/
public Builder addClasspathFiles(Collection<Path> files) throws IOException {
for (Path file : files) {
- addClassProvider(file, classpathResourceProviders);
+ addClassProvider(FilteredClassPath.unfiltered(file), classpathResourceProviders);
}
return this;
}
@@ -525,15 +531,15 @@
/**
* Add library file resources.
*/
- public Builder addLibraryFiles(Path... files) throws IOException {
+ public Builder addLibraryFiles(FilteredClassPath... files) throws IOException {
return addLibraryFiles(Arrays.asList(files));
}
/**
* Add library file resources.
*/
- public Builder addLibraryFiles(Collection<Path> files) throws IOException {
- for (Path file : files) {
+ public Builder addLibraryFiles(Collection<FilteredClassPath> files) throws IOException {
+ for (FilteredClassPath file : files) {
addClassProvider(file, libraryResourceProviders);
}
return this;
@@ -722,7 +728,8 @@
mainDexListOutput);
}
- private void addProgramFile(Path file) throws IOException {
+ private void addProgramFile(FilteredClassPath filteredClassPath) throws IOException {
+ Path file = filteredClassPath.getPath();
if (!Files.exists(file)) {
throw new FileNotFoundException("Non-existent input file: " + file);
}
@@ -733,20 +740,25 @@
} else if (isClassFile(file)) {
programResources.add(Resource.fromFile(Resource.Kind.CLASSFILE, file));
} else if (isArchive(file)) {
- programFileArchiveReaders.add(new ProgramFileArchiveReader(file, ignoreDexInArchive));
+ programFileArchiveReaders
+ .add(new ProgramFileArchiveReader(filteredClassPath, ignoreDexInArchive));
} else {
throw new CompilationError("Unsupported source file type for file: " + file);
}
}
- private void addClassProvider(Path file, List<ClassFileResourceProvider> providerList)
+ private void addClassProvider(FilteredClassPath classPath,
+ List<ClassFileResourceProvider> providerList)
throws IOException {
+ Path file = classPath.getPath();
if (!Files.exists(file)) {
throw new FileNotFoundException("Non-existent input file: " + file);
}
if (isArchive(file)) {
- providerList.add(PreloadedClassFileProvider.fromArchive(file));
+ providerList.add(PreloadedClassFileProvider.fromArchive(classPath));
} else if (Files.isDirectory(file) ) {
+ // This is only used for D8 incremental compilation.
+ assert classPath.isUnfiltered();
providerList.add(DirectoryClassFileProvider.fromDirectory(file));
} else {
throw new CompilationError("Unsupported source file type for file: " + file);
diff --git a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
index 5ea2a30..8e3c638 100644
--- a/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/PreloadedClassFileProvider.java
@@ -10,11 +10,10 @@
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.Resource;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
-
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
@@ -27,8 +26,11 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-/** Lazy Java class file resource provider based on preloaded/prebuilt context. */
+/**
+ * Lazy Java class file resource provider based on preloaded/prebuilt context.
+ */
public final class PreloadedClassFileProvider implements ClassFileResourceProvider {
+
private final Map<String, byte[]> content;
private PreloadedClassFileProvider(Map<String, byte[]> content) {
@@ -49,16 +51,20 @@
return Resource.fromBytes(Resource.Kind.CLASSFILE, bytes, Collections.singleton(descriptor));
}
- /** Create preloaded content resource provider from archive file. */
- public static ClassFileResourceProvider fromArchive(Path archive) throws IOException {
- assert isArchive(archive);
+ /**
+ * Create preloaded content resource provider from archive file.
+ */
+ public static ClassFileResourceProvider fromArchive(FilteredClassPath archive)
+ throws IOException {
+ assert isArchive(archive.getPath());
Builder builder = builder();
- try (ZipFile zipFile = new ZipFile(archive.toFile())) {
+ try (ZipFile zipFile = new ZipFile(archive.getPath().toFile())) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
- if (isClassFile(Paths.get(name))) {
+ Path entryPath = Paths.get(name);
+ if (isClassFile(entryPath) && archive.matchesFile(entryPath)) {
try (InputStream entryStream = zipFile.getInputStream(entry)) {
builder.addResource(guessTypeDescriptor(name), ByteStreams.toByteArray(entryStream));
}
@@ -80,8 +86,8 @@
assert name.endsWith(CLASS_EXTENSION) :
"Name " + name + " must have " + CLASS_EXTENSION + " suffix";
String fileName =
- File.separatorChar == '/' ? name.toString() :
- name.toString().replace(File.separatorChar, '/');
+ File.separatorChar == '/' ? name.toString() :
+ name.toString().replace(File.separatorChar, '/');
String descriptor = fileName.substring(0, fileName.length() - CLASS_EXTENSION.length());
if (descriptor.contains(".")) {
throw new CompilationError("Unexpected file name in the archive: " + fileName);
@@ -94,12 +100,15 @@
return content.size() + " preloaded resources";
}
- /** Create a new empty builder. */
+ /**
+ * Create a new empty builder.
+ */
public static Builder builder() {
return new Builder();
}
public static final class Builder {
+
private Map<String, byte[]> content = new HashMap<>();
private Builder() {
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
index 79ab853..57bcd68 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramFileArchiveReader.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.Resource;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
@@ -23,39 +24,42 @@
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
-class ProgramFileArchiveReader {
+public class ProgramFileArchiveReader {
- private final Path archive;
+ private final FilteredClassPath archive;
private boolean ignoreDexInArchive;
private List<Resource> dexResources = null;
private List<Resource> classResources = null;
- ProgramFileArchiveReader(Path archive, boolean ignoreDexInArchive) {
+ ProgramFileArchiveReader(FilteredClassPath archive, boolean ignoreDexInArchive) {
this.archive = archive;
this.ignoreDexInArchive = ignoreDexInArchive;
}
private void readArchive() throws IOException {
- assert isArchive(archive);
+ assert isArchive(archive.getPath());
dexResources = new ArrayList<>();
classResources = new ArrayList<>();
- try (ZipFile zipFile = new ZipFile(archive.toFile())) {
+ try (ZipFile zipFile = new ZipFile(archive.getPath().toFile())) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
try (InputStream stream = zipFile.getInputStream(entry)) {
Path name = Paths.get(entry.getName());
- if (isDexFile(name)) {
- if (!ignoreDexInArchive) {
- Resource resource =
- new OneShotByteResource(Resource.Kind.DEX, ByteStreams.toByteArray(stream), null);
- dexResources.add(resource);
+ if (archive.matchesFile(name)) {
+ if (isDexFile(name)) {
+ if (!ignoreDexInArchive) {
+ Resource resource =
+ new OneShotByteResource(Resource.Kind.DEX, ByteStreams.toByteArray(stream),
+ null);
+ dexResources.add(resource);
+ }
+ } else if (isClassFile(name)) {
+ String descriptor = PreloadedClassFileProvider.guessTypeDescriptor(name);
+ Resource resource = new OneShotByteResource(Resource.Kind.CLASSFILE,
+ ByteStreams.toByteArray(stream), Collections.singleton(descriptor));
+ classResources.add(resource);
}
- } else if (isClassFile(name)) {
- String descriptor = PreloadedClassFileProvider.guessTypeDescriptor(name);
- Resource resource = new OneShotByteResource(Resource.Kind.CLASSFILE,
- ByteStreams.toByteArray(stream), Collections.singleton(descriptor));
- classResources.add(resource);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingBiConsumer.java b/src/main/java/com/android/tools/r8/utils/ThrowingBiConsumer.java
index d7da332..e922562 100644
--- a/src/main/java/com/android/tools/r8/utils/ThrowingBiConsumer.java
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingBiConsumer.java
@@ -1,3 +1,6 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
import java.util.function.BiConsumer;
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingBiFunction.java b/src/main/java/com/android/tools/r8/utils/ThrowingBiFunction.java
new file mode 100644
index 0000000..01d00d2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingBiFunction.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.util.function.BiFunction;
+
+/**
+ * Similar to a {@link BiFunction} but throws a single {@link Throwable}.
+ *
+ * @param <T> the type of the first argument
+ * @param <U> the type of the second argument
+ * @param <R> the return type
+ * @param <E> the type of the {@link Throwable}
+ */
+@FunctionalInterface
+public interface ThrowingBiFunction<T, U, R, E extends Throwable> {
+
+ R apply(T t, U u) throws E;
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
index 8fb8c84..61557e5 100644
--- a/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
+++ b/src/main/java/com/android/tools/r8/utils/ThrowingConsumer.java
@@ -1,3 +1,6 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
import java.util.function.Consumer;
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 2159d6b..6461a57 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.D8Command.Builder;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unimplemented;
@@ -195,8 +196,7 @@
abstract void addClasspathReference(
Path testJarFile, D8Command.Builder builder) throws IOException;
- abstract void addLibraryReference(
- D8Command.Builder builder, Path location) throws IOException;
+ abstract void addLibraryReference(Builder builder, Path location) throws IOException;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index c2ac969..c39dc31 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -8,6 +8,8 @@
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.D8Command.Builder;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DirectoryClassFileProvider;
import com.android.tools.r8.utils.FileUtils;
@@ -48,9 +50,9 @@
}
@Override
- void addLibraryReference(D8Command.Builder builder, Path location) throws IOException {
+ void addLibraryReference(Builder builder, Path location) throws IOException {
builder.addLibraryResourceProvider(
- PreloadedClassFileProvider.fromArchive(location));
+ PreloadedClassFileProvider.fromArchive(FilteredClassPath.unfiltered(location)));
}
@Override
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 925ba5b..f2c4d15 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
@@ -48,7 +49,8 @@
protected static AndroidApp readClasses(Class... classes) throws IOException {
AndroidApp.Builder builder = AndroidApp.builder();
for (Class clazz : classes) {
- builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
+ builder.addProgramFiles(
+ FilteredClassPath.unfiltered(ToolHelper.getClassFileForTestClass(clazz)));
}
return builder.build();
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index e14de46..e8fcc4c 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
import com.android.tools.r8.shaking.ProguardRuleParserException;
@@ -315,10 +316,13 @@
}
static class RetainedTemporaryFolder extends TemporaryFolder {
+
RetainedTemporaryFolder(java.io.File parentFolder) {
super(parentFolder);
}
- protected void after() {} // instead of remove, do nothing
+
+ protected void after() {
+ } // instead of remove, do nothing
}
// For non-Linux platforms create the temporary directory in the repository root to simplify
@@ -435,9 +439,9 @@
public static DexApplication buildApplication(List<String> fileNames)
throws IOException, ExecutionException {
return new ApplicationReader(
- AndroidApp.fromProgramFiles(ListUtils.map(fileNames, Paths::get)),
- new InternalOptions(),
- new Timing("ToolHelper buildApplication"))
+ AndroidApp.fromProgramFiles(ListUtils.map(fileNames, Paths::get)),
+ new InternalOptions(),
+ new Timing("ToolHelper buildApplication"))
.read();
}
@@ -485,14 +489,15 @@
}
public static CompilationResult runR8WithFullResult(
- R8Command command, Consumer<InternalOptions> optionsConsumer)
- throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
- // TODO(zerny): Should we really be adding the android library in ToolHelper?
+ R8Command command, Consumer<InternalOptions> optionsConsumer)
+ throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+ // TODO(zerny): Should we really be adding the android library in ToolHelper?
AndroidApp app = command.getInputApp();
if (app.getLibraryResourceProviders().isEmpty()) {
app =
AndroidApp.builder(app)
- .addLibraryFiles(Paths.get(getAndroidJar(command.getMinApiLevel())))
+ .addLibraryFiles(
+ FilteredClassPath.unfiltered(getAndroidJar(command.getMinApiLevel())))
.build();
}
InternalOptions options = command.getInternalOptions();
diff --git a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
index 4e8881a..104ba43 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
@@ -27,8 +28,10 @@
public void checkSwitchMapsRemoved()
throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
AndroidApp.Builder builder = AndroidApp.builder();
- builder.addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()));
- builder.addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR).resolve(JAR_FILE));
+ builder.addLibraryFiles(
+ FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()));
+ builder.addProgramFiles(
+ FilteredClassPath.unfiltered(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR).resolve(JAR_FILE)));
AndroidApp result = compileWithR8(builder.build(), writeTextToTempFile(PG_CONFIG));
DexInspector inspector = new DexInspector(result);
Assert.assertFalse(inspector.clazz(SWITCHMAP_CLASS_NAME).isPresent());
diff --git a/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java b/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
new file mode 100644
index 0000000..48ac44a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/FilteredClassPathTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.Resource;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidApp.Builder;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ProgramFileArchiveReader;
+import com.android.tools.r8.utils.ThrowingBiFunction;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FilteredClassPathTest {
+
+ private void testPath(List<String> filters, List<String> positives, List<String> negatives) {
+ FilteredClassPath path = makeFilteredClassPath(filters);
+ Assert.assertTrue(
+ positives.stream().map(FilteredClassPathTest::adaptFileSeparator).map(Paths::get)
+ .allMatch(path::matchesFile));
+ Assert.assertFalse(
+ negatives.stream().map(FilteredClassPathTest::adaptFileSeparator).map(Paths::get)
+ .allMatch(path::matchesFile));
+ }
+
+ private static String adaptFileSeparator(String s) {
+ return s.replace('/', File.separatorChar);
+ }
+
+ private static FilteredClassPath makeFilteredClassPath(List<String> filters) {
+ // TODO(herhut): Move to stream API once updated to guava 23.
+ return new FilteredClassPath(Paths.get("foo"),
+ ImmutableList.copyOf(ListUtils.map(filters, FilteredClassPathTest::adaptFileSeparator)));
+ }
+
+ @Test
+ public void testFilterMatching() {
+ testPath(ImmutableList.of("!boo"),
+ ImmutableList.of("bool", "bo"),
+ ImmutableList.of("boo"));
+ testPath(ImmutableList.of("!*.boo"),
+ ImmutableList.of("zoo.bar", "Fish.bool", "Cat.bo", "boo", "path/zoo.boo",
+ "zoo.bool", "zoo.bo"),
+ ImmutableList.of("mi.boo", ".boo"));
+ testPath(ImmutableList.of("!*s/*.boo"),
+ ImmutableList.of("zoo.bar", "Fish.bool", "Cat.bo", "boo", "path/zoo.boo", "sx/a.boo",
+ "ass/boo", "s/x/.boo", "s/s/s.boo", "s/zoo.bool"),
+ ImmutableList.of("mi.boo", ".boo", "ass/a.boo", "s/.boo", "asis/sos.boo"));
+ testPath(ImmutableList.of("!**s/*.boo"),
+ ImmutableList.of("zoo.bar", "Fish.bool", "Cat.bo", "boo", "path/zoo.boo", "sx/a.boo",
+ "ass/boo", "s/x/.boo", "s/zoo.bool"),
+ ImmutableList.of("mi.boo", ".boo", "ass/a.boo", "s/.boo", "asis/sos.boo", "s/s/s.boo",
+ "xyz/abz/ass/.boo"));
+ testPath(ImmutableList.of("!*.boo", "*.bar", "*.baz"),
+ ImmutableList.of("sinz.bar", "song.baz", ".baz", ".bar"),
+ ImmutableList.of("sinz.boo", ".boo", ".bang", "goo.bang", "s/foo.baz"));
+ }
+
+ private void testApplicationFiltered(
+ ThrowingBiFunction<Builder, FilteredClassPath, Builder, IOException> setter,
+ Function<AndroidApp, List<String>> getter)
+ throws IOException {
+ Path androidJar = Paths.get(ToolHelper.getDefaultAndroidJar());
+ AndroidApp app = setter.apply(AndroidApp.builder(), new FilteredClassPath(androidJar,
+ ImmutableList.of("!java/lang/**.class", "java/util/**.class"))).build();
+ List<String> descriptors = getter.apply(app);
+ Assert.assertTrue(descriptors.stream().noneMatch(s -> s.startsWith("Ljava/lang")));
+ Assert.assertTrue(descriptors.stream().anyMatch(s -> s.startsWith("Ljava/util")));
+ Assert.assertTrue(descriptors.stream().noneMatch(s -> s.startsWith("Lcom/android")));
+ }
+
+ @Test
+ public void testLibraryFiltered() throws IOException {
+ testApplicationFiltered(Builder::addLibraryFiles, app -> {
+ ClassFileResourceProvider provider = Iterables
+ .getOnlyElement(app.getLibraryResourceProviders());
+ List<Resource> resources = ListUtils
+ .map(provider.getClassDescriptors(), provider::getResource);
+ return resources.stream().flatMap(r -> r.getClassDescriptors().stream())
+ .collect(Collectors.toList());
+ });
+ }
+
+ private static Stream<Resource> getClassProgramResources(ProgramFileArchiveReader reader) {
+ try {
+ return reader.getClassProgramResources().stream();
+ } catch (IOException e) {
+ return Stream.empty();
+ }
+ }
+
+ @Test
+ public void testProgramFiltered() throws IOException {
+ testApplicationFiltered(Builder::addProgramFiles,
+ app -> app.getProgramFileArchiveReaders().stream()
+ .flatMap(FilteredClassPathTest::getClassProgramResources)
+ .flatMap(r -> r.getClassDescriptors().stream()).collect(
+ Collectors.toList()));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 48f8835..c96d603 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -19,6 +19,7 @@
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Collections;
import java.util.List;
import org.junit.Test;
@@ -414,6 +415,18 @@
}
@Test
+ public void parseInvalidFilePattern() throws IOException {
+ try {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(new ProguardConfigurationSourceStrings(
+ Collections.singletonList("-injars abc.jar(*.zip;*.class)")));
+ } catch (ProguardRuleParserException e) {
+ return;
+ }
+ fail();
+ }
+
+ @Test
public void parseSeeds() throws IOException, ProguardRuleParserException {
ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
parser.parse(Paths.get(SEEDS));
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
index edcd454..470f16e 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
@@ -8,12 +8,12 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.ClassSubject;
import com.android.tools.r8.utils.InternalOptions;
import java.io.IOException;
-import java.nio.file.Paths;
import org.antlr.runtime.RecognitionException;
import org.junit.Test;
@@ -61,7 +61,7 @@
InternalOptions options = new InternalOptions();
AndroidApp app = AndroidApp.builder()
.addDexProgramData(builder.compile())
- .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .addLibraryFiles(FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()))
.build();
// Java standard library added - java.lang.String is present.
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index 56059f6..cecc10e 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.ir.code.ValueNumberGenerator;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DexInspector;
@@ -37,7 +38,6 @@
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -435,7 +435,7 @@
try {
AndroidApp input = AndroidApp.builder()
.addDexProgramData(builder.compile())
- .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+ .addLibraryFiles(FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()))
.build();
return buildApplication(input, options);
} catch (IOException | RecognitionException e) {
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
index 21d3b9e..77366fb 100644
--- a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.shaking.FilteredClassPath;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DexInspector;
import com.android.tools.r8.utils.DexInspector.MethodSubject;
@@ -502,7 +503,8 @@
// Add the Jasmin class and a class from Java source with the main method.
AndroidApp.Builder appBuilder = AndroidApp.builder();
appBuilder.addClassProgramData(builder.buildClasses());
- appBuilder.addProgramFiles(ToolHelper.getClassFileForTestClass(CheckSwitchInTestClass.class));
+ appBuilder.addProgramFiles(FilteredClassPath
+ .unfiltered(ToolHelper.getClassFileForTestClass(CheckSwitchInTestClass.class)));
AndroidApp app = compileWithR8(appBuilder.build());
DexInspector inspector = new DexInspector(app);
diff --git a/src/test/proguard/valid/library-jars-win.flags b/src/test/proguard/valid/library-jars-win.flags
index c6ffbb4..f580366 100644
--- a/src/test/proguard/valid/library-jars-win.flags
+++ b/src/test/proguard/valid/library-jars-win.flags
@@ -1,2 +1,2 @@
--libraryjars some/library.jar
--libraryjars some/library.jar;some/other/library.jar;yet/another/library.jar
\ No newline at end of file
+-libraryjars some\library.jar
+-libraryjars some\library.jar( **\*foo.class , **.bar );some\other\library.jar;yet\another\library.jar(**.class)
diff --git a/src/test/proguard/valid/library-jars.flags b/src/test/proguard/valid/library-jars.flags
index 9cb77d4..8a48828 100644
--- a/src/test/proguard/valid/library-jars.flags
+++ b/src/test/proguard/valid/library-jars.flags
@@ -1,2 +1,2 @@
-libraryjars some/library.jar
--libraryjars some/library.jar:some/other/library.jar:yet/another/library.jar
\ No newline at end of file
+-libraryjars some/library.jar( **/*foo.class , **.bar ):some/other/library.jar:yet/another/library.jar(**.class)