8144226: Sjavac's handling of include/exclude patterns is buggy, redundant and inconsistent

Rewrote sjavac include/exclude pattern handling.

Reviewed-by: jlahoda
This commit is contained in:
Andreas Lundblad 2016-01-08 17:14:10 +01:00
parent 180c59d147
commit b345518d32
17 changed files with 462 additions and 522 deletions

View file

@ -26,11 +26,20 @@
package com.sun.tools.sjavac; package com.sun.tools.sjavac;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Set; import java.util.Set;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.regex.PatternSyntaxException;
/** A Source object maintains information about a source file. /** A Source object maintains information about a source file.
* For example which package it belongs to and kind of source it is. * For example which package it belongs to and kind of source it is.
@ -56,8 +65,6 @@ public class Source implements Comparable<Source> {
private long lastModified; private long lastModified;
// The source File. // The source File.
private File file; private File file;
// The source root under which file resides.
private File root;
// If the source is generated. // If the source is generated.
private boolean isGenerated; private boolean isGenerated;
// If the source is only linked to, not compiled. // If the source is only linked to, not compiled.
@ -78,7 +85,7 @@ public class Source implements Comparable<Source> {
return name.hashCode(); return name.hashCode();
} }
public Source(Module m, String n, File f, File r) { public Source(Module m, String n, File f) {
name = n; name = n;
int dp = n.lastIndexOf("."); int dp = n.lastIndexOf(".");
if (dp != -1) { if (dp != -1) {
@ -87,7 +94,6 @@ public class Source implements Comparable<Source> {
suffix = ""; suffix = "";
} }
file = f; file = f;
root = r;
lastModified = f.lastModified(); lastModified = f.lastModified();
linkedOnly = false; linkedOnly = false;
} }
@ -102,7 +108,6 @@ public class Source implements Comparable<Source> {
suffix = ""; suffix = "";
} }
file = null; file = null;
root = null;
lastModified = lm; lastModified = lm;
linkedOnly = false; linkedOnly = false;
int ls = n.lastIndexOf('/'); int ls = n.lastIndexOf('/');
@ -112,7 +117,6 @@ public class Source implements Comparable<Source> {
public String suffix() { return suffix; } public String suffix() { return suffix; }
public Package pkg() { return pkg; } public Package pkg() { return pkg; }
public File file() { return file; } public File file() { return file; }
public File root() { return root; }
public long lastModified() { public long lastModified() {
return lastModified; return lastModified;
} }
@ -183,225 +187,122 @@ public class Source implements Comparable<Source> {
*/ */
static public void scanRoot(File root, static public void scanRoot(File root,
Set<String> suffixes, Set<String> suffixes,
List<String> excludes, List<String> includes, List<String> excludes,
List<String> excludeFiles, List<String> includeFiles, List<String> includes,
Map<String,Source> foundFiles, Map<String,Source> foundFiles,
Map<String,Module> foundModules, Map<String,Module> foundModules,
Module currentModule, final Module currentModule,
boolean permitSourcesWithoutPackage, boolean permitSourcesWithoutPackage,
boolean inGensrc, boolean inGensrc,
boolean inLinksrc) boolean inLinksrc)
throws ProblemException { throws IOException, ProblemException {
if (root == null) return; if (root == null)
int root_prefix = root.getPath().length()+1; return;
// This is the root source directory, it must not contain any Java sources files
// because we do not allow Java source files without a package.
// (Unless of course --permit-sources-without-package has been specified.)
// It might contain other source files however, (for -tr and -copy) these will
// always be included, since no package pattern can match the root directory.
currentModule = addFilesInDir(root, root_prefix, root, suffixes, permitSourcesWithoutPackage,
excludeFiles, includeFiles,
foundFiles, foundModules, currentModule,
inGensrc, inLinksrc);
File[] dirfiles = root.listFiles(); FileSystem fs = root.toPath().getFileSystem();
for (File d : dirfiles) {
if (d.isDirectory()) { if (includes.isEmpty()) {
// Descend into the directory structure. includes = Collections.singletonList("**");
scanDirectory(d, root_prefix, root, suffixes,
excludes, includes, excludeFiles, includeFiles,
foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
}
} }
}
/** List<PathMatcher> includeMatchers = createPathMatchers(fs, includes);
* Test if a path matches any of the patterns given. List<PathMatcher> excludeMatchers = createPathMatchers(fs, excludes);
* The pattern foo/bar matches only foo/bar
* The pattern foo/* matches foo/bar and foo/bar/zoo etc
*/
static private boolean hasMatch(String path, List<String> patterns) {
// Convert Windows '\' to '/' for the sake of comparing with the patterns Files.walkFileTree(root.toPath(), new SimpleFileVisitor<Path>() {
path = path.replace(File.separatorChar, '/'); @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
for (String p : patterns) { Path relToRoot = root.toPath().relativize(file);
// Exact match
if (p.equals(path))
return true;
// Single dot the end matches this package and all its subpackages. if (includeMatchers.stream().anyMatch(im -> im.matches(relToRoot))
if (p.endsWith("/*")) { && excludeMatchers.stream().noneMatch(em -> em.matches(relToRoot))
// Remove the wildcard && suffixes.contains(Util.fileSuffix(file))) {
String patprefix = p.substring(0,p.length()-2);
// Does the path start with the pattern prefix? // TODO: Test this.
if (path.startsWith(patprefix)) { Source existing = foundFiles.get(file);
// If the path has the same length as the pattern prefix, then it is a match. if (existing != null) {
// If the path is longer, then make sure that throw new IOException("You have already added the file "+file+" from "+existing.file().getPath());
// the next part of the path starts with a dot (.) to prevent
// wildcard matching in the middle of a package name.
if (path.length()==patprefix.length() || path.charAt(patprefix.length())=='/') {
return true;
} }
} existing = currentModule.lookupSource(file.toString());
} if (existing != null) {
}
return false;
}
/** // Oups, the source is already added, could be ok, could be not, lets check.
* Matches patterns with the asterisk first. */ if (inLinksrc) {
// The pattern foo/bar.java only matches foo/bar.java // So we are collecting sources for linking only.
// The pattern */bar.java matches foo/bar.java and zoo/bar.java etc if (existing.isLinkedOnly()) {
static private boolean hasFileMatch(String path, List<String> patterns) { // Ouch, this one is also for linking only. Bad.
// Convert Windows '\' to '/' for the sake of comparing with the patterns throw new IOException("You have already added the link only file " + file + " from " + existing.file().getPath());
path = path.replace(File.separatorChar, '/'); }
// Ok, the existing source is to be compiled. Thus this link only is redundant
// since all compiled are also linked to. Continue to the next source.
// But we need to add the source, so that it will be visible to linking,
// if not the multi core compile will fail because a JavaCompiler cannot
// find the necessary dependencies for its part of the source.
foundFiles.put(file.toString(), existing);
} else {
// We are looking for sources to compile, if we find an existing to be compiled
// source with the same name, it is an internal error, since we must
// find the sources to be compiled before we find the sources to be linked to.
throw new IOException("Internal error: Double add of file " + file + " from " + existing.file().getPath());
}
path = Util.normalizeDriveLetter(path);
for (String p : patterns) {
// Exact match
if (p.equals(path)) {
return true;
}
// Single dot the end matches this package and all its subpackages.
if (p.startsWith("*")) {
// Remove the wildcard
String patsuffix = p.substring(1);
// Does the path start with the pattern prefix?
if (path.endsWith(patsuffix)) {
return true;
}
}
}
return false;
}
/**
* Add the files in the directory, assuming that the file has not been excluded.
* Returns a fresh Module object, if this was a dir with a module-info.java file.
*/
static private Module addFilesInDir(File dir, int rootPrefix, File root,
Set<String> suffixes, boolean allow_javas,
List<String> excludeFiles, List<String> includeFiles,
Map<String,Source> foundFiles,
Map<String,Module> foundModules,
Module currentModule,
boolean inGensrc,
boolean inLinksrc)
throws ProblemException
{
for (File f : dir.listFiles()) {
if (!f.isFile())
continue;
boolean should_add =
(excludeFiles == null || excludeFiles.isEmpty() || !hasFileMatch(f.getPath(), excludeFiles))
&& (includeFiles == null || includeFiles.isEmpty() || hasFileMatch(f.getPath(), includeFiles));
if (!should_add)
continue;
if (!allow_javas && f.getName().endsWith(".java")) {
throw new ProblemException("No .java files are allowed in the source root "+dir.getPath()+
", please remove "+f.getName());
}
// Extract the file name relative the root.
String fn = f.getPath().substring(rootPrefix);
// Extract the package name.
int sp = fn.lastIndexOf(File.separatorChar);
String pkg = "";
if (sp != -1) {
pkg = fn.substring(0,sp).replace(File.separatorChar,'.');
}
// Is this a module-info.java file?
if (fn.endsWith("module-info.java")) {
// Aha! We have recursed into a module!
if (!currentModule.name().equals("")) {
throw new ProblemException("You have an extra module-info.java inside a module! Please remove "+fn);
}
String module_name = fn.substring(0,fn.length()-16);
currentModule = new Module(module_name, f.getPath());
foundModules.put(module_name, currentModule);
}
// Extract the suffix.
int dp = fn.lastIndexOf(".");
String suffix = "";
if (dp > 0) {
suffix = fn.substring(dp);
}
// Should the file be added?
if (suffixes.contains(suffix)) {
Source of = foundFiles.get(f.getPath());
if (of != null) {
throw new ProblemException("You have already added the file "+fn+" from "+of.file().getPath());
}
of = currentModule.lookupSource(f.getPath());
if (of != null) {
// Oups, the source is already added, could be ok, could be not, lets check.
if (inLinksrc) {
// So we are collecting sources for linking only.
if (of.isLinkedOnly()) {
// Ouch, this one is also for linking only. Bad.
throw new ProblemException("You have already added the link only file "+fn+" from "+of.file().getPath());
}
// Ok, the existing source is to be compiled. Thus this link only is redundant
// since all compiled are also linked to. Continue to the next source.
// But we need to add the source, so that it will be visible to linking,
// if not the multi core compile will fail because a JavaCompiler cannot
// find the necessary dependencies for its part of the source.
foundFiles.put(f.getPath(), of);
continue;
} else { } else {
// We are looking for sources to compile, if we find an existing to be compiled
// source with the same name, it is an internal error, since we must //////////////////////////////////////////////////////////////
// find the sources to be compiled before we find the sources to be linked to. // Add source
throw new ProblemException("Internal error: Double add of file "+fn+" from "+of.file().getPath()); Source s = new Source(currentModule, file.toString(), file.toFile());
if (inGensrc) {
s.markAsGenerated();
}
if (inLinksrc) {
s.markAsLinkedOnly();
}
String pkg = packageOfJavaFile(root.toPath(), file);
pkg = currentModule.name() + ":" + pkg;
foundFiles.put(file.toString(), s);
currentModule.addSource(pkg, s);
//////////////////////////////////////////////////////////////
} }
} }
Source s = new Source(currentModule, f.getPath(), f, root);
if (inGensrc) s.markAsGenerated(); return FileVisitResult.CONTINUE;
if (inLinksrc) {
s.markAsLinkedOnly();
}
pkg = currentModule.name()+":"+pkg;
foundFiles.put(f.getPath(), s);
currentModule.addSource(pkg, s);
} }
} });
return currentModule;
} }
static private void scanDirectory(File dir, int rootPrefix, File root, private static List<PathMatcher> createPathMatchers(FileSystem fs, List<String> patterns) {
Set<String> suffixes, List<PathMatcher> matchers = new ArrayList<>();
List<String> excludes, List<String> includes, for (String pattern : patterns) {
List<String> excludeFiles, List<String> includeFiles, try {
Map<String,Source> foundFiles, matchers.add(fs.getPathMatcher("glob:" + pattern));
Map<String,Module> foundModules, } catch (PatternSyntaxException e) {
Module currentModule, boolean inGensrc, boolean inLinksrc) Log.error("Invalid pattern: " + pattern);
throws ProblemException { throw e;
String path = "";
// Remove the root prefix from the dir path
if (dir.getPath().length() > rootPrefix) {
path = dir.getPath().substring(rootPrefix);
}
// Should this package directory be included and not excluded?
if ((includes==null || includes.isEmpty() || hasMatch(path, includes)) &&
(excludes==null || excludes.isEmpty() || !hasMatch(path, excludes))) {
// Add the source files.
currentModule = addFilesInDir(dir, rootPrefix, root, suffixes, true, excludeFiles, includeFiles,
foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
}
for (File d : dir.listFiles()) {
if (d.isDirectory()) {
// Descend into the directory structure.
scanDirectory(d, rootPrefix, root, suffixes,
excludes, includes, excludeFiles, includeFiles,
foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
} }
} }
return matchers;
}
private static String packageOfJavaFile(Path sourceRoot, Path javaFile) {
Path javaFileDir = javaFile.getParent();
Path packageDir = sourceRoot.relativize(javaFileDir);
List<String> separateDirs = new ArrayList<>();
for (Path pathElement : packageDir) {
separateDirs.add(pathElement.getFileName().toString());
}
return String.join(".", separateDirs);
}
@Override
public String toString() {
return String.format("%s[pkg: %s, name: %s, suffix: %s, file: %s, isGenerated: %b, linkedOnly: %b]",
getClass().getSimpleName(),
pkg,
name,
suffix,
file,
isGenerated,
linkedOnly);
} }
} }

