external compiler: compute compilation classpath using common JpsDependenciesEnumerator

This commit is contained in:
nik
2012-08-30 17:53:56 +04:00
parent a034231aee
commit d6bb8a041a
12 changed files with 104 additions and 173 deletions
@@ -1,18 +1,22 @@
package org.jetbrains.jps;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.JpsProject;
import org.jetbrains.jps.model.JpsSimpleElement;
import org.jetbrains.jps.model.java.JavaSourceRootProperties;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.java.JpsJavaClasspathKind;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.library.JpsLibrary;
import org.jetbrains.jps.model.java.*;
import org.jetbrains.jps.model.library.JpsOrderRootType;
import org.jetbrains.jps.model.module.*;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.model.module.JpsDependencyElement;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
import org.jetbrains.jps.model.module.JpsSdkDependency;
import java.io.File;
import java.util.*;
@@ -21,93 +25,69 @@ import java.util.*;
* @author Eugene Zhuravlev
* Date: 9/30/11
*/
//todo[nik] use JpsDependenciesEnumerator instead
public class ProjectPaths {
private static final String DEFAULT_GENERATED_DIR_NAME = "generated";
@NotNull
private final JpsProject myProject;
//private final Map<JpsJavaClasspathKind, Map<ModuleChunk, List<String>>> myCachedClasspath = new HashMap<JpsJavaClasspathKind, Map<ModuleChunk, List<String>>>();
public ProjectPaths(JpsProject project) {
public ProjectPaths(@NotNull JpsProject project) {
myProject = project;
}
public Collection<File> getClasspathFiles(ModuleChunk chunk, JpsJavaClasspathKind kind) {
return getClasspathFiles(chunk, kind, !kind.isRuntime());
public Collection<File> getCompilationClasspathFiles(ModuleChunk chunk, boolean includeTests) {
return getCompilationClasspathFiles(chunk, includeTests, true, true);
}
public List<String> getClasspath(ModuleChunk chunk, JpsJavaClasspathKind kind) {
return getPathsList(getClasspathFiles(chunk, kind));
}
public Collection<File> getClasspathFiles(ModuleChunk chunk, JpsJavaClasspathKind kind, final boolean excludeMainModuleOutput) {
return getClasspathFiles(chunk, kind, excludeMainModuleOutput, ClasspathPart.WHOLE);
public Collection<File> getCompilationClasspathFiles(ModuleChunk chunk,
boolean includeTests,
final boolean excludeMainModuleOutput,
final boolean exportedOnly) {
return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(includeTests), excludeMainModuleOutput, ClasspathPart.WHOLE, exportedOnly);
}
public Collection<File> getPlatformCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.isTests()), excludeMainModuleOutput, ClasspathPart.BEFORE_JDK);
return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.isTests()), excludeMainModuleOutput, ClasspathPart.BEFORE_JDK, true);
}
public Collection<File> getCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.isTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK);
return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.isTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK, true);
}
private Collection<File> getClasspathFiles(ModuleChunk chunk, JpsJavaClasspathKind kind, final boolean excludeMainModuleOutput, ClasspathPart classpathPart) {
private Collection<File> getClasspathFiles(ModuleChunk chunk,
JpsJavaClasspathKind kind,
final boolean excludeMainModuleOutput,
ClasspathPart classpathPart, final boolean exportedOnly) {
final Set<File> files = new LinkedHashSet<File>();
for (JpsModule module : chunk.getModules()) {
final ClasspathItemFilter filter = classpathPart == ClasspathPart.WHOLE ? ACCEPT_ALL :
classpathPart == ClasspathPart.BEFORE_JDK ? new BeforeSdkItemFilter(module) : new NotFilter(new BeforeSdkItemFilter(module));
collectClasspath(module, kind, files, new HashSet<JpsModule>(), false, excludeMainModuleOutput, false, filter);
JpsJavaDependenciesEnumerator enumerator = JpsJavaExtensionService.dependencies(module).includedIn(kind).recursively();
if (exportedOnly) {
enumerator = enumerator.exportedOnly();
}
if (classpathPart == ClasspathPart.BEFORE_JDK) {
enumerator = enumerator.satisfying(new BeforeSdkItemFilter(module));
}
else if (classpathPart == ClasspathPart.AFTER_JDK) {
enumerator = enumerator.satisfying(Conditions.not(new BeforeSdkItemFilter(module))).withoutSdk();
}
JpsJavaDependenciesRootsEnumerator rootsEnumerator = enumerator.classes();
if (excludeMainModuleOutput) {
rootsEnumerator = rootsEnumerator.withoutSelfModuleOutput();
}
files.addAll(rootsEnumerator.getRoots());
}
if (classpathPart == ClasspathPart.BEFORE_JDK) {
for (JpsModule module : chunk.getModules()) {
JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE);
if (sdk != null) {
files.addAll(sdk.getParent().getFiles(JpsOrderRootType.COMPILED));
}
}
}
return files;
}
private void collectClasspath(JpsModule module, JpsJavaClasspathKind kind, Set<File> classpath, Set<JpsModule> processed, boolean exportedOnly,
boolean excludeMainModuleOutput, final boolean excludeSdk, ClasspathItemFilter filter) {
if (!processed.add(module)) {
return;
}
for (JpsDependencyElement it : JpsJavaExtensionService.getInstance().getDependencies(module, kind, exportedOnly)) {
if (!filter.accept(module, it) || it instanceof JpsSdkDependency && excludeSdk) {
continue;
}
if (it instanceof JpsModuleSourceDependency) {
if (!excludeMainModuleOutput && kind.isTestsIncluded()) {
final File out = getModuleOutputDir(module, true);
if (out != null) {
classpath.add(out);
}
}
if (!excludeMainModuleOutput || kind.isTestsIncluded()) {
final File out = getModuleOutputDir(module, false);
if (out != null) {
classpath.add(out);
}
}
}
else if (it instanceof JpsModuleDependency) {
final JpsModule dep = ((JpsModuleDependency)it).getModule();
if (dep != null) {
collectClasspath(dep, kind, classpath, processed, !kind.isRuntime(), false, true, filter);
}
}
else if (it instanceof JpsLibraryDependency) {
addLibraryFiles(classpath, ((JpsLibraryDependency)it).getLibrary());
}
else if (it instanceof JpsSdkDependency) {
addLibraryFiles(classpath, ((JpsSdkDependency)it).resolveSdk());
}
}
}
private static void addLibraryFiles(Set<File> classpath, @Nullable JpsLibrary library) {
if (library != null) {
classpath.addAll(library.getFiles(JpsOrderRootType.COMPILED));
}
}
private static void addFile(Set<File> classpath, @Nullable String url) {
if (url != null) {
classpath.add(JpsPathUtil.urlToFile(url));
@@ -123,17 +103,19 @@ public class ProjectPaths {
}
/**
*
* @param chunk
* @param includeTests
* @return mapping "sourceRoot" -> "package prefix" Package prefix uses slashes instead of dots and ends with trailing slash
*/
public static Map<File, String> getSourceRootsWithDependents(ModuleChunk chunk, boolean includeTests) {
public static Map<File, String> getSourceRootsWithDependents(ModuleChunk chunk) {
final boolean includeTests = chunk.isTests();
final Map<File, String> result = new LinkedHashMap<File, String>();
collectPathsRecursively(chunk, JpsJavaClasspathKind.compile(includeTests), new PathsGetter() {
public void apply(JpsModule module, JpsJavaClasspathKind kind) {
processModulesRecursively(chunk, JpsJavaClasspathKind.compile(includeTests), new Consumer<JpsModule>() {
@Override
public void consume(JpsModule module) {
for (JpsModuleSourceRoot root : module.getSourceRoots()) {
if (root.getRootType().equals(JavaSourceRootType.SOURCE) ||
kind.isTestsIncluded() && root.getRootType().equals(JavaSourceRootType.TEST_SOURCE)) {
includeTests && root.getRootType().equals(JavaSourceRootType.TEST_SOURCE)) {
JavaSourceRootProperties properties = (JavaSourceRootProperties)((JpsSimpleElement<?>)root.getProperties()).getData();
String prefix = properties.getPackagePrefix();
if (!prefix.isEmpty()) {
@@ -153,10 +135,12 @@ public class ProjectPaths {
return result;
}
public static Collection<File> getOutputPathsWithDependents(final ModuleChunk chunk, final boolean forTests) {
public static Collection<File> getOutputPathsWithDependents(final ModuleChunk chunk) {
final boolean forTests = chunk.isTests();
final Set<File> sourcePaths = new LinkedHashSet<File>();
collectPathsRecursively(chunk, JpsJavaClasspathKind.compile(forTests), new PathsGetter() {
public void apply(JpsModule module, JpsJavaClasspathKind kind) {
processModulesRecursively(chunk, JpsJavaClasspathKind.compile(forTests), new Consumer<JpsModule>() {
@Override
public void consume(JpsModule module) {
addFile(sourcePaths, JpsJavaExtensionService.getInstance().getOutputUrl(module, forTests));
}
});
@@ -164,40 +148,11 @@ public class ProjectPaths {
}
public static Set<JpsModule> getModulesWithDependentsRecursively(final JpsModule module, final boolean includeTests) {
final Set<JpsModule> result = new HashSet<JpsModule>();
collectPathsRecursively(module, JpsJavaClasspathKind.compile(includeTests), new HashSet<JpsModule>(), new PathsGetter() {
public void apply(JpsModule module, JpsJavaClasspathKind kind) {
result.add(module);
}
});
return result;
return JpsJavaExtensionService.dependencies(module).includedIn(JpsJavaClasspathKind.compile(includeTests)).recursively().getModules();
}
private interface PathsGetter {
void apply(JpsModule module, JpsJavaClasspathKind kind);
}
private static void collectPathsRecursively(ModuleChunk chunk, JpsJavaClasspathKind kind, PathsGetter proc) {
final HashSet<JpsModule> processed = new HashSet<JpsModule>();
for (JpsModule module : chunk.getModules()) {
collectPathsRecursively(module, kind, processed, proc);
}
}
private static void collectPathsRecursively(JpsModule module, JpsJavaClasspathKind kind, Set<JpsModule> processed, PathsGetter processor) {
if (processed.add(module)) {
for (JpsDependencyElement item : JpsJavaExtensionService.getInstance().getDependencies(module, kind, false)) {
if (item instanceof JpsModuleSourceDependency) {
processor.apply(module, kind);
}
else if (item instanceof JpsModuleDependency) {
final JpsModule dep = ((JpsModuleDependency)item).getModule();
if (dep != null) {
collectPathsRecursively(dep, kind, processed, processor);
}
}
}
}
private static void processModulesRecursively(ModuleChunk chunk, JpsJavaClasspathKind kind, Consumer<JpsModule> processor) {
JpsJavaExtensionService.getInstance().enumerateDependencies(chunk.getModules()).includedIn(kind).recursively().processModules(processor);
}
@Nullable
@@ -236,33 +191,14 @@ public class ProjectPaths {
}
public List<String> getProjectRuntimeClasspath(boolean includeTests) {
Set<File> classpath = new LinkedHashSet<File>();
final JpsJavaClasspathKind kind = JpsJavaClasspathKind.runtime(includeTests);
for (JpsModule module : myProject.getModules()) {
collectClasspath(module, kind, classpath, new HashSet<JpsModule>(), false, false, false, WITHOUT_DEP_MODULES);
}
Set<File> classpath = new LinkedHashSet<File>(JpsJavaExtensionService.dependencies(myProject).includedIn(kind).withoutDepModules().classes().getRoots());
return getPathsList(classpath);
}
private interface ClasspathItemFilter {
boolean accept(JpsModule module, JpsDependencyElement item);
}
private enum ClasspathPart {WHOLE, BEFORE_JDK, AFTER_JDK}
private static final ClasspathItemFilter ACCEPT_ALL = new ClasspathItemFilter() {
public boolean accept(JpsModule module, JpsDependencyElement item) {
return true;
}
};
private static final ClasspathItemFilter WITHOUT_DEP_MODULES = new ClasspathItemFilter() {
public boolean accept(JpsModule module, JpsDependencyElement item) {
return !(item instanceof JpsModuleDependency);
}
};
private static class BeforeSdkItemFilter implements ClasspathItemFilter {
private static class BeforeSdkItemFilter implements Condition<JpsDependencyElement> {
private JpsModule myModule;
private boolean mySdkFound;
@@ -270,24 +206,12 @@ public class ProjectPaths {
myModule = module;
}
public boolean accept(JpsModule module, JpsDependencyElement item) {
if (myModule.equals(module) && item instanceof JpsSdkDependency) {
@Override
public boolean value(JpsDependencyElement dependency) {
if (myModule.equals(dependency.getContainingModule()) && dependency instanceof JpsSdkDependency) {
mySdkFound = true;
return true;
}
return !mySdkFound && !(item instanceof JpsSdkDependency);
}
}
private static class NotFilter implements ClasspathItemFilter {
private ClasspathItemFilter myFilter;
private NotFilter(ClasspathItemFilter filter) {
myFilter = filter;
}
public boolean accept(JpsModule module, JpsDependencyElement item) {
return !myFilter.accept(module, item);
return !mySdkFound && !(dependency instanceof JpsSdkDependency);
}
}
@@ -322,7 +322,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
context.checkCanceled();
if (!forms.isEmpty() || addNotNullAssertions) {
final Map<File, String> chunkSourcePath = ProjectPaths.getSourceRootsWithDependents(chunk, chunk.isTests());
final Map<File, String> chunkSourcePath = ProjectPaths.getSourceRootsWithDependents(chunk);
final InstrumentationClassFinder finder = createInstrumentationClassFinder(platformCp, classpath, chunkSourcePath, outputSink);
try {
@@ -919,7 +919,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
final MyNestedFormLoader nestedFormsLoader =
new MyNestedFormLoader(chunkSourcePath, ProjectPaths.getOutputPathsWithDependents(chunk, chunk.isTests()));
new MyNestedFormLoader(chunkSourcePath, ProjectPaths.getOutputPathsWithDependents(chunk));
for (File formFile : formsToInstrument) {
final LwRootContainer rootContainer;
@@ -3,7 +3,6 @@ import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.jps.ModuleChunk
import org.jetbrains.jps.ProjectPaths
import org.jetbrains.jps.builders.rebuild.JpsRebuildTestCase
import org.jetbrains.jps.model.java.JpsJavaClasspathKind
/**
* @author nik
*/
@@ -22,35 +21,22 @@ public class ModuleClasspathTest extends JpsRebuildTestCase {
}
public void testSimpleClasspath() {
assertClasspath('util', JpsJavaClasspathKind.PRODUCTION_COMPILE, ["util/lib/exported.jar", "/jdk15.jar"])
assertClasspath('util', JpsJavaClasspathKind.PRODUCTION_RUNTIME, ["util/lib/exported.jar", "/jdk15.jar", "out/production/util"])
assertClasspath('util', false, ["util/lib/exported.jar", "/jdk15.jar"])
}
public void testScopes() {
assertClasspath("test-util", JpsJavaClasspathKind.PRODUCTION_COMPILE,
assertClasspath("test-util", false,
["/jdk.jar", "test-util/lib/provided.jar"])
assertClasspath("test-util", JpsJavaClasspathKind.TEST_COMPILE,
assertClasspath("test-util", true,
["/jdk.jar", "test-util/lib/provided.jar", "test-util/lib/test.jar", "out/production/test-util"])
assertClasspath("test-util", JpsJavaClasspathKind.PRODUCTION_RUNTIME,
["/jdk.jar", "test-util/lib/runtime.jar", "out/production/test-util"])
assertClasspath("test-util", JpsJavaClasspathKind.TEST_RUNTIME,
["/jdk.jar", "test-util/lib/provided.jar", "test-util/lib/runtime.jar",
"test-util/lib/test.jar", "out/test/test-util", "out/production/test-util"])
}
public void testDepModules() {
assertClasspath("main", JpsJavaClasspathKind.PRODUCTION_COMPILE,
assertClasspath("main", false,
["util/lib/exported.jar", "out/production/util", "/jdk.jar", "main/lib/service.jar"])
assertClasspath("main", JpsJavaClasspathKind.TEST_COMPILE,
assertClasspath("main", true,
["out/production/main", "util/lib/exported.jar", "out/test/util", "out/production/util", "/jdk.jar",
"out/test/test-util", "out/production/test-util", "main/lib/service.jar"])
assertClasspath("main", JpsJavaClasspathKind.PRODUCTION_RUNTIME,
["out/production/main", "util/lib/exported.jar", "out/production/util", "/jdk.jar", "main/lib/service.jar"])
assertClasspath("main", JpsJavaClasspathKind.TEST_RUNTIME,
["out/test/main", "out/production/main", "util/lib/exported.jar", "out/test/util", "out/production/util", "/jdk.jar",
"test-util/lib/provided.jar", "test-util/lib/runtime.jar", "test-util/lib/test.jar", "out/test/test-util",
"out/production/test-util","main/lib/service.jar"])
}
public void testCompilationClasspath() {
@@ -72,9 +58,9 @@ public class ModuleClasspathTest extends JpsRebuildTestCase {
return new ProjectPaths(myJpsProject)
}
private def assertClasspath(String moduleName, JpsJavaClasspathKind classpathKind, List<String> expected) {
private def assertClasspath(String moduleName, boolean includeTests, List<String> expected) {
ModuleChunk chunk = createChunk(moduleName)
final List<String> classpath = new ProjectPaths(myJpsProject).getClasspath(chunk, classpathKind)
final List<String> classpath = ProjectPaths.getPathsList(new ProjectPaths(myJpsProject).getCompilationClasspathFiles(chunk, includeTests))
assertClasspath(expected, toSystemIndependentPaths(classpath))
}
+1
View File
@@ -8,6 +8,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="annotations" />
<orderEntry type="module" module-name="util-rt" />
</component>
</module>
@@ -1,6 +1,8 @@
package org.jetbrains.jps.model.java;
import com.intellij.openapi.util.Condition;
import org.jetbrains.jps.model.module.JpsDependenciesEnumerator;
import org.jetbrains.jps.model.module.JpsDependencyElement;
/**
* @author nik
@@ -19,6 +21,9 @@ public interface JpsJavaDependenciesEnumerator extends JpsDependenciesEnumerator
@Override
JpsJavaDependenciesEnumerator recursively();
@Override
JpsJavaDependenciesEnumerator satisfying(Condition<JpsDependencyElement> condition);
JpsJavaDependenciesEnumerator includedIn(JpsJavaClasspathKind classpathKind);
JpsJavaDependenciesRootsEnumerator classes();
@@ -1,5 +1,7 @@
package org.jetbrains.jps.model.module;
import com.intellij.openapi.util.Condition;
import com.intellij.util.Consumer;
import org.jetbrains.jps.model.library.JpsLibrary;
import java.util.Set;
@@ -13,6 +15,10 @@ public interface JpsDependenciesEnumerator {
JpsDependenciesEnumerator withoutSdk();
JpsDependenciesEnumerator withoutModuleSourceEntries();
JpsDependenciesEnumerator recursively();
JpsDependenciesEnumerator satisfying(Condition<JpsDependencyElement> condition);
Set<JpsModule> getModules();
Set<JpsLibrary> getLibraries();
void processModules(Consumer<JpsModule> consumer);
}
@@ -1,5 +1,6 @@
package org.jetbrains.jps.model.module.impl;
import com.intellij.openapi.util.Condition;
import com.intellij.util.CollectConsumer;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
@@ -21,6 +22,7 @@ public abstract class JpsDependenciesEnumeratorBase<Self extends JpsDependencies
private boolean myWithoutModuleSourceEntries;
protected boolean myRecursively;
protected final Collection<JpsModule> myRootModules;
private Condition<JpsDependencyElement> myCondition;
protected JpsDependenciesEnumeratorBase(Collection<JpsModule> rootModules) {
myRootModules = rootModules;
@@ -50,6 +52,12 @@ public abstract class JpsDependenciesEnumeratorBase<Self extends JpsDependencies
return self();
}
@Override
public Self satisfying(Condition<JpsDependencyElement> condition) {
myCondition = condition;
return self();
}
@Override
public Self recursively() {
myRecursively = true;
@@ -65,6 +73,7 @@ public abstract class JpsDependenciesEnumeratorBase<Self extends JpsDependencies
return result;
}
@Override
public void processModules(final Consumer<JpsModule> consumer) {
processDependencies(new Processor<JpsDependencyElement>() {
@Override
@@ -97,13 +106,17 @@ public abstract class JpsDependenciesEnumeratorBase<Self extends JpsDependencies
if (!processed.add(module)) return true;
for (JpsDependencyElement element : module.getDependenciesList().getDependencies()) {
if (myCondition != null && !myCondition.value(element)) continue;
if (myWithoutSdk && element instanceof JpsSdkDependency
|| myWithoutLibraries && element instanceof JpsLibraryDependency
|| myWithoutModuleSourceEntries && element instanceof JpsModuleSourceDependency) continue;
if (myWithoutDepModules) {
if (!myRecursively && element instanceof JpsModuleDependency) continue;
if (element instanceof JpsModuleSourceDependency && !isEnumerationRootModule(module)) continue;
}
if (!shouldProcess(module, element)) {
continue;
}
@@ -25,7 +25,6 @@ import org.jetbrains.jps.incremental.messages.FileGeneratedEvent;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.incremental.storage.SourceToOutputMapping;
import org.jetbrains.jps.javac.OutputFileObject;
import org.jetbrains.jps.model.java.JpsJavaClasspathKind;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.model.module.JpsModule;
@@ -275,10 +274,7 @@ public class GroovyBuilder extends ModuleLevelBuilder {
// IMPORTANT! must be the first in classpath
cp.add(ClasspathBootstrap.getResourcePath(GroovyCompilerWrapper.class).getPath());
for (File file : context.getProjectPaths().getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.isTests()), false)) {
cp.add(FileUtil.toCanonicalPath(file.getPath()));
}
for (File file : context.getProjectPaths().getClasspathFiles(chunk, JpsJavaClasspathKind.runtime(chunk.isTests()), false)) {
for (File file : context.getProjectPaths().getCompilationClasspathFiles(chunk, chunk.isTests(), false, false)) {
cp.add(FileUtil.toCanonicalPath(file.getPath()));
}
return new ArrayList<String>(cp);