View file

@ -230,4 +230,10 @@ public class Util {
Function<? super T, ? extends I> indexFunction) { Function<? super T, ? extends I> indexFunction) {
return c.stream().collect(Collectors.<T, I, T>toMap(indexFunction, o -> o)); return c.stream().collect(Collectors.<T, I, T>toMap(indexFunction, o -> o));
} }
public static String fileSuffix(Path file) {
String fileNameStr = file.getFileName().toString();
int dotIndex = fileNameStr.indexOf('.');
return dotIndex == -1 ? "" : fileNameStr.substring(dotIndex);
}
} }

View file

@ -144,77 +144,77 @@ public class SjavacImpl implements Sjavac {
Module current_module = new Module("", ""); Module current_module = new Module("", "");
modules.put("", current_module); modules.put("", current_module);
// Find all sources, use the suffix rules to know which files are sources.
Map<String,Source> sources = new HashMap<>();
// Find the files, this will automatically populate the found modules
// with found packages where the sources are found!
findSourceFiles(options.getSources(),
suffixRules.keySet(),
sources,
modules,
current_module,
options.isDefaultPackagePermitted(),
false);
if (sources.isEmpty()) {
Log.error("Found nothing to compile!");
return RC_FATAL;
}
// Create a map of all source files that are available for linking. Both -src and
// -sourcepath point to such files. It is possible to specify multiple
// -sourcepath options to enable different filtering rules. If the
// filters are the same for multiple sourcepaths, they may be concatenated
// using :(;). Before sending the list of sourcepaths to javac, they are
// all concatenated. The list created here is used by the SmartFileWrapper to
// make sure only the correct sources are actually available.
// We might find more modules here as well.
Map<String,Source> sources_to_link_to = new HashMap<>();
List<SourceLocation> sourceResolutionLocations = new ArrayList<>();
sourceResolutionLocations.addAll(options.getSources());
sourceResolutionLocations.addAll(options.getSourceSearchPaths());
findSourceFiles(sourceResolutionLocations,
Collections.singleton(".java"),
sources_to_link_to,
modules,
current_module,
options.isDefaultPackagePermitted(),
true);
// Add the set of sources to the build database.
javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
javac_state.now().checkInternalState("checking sources", false, sources);
javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
javac_state.setVisibleSources(sources_to_link_to);
int round = 0;
printRound(round);
// If there is any change in the source files, taint packages
// and mark the database in need of saving.
javac_state.checkSourceStatus(false);
// Find all existing artifacts. Their timestamp will match the last modified timestamps stored
// in javac_state, simply because loading of the JavacState will clean out all artifacts
// that do not match the javac_state database.
javac_state.findAllArtifacts();
// Remove unidentified artifacts from the bin, gensrc and header dirs.
// (Unless we allow them to be there.)
// I.e. artifacts that are not known according to the build database (javac_state).
// For examples, files that have been manually copied into these dirs.
// Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
// in javac_state) have already been removed when the javac_state was loaded.
if (!options.areUnidentifiedArtifactsPermitted()) {
javac_state.removeUnidentifiedArtifacts();
}
// Go through all sources and taint all packages that miss artifacts.
javac_state.taintPackagesThatMissArtifacts();
try { try {
// Find all sources, use the suffix rules to know which files are sources.
Map<String,Source> sources = new HashMap<>();
// Find the files, this will automatically populate the found modules
// with found packages where the sources are found!
findSourceFiles(options.getSources(),
suffixRules.keySet(),
sources,
modules,
current_module,
options.isDefaultPackagePermitted(),
false);
if (sources.isEmpty()) {
Log.error("Found nothing to compile!");
return RC_FATAL;
}
// Create a map of all source files that are available for linking. Both -src and
// -sourcepath point to such files. It is possible to specify multiple
// -sourcepath options to enable different filtering rules. If the
// filters are the same for multiple sourcepaths, they may be concatenated
// using :(;). Before sending the list of sourcepaths to javac, they are
// all concatenated. The list created here is used by the SmartFileWrapper to
// make sure only the correct sources are actually available.
// We might find more modules here as well.
Map<String,Source> sources_to_link_to = new HashMap<>();
List<SourceLocation> sourceResolutionLocations = new ArrayList<>();
sourceResolutionLocations.addAll(options.getSources());
sourceResolutionLocations.addAll(options.getSourceSearchPaths());
findSourceFiles(sourceResolutionLocations,
Collections.singleton(".java"),
sources_to_link_to,
modules,
current_module,
options.isDefaultPackagePermitted(),
true);
// Add the set of sources to the build database.
javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
javac_state.now().checkInternalState("checking sources", false, sources);
javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
javac_state.setVisibleSources(sources_to_link_to);
int round = 0;
printRound(round);
// If there is any change in the source files, taint packages
// and mark the database in need of saving.
javac_state.checkSourceStatus(false);
// Find all existing artifacts. Their timestamp will match the last modified timestamps stored
// in javac_state, simply because loading of the JavacState will clean out all artifacts
// that do not match the javac_state database.
javac_state.findAllArtifacts();
// Remove unidentified artifacts from the bin, gensrc and header dirs.
// (Unless we allow them to be there.)
// I.e. artifacts that are not known according to the build database (javac_state).
// For examples, files that have been manually copied into these dirs.
// Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
// in javac_state) have already been removed when the javac_state was loaded.
if (!options.areUnidentifiedArtifactsPermitted()) {
javac_state.removeUnidentifiedArtifacts();
}
// Go through all sources and taint all packages that miss artifacts.
javac_state.taintPackagesThatMissArtifacts();
// Check recorded classpath public apis. Taint packages that depend on // Check recorded classpath public apis. Taint packages that depend on
// classpath classes whose public apis have changed. // classpath classes whose public apis have changed.
javac_state.taintPackagesDependingOnChangedClasspathPackages(); javac_state.taintPackagesDependingOnChangedClasspathPackages();
@ -229,8 +229,16 @@ public class SjavacImpl implements Sjavac {
// (Generated sources must always have a package.) // (Generated sources must always have a package.)
Map<String,Source> generated_sources = new HashMap<>(); Map<String,Source> generated_sources = new HashMap<>();
Source.scanRoot(Util.pathToFile(options.getGenSrcDir()), Util.set(".java"), null, null, null, null, Source.scanRoot(Util.pathToFile(options.getGenSrcDir()),
generated_sources, modules, current_module, false, true, false); Util.set(".java"),
Collections.emptyList(),
Collections.emptyList(),
generated_sources,
modules,
current_module,
false,
true,
false);
javac_state.now().flattenPackagesSourcesAndArtifacts(modules); javac_state.now().flattenPackagesSourcesAndArtifacts(modules);
// Recheck the the source files and their timestamps again. // Recheck the the source files and their timestamps again.
javac_state.checkSourceStatus(true); javac_state.checkSourceStatus(true);
@ -254,7 +262,10 @@ public class SjavacImpl implements Sjavac {
printRound(round); printRound(round);
// Clean out artifacts in tainted packages. // Clean out artifacts in tainted packages.
javac_state.deleteClassArtifactsInTaintedPackages(); javac_state.deleteClassArtifactsInTaintedPackages();
again = javac_state.performJavaCompilations(compilationService, options, recently_compiled, rc); again = javac_state.performJavaCompilations(compilationService,
options,
recently_compiled,
rc);
if (!rc[0]) { if (!rc[0]) {
Log.debug("Compilation failed."); Log.debug("Compilation failed.");
break; break;
@ -344,7 +355,8 @@ public class SjavacImpl implements Sjavac {
Map<String, Module> foundModules, Map<String, Module> foundModules,
Module currentModule, Module currentModule,
boolean permitSourcesInDefaultPackage, boolean permitSourcesInDefaultPackage,
boolean inLinksrc) { boolean inLinksrc)
throws IOException {
for (SourceLocation source : sourceLocations) { for (SourceLocation source : sourceLocations) {
source.findSourceFiles(sourceTypes, source.findSourceFiles(sourceTypes,

View file

@ -93,7 +93,7 @@ public enum Option {
CLASSPATH.processMatching(iter, helper); CLASSPATH.processMatching(iter, helper);
} }
}, },
X("-x", "Exclude directory from the subsequent source directory") { X("-x", "Exclude files matching the given pattern") {
@Override @Override
protected void processMatching(ArgumentIterator iter, OptionHelper helper) { protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
String pattern = getFilePatternArg(iter, helper); String pattern = getFilePatternArg(iter, helper);
@ -101,7 +101,7 @@ public enum Option {
helper.exclude(pattern); helper.exclude(pattern);
} }
}, },
I("-i", "Include only the given directory from the subsequent source directory") { I("-i", "Include only files matching the given pattern") {
@Override @Override
protected void processMatching(ArgumentIterator iter, OptionHelper helper) { protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
String pattern = getFilePatternArg(iter, helper); String pattern = getFilePatternArg(iter, helper);
@ -109,22 +109,6 @@ public enum Option {
helper.include(pattern); helper.include(pattern);
} }
}, },
XF("-xf", "Exclude a given file") {
@Override
protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
String pattern = getFilePatternArg(iter, helper);
if (pattern != null)
helper.excludeFile(pattern);
}
},
IF("-if", "Include only the given file") {
@Override
protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
String pattern = getFilePatternArg(iter, helper);
if (pattern != null)
helper.includeFile(pattern);
}
},
TR("-tr", "Translate resources") { TR("-tr", "Translate resources") {
@Override @Override
protected void processMatching(ArgumentIterator iter, OptionHelper helper) { protected void processMatching(ArgumentIterator iter, OptionHelper helper) {
@ -338,7 +322,7 @@ public enum Option {
String getFilePatternArg(ArgumentIterator iter, OptionHelper helper) { String getFilePatternArg(ArgumentIterator iter, OptionHelper helper) {
if (!iter.hasNext()) { if (!iter.hasNext()) {
helper.reportError(arg + " must be followed by a file or directory pattern."); helper.reportError(arg + " must be followed by a glob pattern.");
return null; return null;
} }

View file

@ -53,12 +53,6 @@ public abstract class OptionHelper {
/** Record a package inclusion pattern */ /** Record a package inclusion pattern */
public abstract void include(String incl); public abstract void include(String incl);
/** Record a file exclusion */
public abstract void excludeFile(String exclFile);
/** Record a file inclusion */
public abstract void includeFile(String inclFile);
/** Record a root of sources to be compiled */ /** Record a root of sources to be compiled */
public abstract void sourceRoots(List<Path> path); public abstract void sourceRoots(List<Path> path);

View file

@ -220,8 +220,6 @@ public class Options {
for (SourceLocation sl : locs) { for (SourceLocation sl : locs) {
for (String pkg : sl.includes) addArg(Option.I, pkg); for (String pkg : sl.includes) addArg(Option.I, pkg);
for (String pkg : sl.excludes) addArg(Option.X, pkg); for (String pkg : sl.excludes) addArg(Option.X, pkg);
for (String f : sl.excludedFiles) addArg(Option.XF, f);
for (String f : sl.includedFiles) addArg(Option.IF, f);
addArg(opt, sl.getPath()); addArg(opt, sl.getPath());
} }
} }
@ -379,18 +377,6 @@ public class Options {
includes.add(inclPattern); includes.add(inclPattern);
} }
@Override
public void excludeFile(String exclFilePattern) {
exclFilePattern = Util.normalizeDriveLetter(exclFilePattern);
excludeFiles.add(exclFilePattern);
}
@Override
public void includeFile(String inclFilePattern) {
inclFilePattern = Util.normalizeDriveLetter(inclFilePattern);
includeFiles.add(inclFilePattern);
}
@Override @Override
public void addTransformer(String suffix, Transformer tr) { public void addTransformer(String suffix, Transformer tr) {
if (trRules.containsKey(suffix)) { if (trRules.containsKey(suffix)) {
@ -519,9 +505,7 @@ public class Options {
result.add(new SourceLocation( result.add(new SourceLocation(
path, path,
includes, includes,
excludes, excludes));
includeFiles,
excludeFiles));
} }
resetFilters(); resetFilters();
return result; return result;

View file

@ -25,11 +25,13 @@
package com.sun.tools.sjavac.options; package com.sun.tools.sjavac.options;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.sun.tools.sjavac.Log;
import com.sun.tools.sjavac.Module; import com.sun.tools.sjavac.Module;
import com.sun.tools.sjavac.ProblemException; import com.sun.tools.sjavac.ProblemException;
import com.sun.tools.sjavac.Source; import com.sun.tools.sjavac.Source;
@ -49,18 +51,14 @@ public class SourceLocation {
private Path path; private Path path;
// Package include / exclude patterns and file includes / excludes. // Package include / exclude patterns and file includes / excludes.
List<String> includes, excludes, includedFiles, excludedFiles; List<String> includes, excludes;
public SourceLocation(Path path, public SourceLocation(Path path,
List<String> includes, List<String> includes,
List<String> excludes, List<String> excludes) {
List<String> includedFiles,
List<String> excludedFiles) {
this.path = path; this.path = path;
this.includes = includes; this.includes = includes;
this.excludes = excludes; this.excludes = excludes;
this.includedFiles = includedFiles;
this.excludedFiles = excludedFiles;
} }
@ -81,17 +79,23 @@ public class SourceLocation {
Map<String, Module> foundModules, Map<String, Module> foundModules,
Module currentModule, Module currentModule,
boolean permitSourcesInDefaultPackage, boolean permitSourcesInDefaultPackage,
boolean inLinksrc) { boolean inLinksrc)
throws IOException {
try { try {
Source.scanRoot(path.toFile(), suffixes, excludes, includes, Source.scanRoot(path.toFile(),
excludedFiles, includedFiles, foundFiles, foundModules, suffixes,
currentModule, permitSourcesInDefaultPackage, false, excludes,
inLinksrc); includes,
foundFiles,
foundModules,
currentModule,
permitSourcesInDefaultPackage,
false,
inLinksrc);
} catch (ProblemException e) { } catch (ProblemException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/** Get the root directory of this source location */ /** Get the root directory of this source location */
public Path getPath() { public Path getPath() {
return path; return path;
@ -107,14 +111,9 @@ public class SourceLocation {
return excludes; return excludes;
} }
/** Get the file include patterns */ @Override
public List<String> getIncludedFiles() { public String toString() {
return includedFiles; return String.format("%s[\"%s\", includes: %s, excludes: %s]",
getClass().getSimpleName(), path, includes, excludes);
} }
/** Get the file exclude patterns */
public List<String> getExcludedFiles() {
return excludedFiles;
}
} }

View file

@ -55,9 +55,9 @@ public class CompileExcludingDependency extends SJavacTester {
tb.writeFile(GENSRC.resolve("beta/B.java"), tb.writeFile(GENSRC.resolve("beta/B.java"),
"package beta; public class B { }"); "package beta; public class B { }");
compile("-x", "beta", compile("-x", "beta/*",
"-src", GENSRC.toString(), "-src", GENSRC.toString(),
"-x", "alfa/omega", "-x", "alfa/omega/*",
"-sourcepath", GENSRC.toString(), "-sourcepath", GENSRC.toString(),
"-d", BIN.toString(), "-d", BIN.toString(),
"--state-dir=" + BIN, "--state-dir=" + BIN,

View file

@ -47,8 +47,8 @@ public class CompileWithAtFile extends SJavacTester {
void test() throws Exception { void test() throws Exception {
tb.writeFile(GENSRC.resolve("list.txt"), tb.writeFile(GENSRC.resolve("list.txt"),
"-if */alfa/omega/A.java\n" + "-i alfa/omega/A.java\n" +
"-if */beta/B.java\n" + "-i beta/B.java\n" +
GENSRC + "\n" + GENSRC + "\n" +
"-d " + BIN + "\n" + "-d " + BIN + "\n" +
"--state-dir=" + BIN + "\n"); "--state-dir=" + BIN + "\n");

View file

@ -64,7 +64,7 @@ public class CompileWithInvisibleSources extends SJavacTester {
"package beta; public class B { }"); "package beta; public class B { }");
compile(GENSRC.toString(), compile(GENSRC.toString(),
"-x", "beta", "-x", "beta/*",
"-sourcepath", GENSRC2.toString(), "-sourcepath", GENSRC2.toString(),
"-sourcepath", GENSRC3.toString(), "-sourcepath", GENSRC3.toString(),
"-d", BIN.toString(), "-d", BIN.toString(),

View file

@ -62,7 +62,7 @@ public class CompileWithOverrideSources extends SJavacTester {
tb.writeFile(GENSRC2.resolve("beta/B.java"), tb.writeFile(GENSRC2.resolve("beta/B.java"),
"package beta; public class B { }"); "package beta; public class B { }");
compile("-x", "beta", compile("-x", "beta/*",
GENSRC.toString(), GENSRC.toString(),
GENSRC2.toString(), GENSRC2.toString(),
"-d", BIN.toString(), "-d", BIN.toString(),

View file

@ -1,94 +0,0 @@
/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8037085
* @summary Ensures that sjavac can handle various exclusion patterns.
*
* @modules jdk.compiler/com.sun.tools.sjavac
* @build Wrapper
* @run main Wrapper ExclPattern
*/
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class ExclPattern {
public static void main(String[] ignore) throws IOException {
String toBeExcluded = "pkg/excl-dir/excluded.txt";
String toBeIncluded = "pkg/incl-dir/included.txt";
// Set up source directory with directory to be excluded
populate(Paths.get("srcdir"),
"pkg/SomeClass.java",
"package pkg; public class SomeClass { }",
toBeExcluded,
"This file should not end up in the dest directory.",
toBeIncluded,
"This file should end up in the dest directory.");
String[] args = {
"-x", "pkg/excl-dir/*",
"-src", "srcdir",
"-d", "dest",
"--state-dir=dest",
"-j", "1",
"-copy", ".txt",
"--server:portfile=testserver,background=false",
"--log=debug"
};
int rc = com.sun.tools.sjavac.Main.go(args);
if (rc != 0) throw new RuntimeException("Error during compile!");
if (!Files.exists(Paths.get("dest/" + toBeIncluded)))
throw new AssertionError("File missing: " + toBeIncluded);
if (Files.exists(Paths.get("dest/" + toBeExcluded)))
throw new AssertionError("File present: " + toBeExcluded);
}
static void populate(Path root, String... args) throws IOException {
if (!Files.exists(root))
Files.createDirectory(root);
for (int i = 0; i < args.length; i += 2) {
String filename = args[i];
String content = args[i+1];
Path p = root.resolve(filename);
Files.createDirectories(p.getParent());
try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(p,
Charset.defaultCharset()))) {
out.println(content);
}
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8144226
* @summary Ensures that excluded files are inaccessible (even for implicit
* compilation)
*
* @modules jdk.compiler/com.sun.tools.sjavac
* @library /tools/lib
* @build Wrapper ToolBox
* @run main Wrapper HiddenFiles
*/
import com.sun.tools.javac.util.Assert;
import com.sun.tools.sjavac.server.Sjavac;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class HiddenFiles extends SjavacBase {
public static void main(String[] ignore) throws Exception {
Path BIN = Paths.get("bin");
Path STATE_DIR = Paths.get("state-dir");
Path SRC = Paths.get("src");
Files.createDirectories(BIN);
Files.createDirectories(STATE_DIR);
toolbox.writeJavaFiles(SRC, "package pkg; class A { B b; }");
toolbox.writeJavaFiles(SRC, "package pkg; class B { }");
// This compilation should fail (return RC_FATAL) since A.java refers to B.java and B.java
// is excluded.
int rc = compile("-x", "pkg/B.java", SRC.toString(),
"--server:portfile=testportfile,background=false",
"-d", BIN.toString(),
"--state-dir=" + STATE_DIR);
Assert.check(rc == Sjavac.RC_FATAL, "Compilation succeeded unexpectedly.");
}
}

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8037085
* @summary Ensures that sjavac can handle various exclusion patterns.
*
* @modules jdk.compiler/com.sun.tools.sjavac
* @library /tools/lib
* @build Wrapper ToolBox
* @run main Wrapper IncludeExcludePatterns
*/
import com.sun.tools.javac.util.Assert;
import com.sun.tools.sjavac.server.Sjavac;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class IncludeExcludePatterns extends SjavacBase {
final Path SRC = Paths.get("src");
final Path BIN = Paths.get("bin");
final Path STATE_DIR = Paths.get("state-dir");
// An arbitrarily but sufficiently complicated source tree.
final Path A = Paths.get("pkga/A.java");
final Path X1 = Paths.get("pkga/subpkg/Xx.java");
final Path Y = Paths.get("pkga/subpkg/subsubpkg/Y.java");
final Path B = Paths.get("pkgb/B.java");
final Path C = Paths.get("pkgc/C.java");
final Path X2 = Paths.get("pkgc/Xx.java");
final Path[] ALL_PATHS = {A, X1, Y, B, C, X2};
public static void main(String[] ignore) throws Exception {
new IncludeExcludePatterns().runTest();
}
public void runTest() throws IOException, ReflectiveOperationException {
Files.createDirectories(BIN);
Files.createDirectories(STATE_DIR);
for (Path p : ALL_PATHS) {
writeDummyClass(p);
}
// Single file
testPattern("pkga/A.java", A);
// Leading wild cards
testPattern("*/A.java", A);
testPattern("**/Xx.java", X1, X2);
testPattern("**x.java", X1, X2);
// Wild card in middle of path
testPattern("pkga/*/Xx.java", X1);
testPattern("pkga/**/Y.java", Y);
// Trailing wild cards
testPattern("pkga/*", A);
testPattern("pkga/**", A, X1, Y);
// Multiple wildcards
testPattern("pkga/*/*/Y.java", Y);
testPattern("**/*/**", X1, Y);
}
// Given "src/pkg/subpkg/A.java" this method returns "A"
String classNameOf(Path javaFile) {
return javaFile.getFileName()
.toString()
.replace(".java", "");
}
// Puts an empty (dummy) class definition in the given path.
void writeDummyClass(Path javaFile) throws IOException {
String pkg = javaFile.getParent().toString().replace('/', '.');
String cls = javaFile.getFileName().toString().replace(".java", "");
toolbox.writeFile(SRC.resolve(javaFile), "package " + pkg + "; class " + cls + " {}");
}
void testPattern(String filterArgs, Path... sourcesExpectedToBeVisible)
throws ReflectiveOperationException, IOException {
testFilter("-i " + filterArgs, Arrays.asList(sourcesExpectedToBeVisible));
Set<Path> complement = new HashSet<>(Arrays.asList(ALL_PATHS));
complement.removeAll(Arrays.asList(sourcesExpectedToBeVisible));
testFilter("-x " + filterArgs, complement);
}
void testFilter(String filterArgs, Collection<Path> sourcesExpectedToBeVisible)
throws IOException, ReflectiveOperationException {
System.out.println("Testing filter: " + filterArgs);
toolbox.cleanDirectory(BIN);
toolbox.cleanDirectory(STATE_DIR);
String args = filterArgs + " " + SRC
+ " --server:portfile=testportfile,background=false"
+ " -d " + BIN
+ " --state-dir=" + STATE_DIR;
int rc = compile((Object[]) args.split(" "));
// Compilation should always pass in these tests
Assert.check(rc == Sjavac.RC_OK, "Compilation failed unexpectedly.");
// The resulting .class files should correspond to the visible source files
Set<Path> result = allFilesInDir(BIN);
Set<Path> expected = correspondingClassFiles(sourcesExpectedToBeVisible);
if (!result.equals(expected)) {
System.out.println("Result:");
printPaths(result);
System.out.println("Expected:");
printPaths(expected);
Assert.error("Test case failed: " + filterArgs);
}
}
void printPaths(Collection<Path> paths) {
paths.stream()
.sorted()
.forEachOrdered(p -> System.out.println(" " + p));
}
// Given "pkg/A.java, pkg/B.java" this method returns "bin/pkg/A.class, bin/pkg/B.class"
Set<Path> correspondingClassFiles(Collection<Path> javaFiles) {
return javaFiles.stream()
.map(javaFile -> javaFile.resolveSibling(classNameOf(javaFile) + ".class"))
.map(BIN::resolve)
.collect(Collectors.toSet());
}
Set<Path> allFilesInDir(Path p) throws IOException {
try (Stream<Path> files = Files.walk(p).filter(Files::isRegularFile)) {
return files.collect(Collectors.toSet());
}
}
}

View file

@ -61,7 +61,6 @@ public class OptionDecoding {
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
testPaths(); testPaths();
testDupPaths(); testDupPaths();
testSourceLocations();
testSimpleOptions(); testSimpleOptions();
testServerConf(); testServerConf();
testSearchPaths(); testSearchPaths();
@ -110,78 +109,6 @@ public class OptionDecoding {
} }
} }
// Test source locations and -x, -i, -xf, -if filters
static void testSourceLocations() throws IOException {
Path a1 = Paths.get("root/pkg1/ClassA1.java");
Path a2 = Paths.get("root/pkg1/ClassA2.java");
Path b1 = Paths.get("root/pkg1/pkg2/ClassB1.java");
Path b2 = Paths.get("root/pkg1/pkg2/ClassB2.java");
Path c1 = Paths.get("root/pkg3/ClassC1.java");
Path c2 = Paths.get("root/pkg3/ClassC2.java");
for (Path p : Arrays.asList(a1, a2, b1, b2, c1, c2)) {
Files.createDirectories(p.getParent());
Files.createFile(p);
}
// Test -if
{
Options options = Options.parseArgs("-if", "root/pkg1/ClassA1.java", "root");
Map<String, Source> foundFiles = new HashMap<>();
SjavacImpl.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
new HashMap<String, Module>(), new Module("", ""), false, true);
checkFilesFound(foundFiles.keySet(), a1);
}
// Test -i
System.out.println("--------------------------- CHECKING -i ----------------");
{
Options options = Options.parseArgs("-i", "pkg1/*", "root");
Map<String, Source> foundFiles = new HashMap<>();
SjavacImpl.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
new HashMap<String, Module>(), new Module("", ""), false, true);
checkFilesFound(foundFiles.keySet(), a1, a2, b1, b2);
}
System.out.println("--------------------------------------------------------");
// Test -xf
{
Options options = Options.parseArgs("-xf", "root/pkg1/ClassA1.java", "root");
Map<String, Source> foundFiles = new HashMap<>();
SjavacImpl.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
new HashMap<String, Module>(), new Module("", ""), false, true);
checkFilesFound(foundFiles.keySet(), a2, b1, b2, c1, c2);
}
// Test -x
{
Options options = Options.parseArgs("-i", "pkg1/*", "root");
Map<String, Source> foundFiles = new HashMap<>();
SjavacImpl.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
new HashMap<String, Module>(), new Module("", ""), false, true);
checkFilesFound(foundFiles.keySet(), a1, a2, b1, b2);
}
// Test -x and -i
{
Options options = Options.parseArgs("-i", "pkg1/*", "-x", "pkg1/pkg2/*", "root");
Map<String, Source> foundFiles = new HashMap<>();
SjavacImpl.findSourceFiles(options.getSources(), Collections.singleton(".java"), foundFiles,
new HashMap<String, Module>(), new Module("", ""), false, true);
checkFilesFound(foundFiles.keySet(), a1, a2);
}
}
// Test basic options // Test basic options
static void testSimpleOptions() { static void testSimpleOptions() {
Options options = Options.parseArgs("-j", "17", "--log=debug"); Options options = Options.parseArgs("-j", "17", "--log=debug");
@ -216,8 +143,8 @@ public class OptionDecoding {
List<String> i, x, iF, xF; List<String> i, x, iF, xF;
i = x = iF = xF = new ArrayList<>(); i = x = iF = xF = new ArrayList<>();
SourceLocation dir1 = new SourceLocation(Paths.get("dir1"), i, x, iF, xF); SourceLocation dir1 = new SourceLocation(Paths.get("dir1"), i, x);
SourceLocation dir2 = new SourceLocation(Paths.get("dir2"), i, x, iF, xF); SourceLocation dir2 = new SourceLocation(Paths.get("dir2"), i, x);
String dir1_PS_dir2 = "dir1" + File.pathSeparator + "dir2"; String dir1_PS_dir2 = "dir1" + File.pathSeparator + "dir2";
Options options = Options.parseArgs("-sourcepath", dir1_PS_dir2); Options options = Options.parseArgs("-sourcepath", dir1_PS_dir2);

View file

@ -58,8 +58,6 @@ public class Serialization {
Option.D.arg, "dest", Option.D.arg, "dest",
Option.I.arg, "pkg/*", Option.I.arg, "pkg/*",
Option.X.arg, "pkg/pkg/*", Option.X.arg, "pkg/pkg/*",
Option.IF.arg, "root/pkg/MyClass1.java",
Option.XF.arg, "root/pkg/MyClass2.java",
Option.SRC.arg, "root", Option.SRC.arg, "root",
Option.SOURCEPATH.arg, "sourcepath", Option.SOURCEPATH.arg, "sourcepath",
Option.CLASSPATH.arg, "classpath", Option.CLASSPATH.arg, "classpath",
@ -87,8 +85,6 @@ public class Serialization {
assertEquals(sl1.getPath(), sl2.getPath()); assertEquals(sl1.getPath(), sl2.getPath());
assertEquals(sl1.getIncludes(), sl2.getIncludes()); assertEquals(sl1.getIncludes(), sl2.getIncludes());
assertEquals(sl1.getExcludes(), sl2.getExcludes()); assertEquals(sl1.getExcludes(), sl2.getExcludes());
assertEquals(sl1.getIncludedFiles(), sl2.getIncludedFiles());
assertEquals(sl1.getExcludedFiles(), sl2.getExcludedFiles());
assertEquals(options1.getClassSearchPath(), options2.getClassSearchPath()); assertEquals(options1.getClassSearchPath(), options2.getClassSearchPath());
assertEquals(options1.getSourceSearchPaths(), options2.getSourceSearchPaths()); assertEquals(options1.getSourceSearchPaths(), options2.getSourceSearchPaths());

View file

@ -62,9 +62,7 @@ public class OptionTestUtil {
if (!sl1.getPath().equals(sl2.getPath()) || if (!sl1.getPath().equals(sl2.getPath()) ||
!sl1.getIncludes().equals(sl2.getIncludes()) || !sl1.getIncludes().equals(sl2.getIncludes()) ||
!sl1.getExcludes().equals(sl2.getExcludes()) || !sl1.getExcludes().equals(sl2.getExcludes()))
!sl1.getIncludedFiles().equals(sl2.getIncludedFiles()) ||
!sl1.getExcludedFiles().equals(sl2.getExcludedFiles()))
throw new AssertionError("Expected " + sl1 + " but got " + sl2); throw new AssertionError("Expected " + sl1 + " but got " + sl2);
} }
} }