From 035ccb817cfa9aa4374c97a786fc575805f95587 Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 27 Sep 2012 10:42:10 +0400 Subject: [PATCH] external compiler: building process ruled by BuildTargets instead of modules --- .../options/CompileStepBeforeRun.java | 2 +- .../src/org/jetbrains/jps/ModuleChunk.java | 12 +- .../src/org/jetbrains/jps/ProjectChunks.java | 103 ----------- .../src/org/jetbrains/jps/ProjectPaths.java | 8 +- .../jetbrains/jps/builders/BuildTarget.java | 7 +- .../jps/builders/BuildTargetIndex.java | 14 +- .../jps/builders/BuildTargetType.java | 4 +- .../jps/builders/impl/BuildTargetChunk.java | 25 +++ .../builders/impl/BuildTargetIndexImpl.java | 109 ++++++++++- .../java/JavaModuleBuildTargetType.java | 2 +- .../jetbrains/jps/incremental/Builder.java | 2 +- .../jps/incremental/BuilderRegistry.java | 8 +- .../jps/incremental/BuilderService.java | 2 +- .../jps/incremental/CompileContext.java | 3 - .../jps/incremental/CompileContextImpl.java | 7 - .../jps/incremental/FSOperations.java | 26 ++- .../jps/incremental/IncProjectBuilder.java | 173 ++++++++++-------- .../jps/incremental/ModuleBuildTarget.java | 10 +- .../jps/incremental/ModuleLevelBuilder.java | 2 +- .../jps/incremental/ProjectLevelBuilder.java | 13 -- .../jps/incremental/TargetBuilder.java | 26 +++ .../artifacts/ArtifactBuildTarget.java | 55 +++++- .../artifacts/ArtifactBuildTargetType.java | 10 +- .../artifacts/ArtifactBuilderService.java | 4 +- .../artifacts/IncArtifactBuilder.java | 58 +++--- .../LayoutElementBuildersRegistry.java | 17 +- .../artifacts/impl/JpsArtifactUtil.java | 19 +- .../ArtifactInstructionsBuilderContext.java | 5 + ...rtifactInstructionsBuilderContextImpl.java | 16 ++ .../jps/incremental/java/JavaBuilder.java | 8 +- .../incremental/storage/BuildDataManager.java | 9 +- .../jps/builders/ModuleClasspathTest.groovy | 2 +- .../artifacts/ArtifactBuilderTest.java | 9 + .../openapi/util/text/StringUtil.java | 2 +- plugins/android/android.iml | 1 + .../android/util/AndroidCommonUtils.java | 2 + .../jps/android/AndroidBuilderService.java | 11 +- .../jps/android/AndroidDexBuilder.java | 11 +- .../jetbrains/jps/android/AndroidJpsUtil.java | 2 +- .../AndroidLibraryPackagingBuilder.java | 2 +- .../jps/android/AndroidPackagingBuilder.java | 11 +- .../AndroidSourceGeneratingBuilder.java | 2 +- .../builder/AndroidProjectBuildTarget.java | 112 ++++++++++++ plugins/android/src/META-INF/plugin.xml | 1 + .../android/compiler/AndroidCompileUtil.java | 6 +- .../AndroidTargetBuildScopeProvider.java | 44 +++++ plugins/devkit/devkit.iml | 1 + plugins/devkit/resources/META-INF/plugin.xml | 1 + .../build/PluginModuleBuildScopeProvider.java | 51 ++++++ .../jps/incremental/groovy/GroovyBuilder.java | 4 +- 50 files changed, 710 insertions(+), 324 deletions(-) delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/ProjectChunks.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetChunk.java delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/incremental/ProjectLevelBuilder.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/incremental/TargetBuilder.java create mode 100644 plugins/android/jps-plugin/src/org/jetbrains/jps/android/builder/AndroidProjectBuildTarget.java create mode 100644 plugins/android/src/org/jetbrains/android/compiler/AndroidTargetBuildScopeProvider.java create mode 100644 plugins/devkit/src/build/PluginModuleBuildScopeProvider.java diff --git a/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java b/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java index ddbc3709a11f..ecd62a5cc9e9 100644 --- a/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java +++ b/java/execution/impl/src/com/intellij/compiler/options/CompileStepBeforeRun.java @@ -170,7 +170,7 @@ public class CompileStepBeforeRun extends BeforeRunTaskProvider myModules; - private final boolean myTests; + private final boolean myContainsTests; private Set myTargets; - public ModuleChunk(Set targets, boolean tests) { - myTests = tests; + public ModuleChunk(Set targets) { + boolean containsTests = false; myTargets = targets; myModules = new LinkedHashSet(); for (ModuleBuildTarget target : targets) { myModules.add(target.getModule()); + containsTests |= target.isTests(); } + myContainsTests = containsTests; } public String getName() { @@ -42,8 +44,8 @@ public class ModuleChunk { return myModules; } - public boolean isTests() { - return myTests; + public boolean containsTests() { + return myContainsTests; } public Set getTargets() { diff --git a/jps/jps-builders/src/org/jetbrains/jps/ProjectChunks.java b/jps/jps-builders/src/org/jetbrains/jps/ProjectChunks.java deleted file mode 100644 index 93b74b936dfa..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/ProjectChunks.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.jetbrains.jps; - -import com.intellij.util.graph.CachingSemiGraph; -import com.intellij.util.graph.DFSTBuilder; -import com.intellij.util.graph.GraphGenerator; -import gnu.trove.TIntArrayList; -import gnu.trove.TIntProcedure; -import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; -import org.jetbrains.jps.incremental.ModuleBuildTarget; -import org.jetbrains.jps.model.JpsProject; -import org.jetbrains.jps.model.module.JpsModule; - -import java.util.*; - -/** - * @author nik - */ -public class ProjectChunks { - private List chunks; - private List myAllTargets; - private Map> myTargetDependencies; - private final JpsProject myProject; - - public ProjectChunks(JpsProject project) { - myProject = project; - } - - public List getChunkList() { - initializeChunks(); - return chunks; - } - - private void initializeChunks() { - if (chunks != null) { - return; - } - - myAllTargets = new ArrayList(myProject.getModules().size()*2); - for (JpsModule module : myProject.getModules()) { - myAllTargets.add(new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION)); - myAllTargets.add(new ModuleBuildTarget(module, JavaModuleBuildTargetType.TEST)); - } - myTargetDependencies = new HashMap>(); - for (ModuleBuildTarget target : myAllTargets) { - myTargetDependencies.put(target, target.computeDependencies()); - } - - GraphGenerator graph = GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph() { - @Override - public Collection getNodes() { - return myAllTargets; - } - - @Override - public Iterator getIn(ModuleBuildTarget n) { - return myTargetDependencies.get(n).iterator(); - } - })); - - final DFSTBuilder builder = new DFSTBuilder(graph); - final TIntArrayList sccs = builder.getSCCs(); - - chunks = new ArrayList(sccs.size()); - sccs.forEach(new TIntProcedure() { - int myTNumber = 0; - public boolean execute(int size) { - final Set chunkNodes = new LinkedHashSet(); - boolean test = false; - for (int j = 0; j < size; j++) { - final ModuleBuildTarget node = builder.getNodeByTNumber(myTNumber + j); - test = node.isTests();//production target cannot depend on test so this flag is the same for all nodes in the cycle - chunkNodes.add(node); - } - chunks.add(new ModuleChunk(chunkNodes, test)); - - myTNumber += size; - return true; - } - }); - } - - public Collection getAllTargets() { - initializeChunks(); - return myAllTargets; - } - - public Set getDependenciesRecursively(ModuleBuildTarget target) { - initializeChunks(); - LinkedHashSet result = new LinkedHashSet(); - for (ModuleBuildTarget dep : myTargetDependencies.get(target)) { - collectDependenciesRecursively(dep, result); - } - return result; - } - - private void collectDependenciesRecursively(ModuleBuildTarget target, LinkedHashSet result) { - if (result.add(target)) { - for (ModuleBuildTarget dep : myTargetDependencies.get(target)) { - collectDependenciesRecursively(dep, result); - } - } - } -} diff --git a/jps/jps-builders/src/org/jetbrains/jps/ProjectPaths.java b/jps/jps-builders/src/org/jetbrains/jps/ProjectPaths.java index 072632a3f8c2..1b7c47c71120 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/ProjectPaths.java +++ b/jps/jps-builders/src/org/jetbrains/jps/ProjectPaths.java @@ -41,11 +41,11 @@ public class ProjectPaths { } public Collection getPlatformCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) { - return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.isTests()), excludeMainModuleOutput, ClasspathPart.BEFORE_JDK, true); + return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.BEFORE_JDK, true); } public Collection getCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) { - return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.isTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK, true); + return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK, true); } private Collection getClasspathFiles(ModuleChunk chunk, @@ -94,7 +94,7 @@ public class ProjectPaths { * @return mapping "sourceRoot" -> "package prefix" Package prefix uses slashes instead of dots and ends with trailing slash */ public static Map getSourceRootsWithDependents(ModuleChunk chunk) { - final boolean includeTests = chunk.isTests(); + final boolean includeTests = chunk.containsTests(); final Map result = new LinkedHashMap(); processModulesRecursively(chunk, JpsJavaClasspathKind.compile(includeTests), new Consumer() { @Override @@ -122,7 +122,7 @@ public class ProjectPaths { } public static Collection getOutputPathsWithDependents(final ModuleChunk chunk) { - final boolean forTests = chunk.isTests(); + final boolean forTests = chunk.containsTests(); final Set sourcePaths = new LinkedHashSet(); processModulesRecursively(chunk, JpsJavaClasspathKind.compile(forTests), new Consumer() { @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java index 7f83261a0012..ec09f73b8e40 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java @@ -25,7 +25,7 @@ public abstract class BuildTarget { return myTargetType; } - public abstract Collection> computeDependencies(); + public abstract Collection> computeDependencies(); public void writeConfiguration(PrintWriter out, BuildRootIndex buildRootIndex) { } @@ -36,8 +36,11 @@ public abstract class BuildTarget { @Nullable public abstract BuildRootDescriptor findRootDescriptor(String rootId, BuildRootIndex rootIndex); + @NotNull + public abstract String getPresentableName(); + @Override public String toString() { - return myTargetType.getTypeId() + " '" + getId() + "'"; + return getPresentableName(); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetIndex.java b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetIndex.java index c79538982c65..bd7e692d38f3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetIndex.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetIndex.java @@ -1,8 +1,11 @@ package org.jetbrains.jps.builders; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.impl.BuildTargetChunk; import java.util.Collection; +import java.util.List; +import java.util.Set; /** * @author nik @@ -10,5 +13,14 @@ import java.util.Collection; public interface BuildTargetIndex { @NotNull > - Collection getAllTargets(@NotNull BuildTargetType type); + List getAllTargets(@NotNull BuildTargetType type); + + @NotNull + Collection> getDependencies(@NotNull BuildTarget target); + + List getSortedTargetChunks(); + + List> getAllTargets(); + + Set> getDependenciesRecursively(BuildTarget target); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetType.java b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetType.java index b3154f85dc6a..2b606f055037 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetType.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTargetType.java @@ -3,7 +3,7 @@ package org.jetbrains.jps.builders; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.JpsModel; -import java.util.Collection; +import java.util.List; /** * @author nik @@ -20,7 +20,7 @@ public abstract class BuildTargetType> { } @NotNull - public abstract Collection computeAllTargets(@NotNull JpsModel model); + public abstract List computeAllTargets(@NotNull JpsModel model); @NotNull public abstract BuildTargetLoader createLoader(@NotNull JpsModel model); diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetChunk.java b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetChunk.java new file mode 100644 index 000000000000..63774edd2fd3 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetChunk.java @@ -0,0 +1,25 @@ +package org.jetbrains.jps.builders.impl; + +import org.jetbrains.jps.builders.BuildTarget; + +import java.util.Set; + +/** + * @author nik + */ +public class BuildTargetChunk { + private Set> myTargets; + + public BuildTargetChunk(Set> targets) { + myTargets = targets; + } + + public Set> getTargets() { + return myTargets; + } + + @Override + public String toString() { + return myTargets.toString(); + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetIndexImpl.java b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetIndexImpl.java index 5863cc84e46a..b8197904bfc3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetIndexImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/impl/BuildTargetIndexImpl.java @@ -1,5 +1,11 @@ package org.jetbrains.jps.builders.impl; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.graph.CachingSemiGraph; +import com.intellij.util.graph.DFSTBuilder; +import com.intellij.util.graph.GraphGenerator; +import gnu.trove.TIntArrayList; +import gnu.trove.TIntProcedure; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildTarget; import org.jetbrains.jps.builders.BuildTargetIndex; @@ -7,27 +13,112 @@ import org.jetbrains.jps.builders.BuildTargetType; import org.jetbrains.jps.incremental.BuilderRegistry; import org.jetbrains.jps.model.JpsModel; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * @author nik */ public class BuildTargetIndexImpl implements BuildTargetIndex { - private Map, Collection>> myTargets; + private Map, List>> myTargets; + private Map, Collection>> myDependencies; + private List myTargetChunks; + private final List> myAllTargets; public BuildTargetIndexImpl(@NotNull JpsModel model) { - myTargets = new HashMap, Collection>>(); + myTargets = new HashMap, List>>(); + List>> targetsByType = new ArrayList>>(); for (BuildTargetType type : BuilderRegistry.getInstance().getTargetTypes()) { - myTargets.put(type, type.computeAllTargets(model)); + List> targets = type.computeAllTargets(model); + myTargets.put(type, targets); + targetsByType.add(targets); + } + myDependencies = new HashMap, Collection>>(); + myAllTargets = ContainerUtil.concat(targetsByType); + } + + @NotNull + @Override + public > List getAllTargets(@NotNull BuildTargetType type) { + //noinspection unchecked + return (List)myTargets.get(type); + } + + @Override + public List getSortedTargetChunks() { + initializeChunks(); + return myTargetChunks; + } + + + private synchronized void initializeChunks() { + if (myTargetChunks != null) { + return; + } + + for (BuildTarget target : getAllTargets()) { + myDependencies.put(target, target.computeDependencies()); + } + + GraphGenerator> + graph = GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph>() { + @Override + public Collection> getNodes() { + return myAllTargets; + } + + @Override + public Iterator> getIn(BuildTarget n) { + return myDependencies.get(n).iterator(); + } + })); + + final DFSTBuilder> builder = new DFSTBuilder>(graph); + final TIntArrayList sccs = builder.getSCCs(); + + myTargetChunks = new ArrayList(sccs.size()); + sccs.forEach(new TIntProcedure() { + int myTNumber = 0; + public boolean execute(int size) { + final Set> chunkNodes = new LinkedHashSet>(); + for (int j = 0; j < size; j++) { + final BuildTarget node = builder.getNodeByTNumber(myTNumber + j); + chunkNodes.add(node); + } + myTargetChunks.add(new BuildTargetChunk(chunkNodes)); + + myTNumber += size; + return true; + } + }); + } + + @Override + public List> getAllTargets() { + return myAllTargets; + } + + @Override + public Set> getDependenciesRecursively(BuildTarget target) { + initializeChunks(); + LinkedHashSet> result = new LinkedHashSet>(); + for (BuildTarget dep : myDependencies.get(target)) { + collectDependenciesRecursively(dep, result); + } + return result; + } + + private void collectDependenciesRecursively(BuildTarget target, LinkedHashSet> result) { + if (result.add(target)) { + for (BuildTarget dep : myDependencies.get(target)) { + collectDependenciesRecursively(dep, result); + } } } @NotNull @Override - public > Collection getAllTargets(@NotNull BuildTargetType type) { - //noinspection unchecked - return (Collection)myTargets.get(type); + public Collection> getDependencies(@NotNull BuildTarget target) { + initializeChunks(); + return myDependencies.get(target); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaModuleBuildTargetType.java b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaModuleBuildTargetType.java index 303275abff73..dd356ad882b7 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaModuleBuildTargetType.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/java/JavaModuleBuildTargetType.java @@ -27,7 +27,7 @@ public class JavaModuleBuildTargetType extends BuildTargetType computeAllTargets(@NotNull JpsModel model) { + public List computeAllTargets(@NotNull JpsModel model) { List modules = model.getProject().getModules(); List targets = new ArrayList(modules.size()); for (JpsModule module : modules) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java index e564e19814a3..3de83437a9fe 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java @@ -2,7 +2,7 @@ package org.jetbrains.jps.incremental; /** * @see ModuleLevelBuilder - * @see ProjectLevelBuilder + * @see TargetBuilder * * @author nik */ diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java index 33222309f5f2..28407dfeef68 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderRegistry.java @@ -18,7 +18,7 @@ public class BuilderRegistry { static final BuilderRegistry ourInstance = new BuilderRegistry(); } private final Map> myModuleLevelBuilders = new HashMap>(); - private final List myProjectLevelBuilders = new ArrayList(); + private final List> myTargetBuilders = new ArrayList>(); private final Map> myTargetTypes = new LinkedHashMap>(); public static BuilderRegistry getInstance() { @@ -31,7 +31,7 @@ public class BuilderRegistry { } for (BuilderService service : JpsServiceManager.getInstance().getExtensions(BuilderService.class)) { - myProjectLevelBuilders.addAll(service.createProjectLevelBuilders()); + myTargetBuilders.addAll(service.createBuilders()); final List moduleLevelBuilders = service.createModuleLevelBuilders(); for (ModuleLevelBuilder builder : moduleLevelBuilders) { myModuleLevelBuilders.get(builder.getCategory()).add(builder); @@ -79,7 +79,7 @@ public class BuilderRegistry { return ContainerUtil.concat(myModuleLevelBuilders.values()); } - public List getProjectLevelBuilders() { - return myProjectLevelBuilders; + public List> getTargetBuilders() { + return myTargetBuilders; } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderService.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderService.java index cdf7138ec37e..d2d9455104bc 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderService.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuilderService.java @@ -20,7 +20,7 @@ public abstract class BuilderService { } @NotNull - public List createProjectLevelBuilders() { + public List> createBuilders() { return Collections.emptyList(); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java index f99b8f3d74db..3dd40df94d5b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContext.java @@ -4,7 +4,6 @@ import com.intellij.openapi.util.UserDataHolder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; -import org.jetbrains.jps.ProjectChunks; import org.jetbrains.jps.ProjectPaths; import org.jetbrains.jps.api.CanceledStatus; import org.jetbrains.jps.cmdline.ProjectDescriptor; @@ -47,8 +46,6 @@ public interface CompileContext extends UserDataHolder, MessageHandler { void setDone(float done); - ProjectChunks getChunks(); - long getCompilationStartStamp(); void markNonIncremental(ModuleBuildTarget target); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContextImpl.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContextImpl.java index 23acf15f3167..fc9ef933e0eb 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContextImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContextImpl.java @@ -29,7 +29,6 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon private final CompileScope myScope; private final boolean myIsMake; private final boolean myIsProjectRebuild; - private final ProjectChunks myChunks; private final MessageHandler myDelegateMessageHandler; private final Set myNonIncrementalModules = new HashSet(); @@ -55,7 +54,6 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon myScope = scope; myIsProjectRebuild = isProjectRebuild; myIsMake = !isProjectRebuild && isMake; - myChunks = new ProjectChunks(pd.jpsProject); myDelegateMessageHandler = delegateMessageHandler; myProjectPaths = new ProjectPaths(pd.jpsProject); } @@ -65,11 +63,6 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon return myCompilationStartStamp; } - @Override - public ProjectChunks getChunks() { - return myChunks; - } - @Override public ProjectPaths getProjectPaths() { return myProjectPaths; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java index 2c7dda13d059..533c8e8eb698 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java @@ -7,7 +7,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.JpsPathUtil; import org.jetbrains.jps.ModuleChunk; -import org.jetbrains.jps.ProjectChunks; +import org.jetbrains.jps.builders.BuildTarget; +import org.jetbrains.jps.builders.impl.BuildTargetChunk; import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.fs.BuildFSState; import org.jetbrains.jps.incremental.fs.RootDescriptor; @@ -65,21 +66,26 @@ public class FSOperations { final Set dirtyTargets = new HashSet(targets); // now mark all modules that depend on dirty modules - final JpsJavaClasspathKind classpathKind = JpsJavaClasspathKind.compile(chunk.isTests()); - final ProjectChunks chunks = context.getChunks(); + final JpsJavaClasspathKind classpathKind = JpsJavaClasspathKind.compile(chunk.containsTests()); boolean found = false; - for (ModuleChunk moduleChunk : chunks.getChunkList()) { + for (BuildTargetChunk targetChunk : context.getProjectDescriptor().getBuildTargetIndex().getSortedTargetChunks()) { if (!found) { - if (moduleChunk.equals(chunk)) { + if (targetChunk.getTargets().equals(chunk.getTargets())) { found = true; } } else { - for (final JpsModule module : moduleChunk.getModules()) { - final Set deps = getDependentModulesRecursively(module, classpathKind); - if (Utils.intersects(deps, modules)) { - dirtyTargets.addAll(moduleChunk.getTargets()); - break; + for (final BuildTarget target : targetChunk.getTargets()) { + if (target instanceof ModuleBuildTarget) { + final Set deps = getDependentModulesRecursively(((ModuleBuildTarget)target).getModule(), classpathKind); + if (Utils.intersects(deps, modules)) { + for (BuildTarget buildTarget : targetChunk.getTargets()) { + if (buildTarget instanceof ModuleBuildTarget) { + dirtyTargets.add((ModuleBuildTarget)buildTarget); + } + } + break; + } } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java index f8e4c6cad29a..0dfe53e82aca 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -16,14 +16,15 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.JpsPathUtil; import org.jetbrains.jps.ModuleChunk; -import org.jetbrains.jps.ProjectChunks; import org.jetbrains.jps.ProjectPaths; import org.jetbrains.jps.api.CanceledStatus; import org.jetbrains.jps.api.GlobalOptions; import org.jetbrains.jps.api.RequestFuture; import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.BuildTarget; +import org.jetbrains.jps.builders.BuildTargetIndex; import org.jetbrains.jps.builders.BuildTargetType; +import org.jetbrains.jps.builders.impl.BuildTargetChunk; import org.jetbrains.jps.builders.java.JavaBuilderUtil; import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; import org.jetbrains.jps.builders.java.dependencyView.Callbacks; @@ -89,8 +90,8 @@ public class IncProjectBuilder { } }; - private volatile float myModulesProcessed = 0.0f; - private final float myTotalModulesWork; + private volatile float myTargetsProcessed = 0.0f; + private final float myTotalTargetsWork; private final int myTotalModuleLevelBuilderCount; private final List myAsyncTasks = new ArrayList(); @@ -101,7 +102,7 @@ public class IncProjectBuilder { myBuilderParams = builderParams; myCancelStatus = cs; myConstantSearch = constantSearch; - myTotalModulesWork = pd.jpsProject.getModules().size() * 2; /* multiply by 2 to reflect production and test sources */ + myTotalTargetsWork = pd.getBuildTargetIndex().getAllTargets().size(); myTotalModuleLevelBuilderCount = builderRegistry.getModuleLevelBuilderCount(); } @@ -202,9 +203,9 @@ public class IncProjectBuilder { } private float updateFractionBuilderFinished(final float delta) { - myModulesProcessed += delta; - float processed = myModulesProcessed; - return processed / myTotalModulesWork; + myTargetsProcessed += delta; + float processed = myTargetsProcessed; + return processed / myTotalTargetsWork; } private void runBuild(CompileContextImpl context, boolean forceCleanCaches) throws ProjectBuildException { @@ -212,7 +213,7 @@ public class IncProjectBuilder { LOG.info("Building project; isRebuild:" + context.isProjectRebuild() + "; isMake:" + context.isMake() + " parallel compilation:" + BuildRunner.PARALLEL_BUILD_ENABLED); - for (ProjectLevelBuilder builder : myBuilderRegistry.getProjectLevelBuilders()) { + for (TargetBuilder builder : myBuilderRegistry.getTargetBuilders()) { builder.buildStarted(context); } for (ModuleLevelBuilder builder : myBuilderRegistry.getModuleLevelBuilders()) { @@ -228,10 +229,7 @@ public class IncProjectBuilder { runTasks(context, myBuilderRegistry.getBeforeTasks()); context.processMessage(new ProgressMessage("Checking sources")); - buildChunks(context, context.getChunks()); - - context.processMessage(new ProgressMessage("Building project")); - runProjectLevelBuilders(context); + buildChunks(context); context.processMessage(new ProgressMessage("Running 'after' tasks")); runTasks(context, myBuilderRegistry.getAfterTasks()); @@ -253,7 +251,7 @@ public class IncProjectBuilder { //} } finally { - for (ProjectLevelBuilder builder : myBuilderRegistry.getProjectLevelBuilders()) { + for (TargetBuilder builder : myBuilderRegistry.getTargetBuilders()) { builder.buildFinished(context); } for (ModuleLevelBuilder builder : myBuilderRegistry.getModuleLevelBuilders()) { @@ -281,7 +279,7 @@ public class IncProjectBuilder { clearOutputs(context); } else { - for (ModuleBuildTarget target : context.getChunks().getAllTargets()) { + for (BuildTarget target : context.getProjectDescriptor().getBuildTargetIndex().getAllTargets()) { clearOutputFiles(context, target); } } @@ -325,18 +323,20 @@ public class IncProjectBuilder { final ProjectPaths paths = context.getProjectPaths(); - for (ModuleBuildTarget target : context.getChunks().getAllTargets()) { - final File out = paths.getModuleOutputDir(target.getModule(), target.isTests()); - if (out != null) { - rootsToDelete.putValue(out, target); - } + for (JavaModuleBuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) { + for (ModuleBuildTarget target : context.getProjectDescriptor().getBuildTargetIndex().getAllTargets(type)) { + final File out = paths.getModuleOutputDir(target.getModule(), target.isTests()); + if (out != null) { + rootsToDelete.putValue(out, target); + } - final ProcessorConfigProfile profile = context.getAnnotationProcessingProfile(target.getModule()); - if (profile.isEnabled()) { - File annotationOut = - paths.getAnnotationProcessorGeneratedSourcesOutputDir(target.getModule(), target.isTests(), profile.getGeneratedSourcesDirectoryName()); - if (annotationOut != null) { - annotationOutputs.add(annotationOut); + final ProcessorConfigProfile profile = context.getAnnotationProcessingProfile(target.getModule()); + if (profile.isEnabled()) { + File annotationOut = + paths.getAnnotationProcessorGeneratedSourcesOutputDir(target.getModule(), target.isTests(), profile.getGeneratedSourcesDirectoryName()); + if (annotationOut != null) { + annotationOutputs.add(annotationOut); + } } } } @@ -390,36 +390,28 @@ public class IncProjectBuilder { ); } - private static void appendRootInfo(Map> rootsToDelete, File out, ModuleBuildTarget target) { - Set infos = rootsToDelete.get(out); - if (infos == null) { - infos = new HashSet(); - rootsToDelete.put(out, infos); - } - infos.add(target); - } - private static void runTasks(CompileContext context, final List tasks) throws ProjectBuildException { for (BuildTask task : tasks) { task.build(context); } } - private void buildChunks(final CompileContextImpl context, ProjectChunks chunks) throws ProjectBuildException { + private void buildChunks(final CompileContextImpl context) throws ProjectBuildException { final CompileScope scope = context.getScope(); final ProjectDescriptor pd = context.getProjectDescriptor(); + BuildTargetIndex targetIndex = pd.getBuildTargetIndex(); try { if (BuildRunner.PARALLEL_BUILD_ENABLED) { - final List chunkGroups = buildChunkGroups(chunks); + final List chunkGroups = buildChunkGroups(targetIndex); for (ChunkGroup group : chunkGroups) { - final List groupChunks = group.getChunks(); + final List groupChunks = group.getChunks(); final int chunkCount = groupChunks.size(); if (chunkCount == 0) { continue; } try { if (chunkCount == 1) { - _buildChunk(createContextWrapper(context), scope, groupChunks.iterator().next()); + buildChunkIfAffected(createContextWrapper(context), scope, groupChunks.iterator().next()); } else { final CountDownLatch latch = new CountDownLatch(chunkCount); @@ -427,19 +419,19 @@ public class IncProjectBuilder { if (LOG.isDebugEnabled()) { final StringBuilder logBuilder = new StringBuilder("Building chunks in parallel: "); - for (ModuleChunk chunk : groupChunks) { - logBuilder.append(chunk.getName()).append("; "); + for (BuildTargetChunk chunk : groupChunks) { + logBuilder.append(chunk.toString()).append("; "); } LOG.debug(logBuilder.toString()); } - for (final ModuleChunk chunk : groupChunks) { + for (final BuildTargetChunk chunk : groupChunks) { final CompileContext chunkLocalContext = createContextWrapper(context); myParallelBuildExecutor.execute(new Runnable() { @Override public void run() { try { - _buildChunk(chunkLocalContext, scope, chunk); + buildChunkIfAffected(chunkLocalContext, scope, chunk); } catch (Throwable e) { synchronized (exRef) { @@ -482,9 +474,9 @@ public class IncProjectBuilder { } else { // non-parallel build - for (ModuleChunk chunk : chunks.getChunkList()) { + for (BuildTargetChunk chunk : targetIndex.getSortedTargetChunks()) { try { - _buildChunk(context, scope, chunk); + buildChunkIfAffected(context, scope, chunk); } finally { pd.dataManager.closeSourceToOutputStorages(Collections.singleton(chunk)); @@ -498,18 +490,18 @@ public class IncProjectBuilder { } } - private void _buildChunk(CompileContext context, CompileScope scope, ModuleChunk chunk) throws ProjectBuildException { + private void buildChunkIfAffected(CompileContext context, CompileScope scope, BuildTargetChunk chunk) throws ProjectBuildException { if (isAffected(scope, chunk)) { - buildChunk(context, chunk); + buildTargetsChunk(context, chunk); } else { - final float fraction = updateFractionBuilderFinished(chunk.getModules().size()); + final float fraction = updateFractionBuilderFinished(chunk.getTargets().size()); context.setDone(fraction); } } - private static boolean isAffected(CompileScope scope, ModuleChunk chunk) { - for (ModuleBuildTarget target : chunk.getTargets()) { + private static boolean isAffected(CompileScope scope, BuildTargetChunk chunk) { + for (BuildTarget target : chunk.getTargets()) { if (scope.isAffected(target)) { return true; } @@ -517,7 +509,47 @@ public class IncProjectBuilder { return false; } - private void buildChunk(CompileContext context, final ModuleChunk chunk) throws ProjectBuildException { + private void buildTargetsChunk(CompileContext context, final BuildTargetChunk chunk) throws ProjectBuildException { + Set> targets = chunk.getTargets(); + if (targets.size() > 1) { + Set moduleTargets = new HashSet(); + for (BuildTarget target : targets) { + if (target instanceof ModuleBuildTarget) { + moduleTargets.add((ModuleBuildTarget)target); + } + else { + context.processMessage(new CompilerMessage(BUILD_NAME, BuildMessage.Kind.ERROR, "Cannot build " + target.getPresentableName() + " because it is included into a circular dependency")); + return; + } + } + buildModuleChunk(context, new ModuleChunk(moduleTargets)); + } + BuildTarget target = targets.iterator().next(); + if (target instanceof ModuleBuildTarget) { + ModuleBuildTarget moduleBuildTarget = (ModuleBuildTarget)target; + buildModuleChunk(context, new ModuleChunk(Collections.singleton(moduleBuildTarget))); + } + else { + buildTarget(target, context); + } + } + + private static void buildTarget(BuildTarget target, CompileContext context) throws ProjectBuildException { + for (TargetBuilder builder : BuilderRegistry.getInstance().getTargetBuilders()) { + buildTarget(target, context, builder); + } + } + + private static > void buildTarget(B target, CompileContext context, TargetBuilder builder) throws ProjectBuildException { + if (builder.getTargetTypes().contains(target.getTargetType())) { + //noinspection unchecked + ((TargetBuilder)builder).build(target, context); + context.checkCanceled(); + } + } + + private void buildModuleChunk(CompileContext context, final ModuleChunk chunk) throws ProjectBuildException { + boolean doneSomething = false; try { Utils.ERRORS_DETECTED_KEY.set(context, Boolean.FALSE); @@ -541,7 +573,7 @@ public class IncProjectBuilder { try { for (BuilderCategory category : BuilderCategory.values()) { for (ModuleLevelBuilder builder : myBuilderRegistry.getBuilders(category)) { - builder.cleanupResources(context, chunk); + builder.cleanupChunkResources(context); } } } @@ -748,9 +780,9 @@ public class IncProjectBuilder { if (buildResult == ModuleLevelBuilder.ExitCode.ADDITIONAL_PASS_REQUIRED) { if (!nextPassRequired) { // recalculate basis - myModulesProcessed -= (buildersPassed * modulesInChunk) / stageCount; + myTargetsProcessed -= (buildersPassed * modulesInChunk) / stageCount; stageCount += myTotalModuleLevelBuilderCount; - myModulesProcessed += (buildersPassed * modulesInChunk) / stageCount; + myTargetsProcessed += (buildersPassed * modulesInChunk) / stageCount; } nextPassRequired = true; } @@ -763,7 +795,7 @@ public class IncProjectBuilder { // forcibly mark all files in the chunk dirty FSOperations.markDirty(context, chunk); // reverting to the beginning - myModulesProcessed -= (buildersPassed * modulesInChunk) / stageCount; + myTargetsProcessed -= (buildersPassed * modulesInChunk) / stageCount; stageCount = myTotalModuleLevelBuilderCount; buildersPassed = 0; nextPassRequired = true; @@ -790,13 +822,6 @@ public class IncProjectBuilder { return doneSomething; } - private void runProjectLevelBuilders(CompileContext context) throws ProjectBuildException { - for (ProjectLevelBuilder builder : myBuilderRegistry.getProjectLevelBuilders()) { - builder.build(context); - context.checkCanceled(); - } - } - private static void syncOutputFiles(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException { final BuildDataManager dataManager = context.getProjectDescriptor().dataManager; try { @@ -854,19 +879,19 @@ public class IncProjectBuilder { } } - private static List buildChunkGroups(ProjectChunks chunks) { - final List allChunks = chunks.getChunkList(); + private static List buildChunkGroups(BuildTargetIndex index) { + final List allChunks = index.getSortedTargetChunks(); // building aux dependencies map - final Map> depsMap = new HashMap>(); - for (ModuleBuildTarget target : chunks.getAllTargets()) { - depsMap.put(target, chunks.getDependenciesRecursively(target)); + final Map, Set>> depsMap = new HashMap, Set>>(); + for (BuildTarget target : index.getAllTargets()) { + depsMap.put(target, index.getDependenciesRecursively(target)); } final List groups = new ArrayList(); ChunkGroup currentGroup = new ChunkGroup(); groups.add(currentGroup); - for (ModuleChunk chunk : allChunks) { + for (BuildTargetChunk chunk : allChunks) { if (dependsOnGroup(chunk, currentGroup, depsMap)) { currentGroup = new ChunkGroup(); groups.add(currentGroup); @@ -877,10 +902,10 @@ public class IncProjectBuilder { } - public static boolean dependsOnGroup(ModuleChunk chunk, ChunkGroup group, Map> depsMap) { - for (ModuleChunk groupChunk : group.getChunks()) { - final Set groupChunkTargets = groupChunk.getTargets(); - for (ModuleBuildTarget target : chunk.getTargets()) { + private static boolean dependsOnGroup(BuildTargetChunk chunk, ChunkGroup group, Map, Set>> depsMap) { + for (BuildTargetChunk groupChunk : group.getChunks()) { + final Set> groupChunkTargets = groupChunk.getTargets(); + for (BuildTarget target : chunk.getTargets()) { if (ContainerUtil.intersects(depsMap.get(target), groupChunkTargets)) { return true; } @@ -999,13 +1024,13 @@ public class IncProjectBuilder { } private static class ChunkGroup { - private final List myChunks = new ArrayList(); + private final List myChunks = new ArrayList(); - public void addChunk(ModuleChunk chunk) { + public void addChunk(BuildTargetChunk chunk) { myChunks.add(chunk); } - public List getChunks() { + public List getChunks() { return myChunks; } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java index a78c7728baae..474b516f04d4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java @@ -55,12 +55,12 @@ public class ModuleBuildTarget extends BuildTarget { } @Override - public Collection computeDependencies() { + public Collection> computeDependencies() { JpsJavaDependenciesEnumerator enumerator = JpsJavaExtensionService.dependencies(myModule).compileOnly(); if (!isTests()) { enumerator.productionOnly(); } - final List dependencies = new ArrayList(); + final List> dependencies = new ArrayList>(); enumerator.processModules(new Consumer() { @Override public void consume(JpsModule module) { @@ -93,6 +93,12 @@ public class ModuleBuildTarget extends BuildTarget { return ContainerUtil.getFirstItem(descriptors); } + @NotNull + @Override + public String getPresentableName() { + return "Module '" + myModuleName + "' " + (myTargetType.isTests() ? "production" : "tests"); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java index 4e0228363333..cacabc1c737a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleLevelBuilder.java @@ -31,6 +31,6 @@ public abstract class ModuleLevelBuilder extends Builder { return myCategory; } - public void cleanupResources(CompileContext context, ModuleChunk chunk) { + public void cleanupChunkResources(CompileContext context) { } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ProjectLevelBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ProjectLevelBuilder.java deleted file mode 100644 index a3e95a39c409..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ProjectLevelBuilder.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.jetbrains.jps.incremental; - -/** - * Use {@link BuilderService} to register implementations of this class - * @author nik - */ -public abstract class ProjectLevelBuilder extends Builder { - protected ProjectLevelBuilder() { - } - - public abstract void build(CompileContext context) throws ProjectBuildException; - -} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/TargetBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/TargetBuilder.java new file mode 100644 index 000000000000..6a4d7474a1d7 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/TargetBuilder.java @@ -0,0 +1,26 @@ +package org.jetbrains.jps.incremental; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.BuildTarget; +import org.jetbrains.jps.builders.BuildTargetType; + +import java.util.Collection; + +/** + * Use {@link BuilderService} to register implementations of this class + * @author nik + */ +public abstract class TargetBuilder> extends Builder { + private final Collection> myTargetTypes; + + protected TargetBuilder(Collection> targetTypes) { + myTargetTypes = targetTypes; + } + + public Collection> getTargetTypes() { + return myTargetTypes; + } + + public abstract void build(@NotNull B target, @NotNull CompileContext context) throws ProjectBuildException; + +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTarget.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTarget.java index c74d4f8a5996..f3e6539b1b68 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTarget.java @@ -1,21 +1,29 @@ package org.jetbrains.jps.incremental.artifacts; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.ProjectPaths; import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.BuildTarget; +import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; +import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.ModuleRootsIndex; import org.jetbrains.jps.incremental.artifacts.builders.LayoutElementBuildersRegistry; +import org.jetbrains.jps.incremental.artifacts.impl.JpsArtifactUtil; import org.jetbrains.jps.incremental.artifacts.instructions.*; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.artifact.JpsArtifact; -import org.jetbrains.jps.model.artifact.elements.JpsCompositePackagingElement; +import org.jetbrains.jps.model.artifact.elements.JpsArtifactOutputPackagingElement; +import org.jetbrains.jps.model.artifact.elements.JpsPackagingElement; +import org.jetbrains.jps.model.java.JpsProductionModuleOutputPackagingElement; +import org.jetbrains.jps.model.java.JpsTestModuleOutputPackagingElement; +import org.jetbrains.jps.model.module.JpsModule; import java.io.PrintWriter; import java.util.Collection; -import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; /** @@ -39,8 +47,36 @@ public class ArtifactBuildTarget extends BuildTarget { } @Override - public Collection> computeDependencies() { - return Collections.emptyList(); + public Collection> computeDependencies() { + final LinkedHashSet> dependencies = new LinkedHashSet>(); + JpsArtifactUtil.processPackagingElements(myArtifact.getRootElement(), new Processor() { + @Override + public boolean process(JpsPackagingElement element) { + if (element instanceof JpsArtifactOutputPackagingElement) { + JpsArtifact included = ((JpsArtifactOutputPackagingElement)element).getArtifactReference().resolve(); + if (included != null && !included.equals(myArtifact)) { + if (!StringUtil.isEmpty(included.getOutputPath())) { + dependencies.add(new ArtifactBuildTarget(included)); + return false; + } + } + } + else if (element instanceof JpsProductionModuleOutputPackagingElement) { + JpsModule module = ((JpsProductionModuleOutputPackagingElement)element).getModuleReference().resolve(); + if (module != null) { + dependencies.add(new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION)); + } + } + else if (element instanceof JpsTestModuleOutputPackagingElement) { + JpsModule module = ((JpsTestModuleOutputPackagingElement)element).getModuleReference().resolve(); + if (module != null) { + dependencies.add(new ModuleBuildTarget(module, JavaModuleBuildTargetType.TEST)); + } + } + return true; + } + }); + return dependencies; } @Override @@ -68,11 +104,10 @@ public class ArtifactBuildTarget extends BuildTarget { @Override public List computeRootDescriptors(JpsModel model, ModuleRootsIndex index) { ArtifactInstructionsBuilderImpl builder = new ArtifactInstructionsBuilderImpl(index, this); - final JpsCompositePackagingElement rootElement = myArtifact.getRootElement(); ArtifactInstructionsBuilderContext context = new ArtifactInstructionsBuilderContextImpl(model, new ProjectPaths(model.getProject())); - String outputPath = StringUtil.notNullize(myArtifact.getOutputPath());//todo[nik] implement simplified instructions generation which only collect roots + String outputPath = StringUtil.notNullize(myArtifact.getOutputPath()); final CopyToDirectoryInstructionCreator instructionCreator = new CopyToDirectoryInstructionCreator(builder, outputPath); - LayoutElementBuildersRegistry.getInstance().generateInstructions(rootElement, instructionCreator, context); + LayoutElementBuildersRegistry.getInstance().generateInstructions(myArtifact, instructionCreator, context); return builder.getDescriptors(); } @@ -81,4 +116,10 @@ public class ArtifactBuildTarget extends BuildTarget { BuildRootIndex rootIndex) { return rootIndex.getTargetRoots(this, null).get(Integer.valueOf(rootId)); } + + @NotNull + @Override + public String getPresentableName() { + return "Artifact '" + myArtifact.getName() + "'"; + } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTargetType.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTargetType.java index 5df9c5531494..fa40bb859461 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTargetType.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuildTargetType.java @@ -1,12 +1,12 @@ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.BuildTargetLoader; import org.jetbrains.jps.builders.BuildTargetType; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.artifact.JpsArtifact; -import org.jetbrains.jps.model.artifact.JpsArtifactService; import java.util.*; @@ -22,11 +22,13 @@ public class ArtifactBuildTargetType extends BuildTargetType computeAllTargets(@NotNull JpsModel model) { + public List computeAllTargets(@NotNull JpsModel model) { Collection artifacts = JpsBuilderArtifactService.getInstance().getArtifacts(model, true); List targets = new ArrayList(artifacts.size()); for (JpsArtifact artifact : artifacts) { - targets.add(new ArtifactBuildTarget(artifact)); + if (!StringUtil.isEmpty(artifact.getOutputPath())) { + targets.add(new ArtifactBuildTarget(artifact)); + } } return targets; } @@ -42,7 +44,7 @@ public class ArtifactBuildTargetType extends BuildTargetType(); - for (JpsArtifact artifact : JpsArtifactService.getInstance().getArtifacts(model.getProject())) { + for (JpsArtifact artifact : JpsBuilderArtifactService.getInstance().getArtifacts(model, true)) { myArtifacts.put(artifact.getName(), artifact); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderService.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderService.java index 80809063b4f5..c609cb7e67db 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderService.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderService.java @@ -3,7 +3,7 @@ package org.jetbrains.jps.incremental.artifacts; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildTargetType; import org.jetbrains.jps.incremental.BuilderService; -import org.jetbrains.jps.incremental.ProjectLevelBuilder; +import org.jetbrains.jps.incremental.TargetBuilder; import java.util.Collections; import java.util.List; @@ -19,7 +19,7 @@ public class ArtifactBuilderService extends BuilderService { @NotNull @Override - public List createProjectLevelBuilders() { + public List> createBuilders() { return Collections.singletonList(new IncArtifactBuilder()); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java index 8c242ab0aa82..04237605703f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/IncArtifactBuilder.java @@ -6,6 +6,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.MultiMap; import gnu.trove.THashSet; import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.cmdline.ProjectDescriptor; @@ -18,7 +19,6 @@ import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.incremental.messages.ProgressMessage; import org.jetbrains.jps.incremental.storage.SourceToOutputMapping; -import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.artifact.JpsArtifact; import java.io.File; @@ -28,48 +28,31 @@ import java.util.*; /** * @author nik */ -public class IncArtifactBuilder extends ProjectLevelBuilder { +public class IncArtifactBuilder extends TargetBuilder { public static final String BUILDER_NAME = "artifacts"; public IncArtifactBuilder() { - super(); + super(Collections.singletonList(ArtifactBuildTargetType.INSTANCE)); } @Override - public void build(CompileContext context) throws ProjectBuildException { - Set affected = new HashSet(); - JpsBuilderArtifactService artifactService = JpsBuilderArtifactService.getInstance(); - JpsModel model = context.getProjectDescriptor().jpsModel; - for (JpsArtifact artifact : artifactService.getArtifacts(model, false)) { - if (context.getScope().isAffected(new ArtifactBuildTarget(artifact))) { - affected.add(artifact); - } + public void build(@NotNull ArtifactBuildTarget target, @NotNull CompileContext context) throws ProjectBuildException { + JpsArtifact artifact = target.getArtifact(); + if (StringUtil.isEmpty(artifact.getOutputPath())) { + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot build '" + artifact.getName() + "' artifact: output path is not specified")); + return; } - affected.addAll(artifactService.getSyntheticArtifacts(model)); - final Set toBuild = ArtifactSorter.addIncludedArtifacts(affected); - - final ArtifactSorter sorter = new ArtifactSorter(model); - final Map selfIncludingNameMap = sorter.getArtifactToSelfIncludingNameMap(); - for (JpsArtifact artifact : sorter.getArtifactsSortedByInclusion()) { - context.checkCanceled(); - if (toBuild.contains(artifact)) { - final JpsArtifact selfIncluding = selfIncludingNameMap.get(artifact); - if (selfIncluding != null) { - String name = selfIncluding.equals(artifact) ? "it" : "'" + selfIncluding.getName() + "' artifact"; - context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot build '" + artifact.getName() + "' artifact: " + name + " includes itself in the output layout")); - break; - } - if (StringUtil.isEmpty(artifact.getOutputPath())) { - context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot build '" + artifact.getName() + "' artifact: output path is not specified")); - break; - } - buildArtifact(new ArtifactBuildTarget(artifact), context); - } - } - } - - private static void buildArtifact(ArtifactBuildTarget target, final CompileContext context) throws ProjectBuildException { final ProjectDescriptor pd = context.getProjectDescriptor(); + final ArtifactSorter sorter = new ArtifactSorter(pd.jpsModel); + final Map selfIncludingNameMap = sorter.getArtifactToSelfIncludingNameMap(); + final JpsArtifact selfIncluding = selfIncludingNameMap.get(artifact); + if (selfIncluding != null) { + String name = selfIncluding.equals(artifact) ? "it" : "'" + selfIncluding.getName() + "' artifact"; + context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot build '" + artifact.getName() + "' artifact: " + name + " includes itself in the output layout")); + return; + } + + try { final ArtifactSourceFilesState state = pd.dataManager.getArtifactsBuildData().getOrCreateState(target, pd); state.ensureFsStateInitialized(pd.dataManager, context); @@ -80,7 +63,7 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { return; } - context.processMessage(new ProgressMessage("Building artifact '" + target.getArtifact().getName() + "'...")); + context.processMessage(new ProgressMessage("Building artifact '" + artifact.getName() + "'...")); final SourceToOutputMapping srcOutMapping = pd.dataManager.getSourceToOutputMap(target); final ArtifactOutputToSourceMapping outSrcMapping = state.getOrCreateOutSrcMapping(); @@ -147,7 +130,8 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { srcOutMapping, outSrcMapping); } else if (outSrcMapping.getState(destination.getOutputFilePath()) == null) { - outSrcMapping.update(destination.getOutputFilePath(), Collections.emptyList()); + outSrcMapping + .update(destination.getOutputFilePath(), Collections.emptyList()); changedJars.add(((JarDestinationInfo)destination).getJarInfo()); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/builders/LayoutElementBuildersRegistry.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/builders/LayoutElementBuildersRegistry.java index bc3b2d80a5cf..e667634f4702 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/builders/LayoutElementBuildersRegistry.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/builders/LayoutElementBuildersRegistry.java @@ -7,6 +7,7 @@ import com.intellij.util.containers.ClassMap; import org.jetbrains.jps.JpsPathUtil; import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactCompilerInstructionCreator; import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactInstructionsBuilderContext; +import org.jetbrains.jps.incremental.artifacts.instructions.CopyToDirectoryInstructionCreator; import org.jetbrains.jps.model.artifact.JpsArtifact; import org.jetbrains.jps.model.artifact.elements.*; import org.jetbrains.jps.model.java.JpsProductionModuleOutputPackagingElement; @@ -47,7 +48,12 @@ public class LayoutElementBuildersRegistry { } } - public void generateInstructions(JpsPackagingElement layoutElement, ArtifactCompilerInstructionCreator instructionCreator, + public void generateInstructions(JpsArtifact artifact, CopyToDirectoryInstructionCreator creator, ArtifactInstructionsBuilderContext context) { + context.enterArtifact(artifact); + generateInstructions(artifact.getRootElement(), creator, context); + } + + private void generateInstructions(JpsPackagingElement layoutElement, ArtifactCompilerInstructionCreator instructionCreator, ArtifactInstructionsBuilderContext builderContext) { final LayoutElementBuilderService builder = myBuilders.get(layoutElement.getClass()); if (builder != null) { @@ -230,7 +236,14 @@ public class LayoutElementBuildersRegistry { final String outputPath = artifact.getOutputPath(); if (StringUtil.isEmpty(outputPath)) { - generateSubstitutionInstructions(element, instructionCreator, builderContext); + try { + if (builderContext.enterArtifact(artifact)) { + generateSubstitutionInstructions(element, instructionCreator, builderContext); + } + } + finally { + builderContext.leaveArtifact(artifact); + } return; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JpsArtifactUtil.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JpsArtifactUtil.java index c7a673e3b330..ab6f4ef08a88 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JpsArtifactUtil.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JpsArtifactUtil.java @@ -1,28 +1,41 @@ package org.jetbrains.jps.incremental.artifacts.impl; import com.intellij.util.Processor; +import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.artifact.elements.JpsComplexPackagingElement; import org.jetbrains.jps.model.artifact.elements.JpsCompositePackagingElement; import org.jetbrains.jps.model.artifact.elements.JpsPackagingElement; +import java.util.Set; + /** * @author nik */ public class JpsArtifactUtil { - public static boolean processPackagingElements(@NotNull JpsPackagingElement element, @NotNull Processor processor) { + public static boolean processPackagingElements(@NotNull JpsPackagingElement element, + @NotNull Processor processor) { + return processPackagingElements(element, processor, new HashSet()); + } + + private static boolean processPackagingElements(@NotNull JpsPackagingElement element, + @NotNull Processor processor, + final Set processed) { + if (!processed.add(element)) { + return false; + } if (!processor.process(element)) { return false; } if (element instanceof JpsCompositePackagingElement) { for (JpsPackagingElement child : ((JpsCompositePackagingElement)element).getChildren()) { - processPackagingElements(child, processor); + processPackagingElements(child, processor, processed); } } else if (element instanceof JpsComplexPackagingElement) { for (JpsPackagingElement child : ((JpsComplexPackagingElement)element).getSubstitution()) { - processPackagingElements(child, processor); + processPackagingElements(child, processor, processed); } } return true; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContext.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContext.java index ff52949da40f..fd1e1164cb1c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContext.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContext.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.ProjectPaths; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.JpsProject; +import org.jetbrains.jps.model.artifact.JpsArtifact; /** * @author nik @@ -16,4 +17,8 @@ public interface ArtifactInstructionsBuilderContext { JpsProject getJpsProject(); JpsModel getJpsModel(); + + boolean enterArtifact(JpsArtifact artifact); + + void leaveArtifact(JpsArtifact artifact); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContextImpl.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContextImpl.java index 5ea6760d8f52..339976a92559 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContextImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderContextImpl.java @@ -4,6 +4,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.ProjectPaths; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.JpsProject; +import org.jetbrains.jps.model.artifact.JpsArtifact; + +import java.util.HashSet; +import java.util.Set; /** * @author nik @@ -11,12 +15,14 @@ import org.jetbrains.jps.model.JpsProject; public class ArtifactInstructionsBuilderContextImpl implements ArtifactInstructionsBuilderContext { private final JpsProject myJpsProject; private final ProjectPaths myProjectPaths; + private final Set myParentArtifacts; private JpsModel myModel; public ArtifactInstructionsBuilderContextImpl(JpsModel jpsModel, ProjectPaths projectPaths) { myJpsProject = jpsModel.getProject(); myModel = jpsModel; myProjectPaths = projectPaths; + myParentArtifacts = new HashSet(); } @Override @@ -29,6 +35,16 @@ public class ArtifactInstructionsBuilderContextImpl implements ArtifactInstructi return myModel; } + @Override + public boolean enterArtifact(JpsArtifact artifact) { + return myParentArtifacts.add(artifact); + } + + @Override + public void leaveArtifact(JpsArtifact artifact) { + myParentArtifacts.remove(artifact); + } + @NotNull @Override public ProjectPaths getProjectPaths() { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 4f1b7ff07e46..297427a69621 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -339,7 +339,7 @@ public class JavaBuilder extends ModuleLevelBuilder { final int filesCount = files.size(); boolean compiledOk = true; if (filesCount > 0) { - LOG.info("Compiling " + filesCount + " java files; module: " + chunkName + (chunk.isTests() ? " (tests)" : "")); + LOG.info("Compiling " + filesCount + " java files; module: " + chunkName + (chunk.containsTests() ? " (tests)" : "")); if (LOG.isDebugEnabled()) { LOG.debug(" classpath for " + chunkName + ":"); for (File file : classpath) { @@ -365,7 +365,7 @@ public class JavaBuilder extends ModuleLevelBuilder { context.processMessage(new ProgressMessage("Instrumenting forms [" + chunkName + "]")); instrumentForms(context, chunk, chunkSourcePath, finder, forms, outputSink); JpsUiDesignerConfiguration configuration = JpsUiDesignerExtensionService.getInstance().getUiDesignerConfiguration(pd.jpsProject); - if (configuration != null && configuration.isCopyFormsRuntimeToOutput() && !chunk.isTests()) { + if (configuration != null && configuration.isCopyFormsRuntimeToOutput() && !chunk.containsTests()) { for (JpsModule module : chunk.getModules()) { final File outputDir = paths.getModuleOutputDir(module, false); if (outputDir != null) { @@ -760,7 +760,7 @@ public class JavaBuilder extends ModuleLevelBuilder { } final File srcOutput = context.getProjectPaths() - .getAnnotationProcessorGeneratedSourcesOutputDir(chunk.getModules().iterator().next(), chunk.isTests(), + .getAnnotationProcessorGeneratedSourcesOutputDir(chunk.getModules().iterator().next(), chunk.containsTests(), profile.getGeneratedSourcesDirectoryName()); if (srcOutput != null) { srcOutput.mkdirs(); @@ -874,7 +874,7 @@ public class JavaBuilder extends ModuleLevelBuilder { } @Override - public void cleanupResources(CompileContext context, ModuleChunk chunk) { + public void cleanupChunkResources(CompileContext context) { JavaBuilderUtil.cleanupChunkResources(context); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java index 308555bc6155..999a1e20f9d2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/storage/BuildDataManager.java @@ -3,10 +3,9 @@ package org.jetbrains.jps.incremental.storage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.BuildTarget; +import org.jetbrains.jps.builders.impl.BuildTargetChunk; import org.jetbrains.jps.builders.java.dependencyView.Mappings; -import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.artifacts.ArtifactsBuildData; import java.io.*; @@ -170,10 +169,10 @@ public class BuildDataManager implements StorageOwner { } } - public void closeSourceToOutputStorages(Collection chunks) throws IOException { + public void closeSourceToOutputStorages(Collection chunks) throws IOException { synchronized (mySourceToOutputLock) { - for (ModuleChunk chunk : chunks) { - for (ModuleBuildTarget target : chunk.getTargets()) { + for (BuildTargetChunk chunk : chunks) { + for (BuildTarget target : chunk.getTargets()) { final SourceToOutputMapping mapping = mySourceToOutputs.remove(target); if (mapping != null) { mapping.close(); diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/ModuleClasspathTest.groovy b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/ModuleClasspathTest.groovy index c8f5935acdd6..de82d179febd 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/ModuleClasspathTest.groovy +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/ModuleClasspathTest.groovy @@ -61,7 +61,7 @@ public class ModuleClasspathTest extends JpsRebuildTestCase { private ModuleChunk createChunk(String moduleName) { def module = myJpsProject.modules.find {it.name == moduleName} - return new ModuleChunk([new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION)] as Set, false) + return new ModuleChunk([new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION)] as Set) } private def assertClasspath(List expected, List classpath) { diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTest.java b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTest.java index 72dda0f8b27a..4690ee72c4de 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/incremental/artifacts/ArtifactBuilderTest.java @@ -238,6 +238,15 @@ public class ArtifactBuilderTest extends ArtifactBuilderTestCase { assertBuildFailed(a); } + public void testArtifactContainingSelfIncludingArtifactWithoutOutput() { + final JpsArtifact a = addArtifact("a", root()); + LayoutElementTestUtil.addArtifactToLayout(a, a); + final JpsArtifact b = addArtifact("b", root().artifact(a)); + a.setOutputPath(null); + + assertBuildFailed(b); + } + //IDEA-73893 public void testManifestFileIsFirstEntry() throws IOException { final String firstFile = createFile("src/A.txt"); diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 1769b6f3e09b..ec52a5636d1e 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -887,7 +887,7 @@ public class StringUtil extends StringUtilRt { } @NotNull - public static String notNullize(final String s) { + public static String notNullize(final @Nullable String s) { return notNullize(s, ""); } diff --git a/plugins/android/android.iml b/plugins/android/android.iml index 0eda4c87660c..5f122f82a9d9 100644 --- a/plugins/android/android.iml +++ b/plugins/android/android.iml @@ -65,6 +65,7 @@ + diff --git a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java index f169fa963bdf..0353bfa77f2d 100644 --- a/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java +++ b/plugins/android/common/src/org/jetbrains/android/util/AndroidCommonUtils.java @@ -98,6 +98,8 @@ public class AndroidCommonUtils { @NonNls public static final String ANDROID_MANIFEST_MERGER_PROPERTY = "manifestmerger.enabled"; @NonNls public static final String PROGUARD_CFG_OUTPUT_FILE_NAME = "proguard.txt"; + @NonNls public static final String PROJECT_BUILD_TARGET_TYPE_ID = "android-project"; + private AndroidCommonUtils() { } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java index cc070517636b..85d1f0f5e35e 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidBuilderService.java @@ -1,9 +1,11 @@ package org.jetbrains.jps.android; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.android.builder.AndroidProjectBuildTarget; +import org.jetbrains.jps.builders.BuildTargetType; import org.jetbrains.jps.incremental.BuilderService; import org.jetbrains.jps.incremental.ModuleLevelBuilder; -import org.jetbrains.jps.incremental.ProjectLevelBuilder; +import org.jetbrains.jps.incremental.TargetBuilder; import java.util.Arrays; import java.util.List; @@ -12,6 +14,11 @@ import java.util.List; * @author Eugene.Kudelevsky */ public class AndroidBuilderService extends BuilderService { + @Override + public List> getTargetTypes() { + return Arrays.asList(AndroidProjectBuildTarget.TargetType.INSTANCE); + } + @NotNull @Override public List createModuleLevelBuilders() { @@ -21,7 +28,7 @@ public class AndroidBuilderService extends BuilderService { @NotNull @Override - public List createProjectLevelBuilders() { + public List> createBuilders() { return Arrays.asList(new AndroidDexBuilder(), new AndroidPackagingBuilder()); } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java index 2356ea7ddc0c..8a4d0c304ee3 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidDexBuilder.java @@ -30,6 +30,7 @@ import org.jetbrains.android.util.AndroidCompilerMessageKind; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.ProjectPaths; +import org.jetbrains.jps.android.builder.AndroidProjectBuildTarget; import org.jetbrains.jps.android.model.JpsAndroidModuleExtension; import org.jetbrains.jps.android.model.JpsAndroidSdkProperties; import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; @@ -52,7 +53,7 @@ import java.util.*; /** * @author Eugene.Kudelevsky */ -public class AndroidDexBuilder extends ProjectLevelBuilder { +public class AndroidDexBuilder extends TargetBuilder { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidDexBuilder"); @NonNls private static final String BUILDER_NAME = "android-dex"; @@ -60,9 +61,13 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { private static final Key BUILD_LISTENER_KEY = Key.create("BUILD_LISTENER_KEY"); public static final Key> DIRTY_OUTPUT_DIRS = Key.create("DIRTY_OUTPUT_DIRS"); + public AndroidDexBuilder() { + super(Collections.singletonList(AndroidProjectBuildTarget.TargetType.INSTANCE)); + } + @Override - public void build(CompileContext context) throws ProjectBuildException { - if (!AndroidJpsUtil.containsAndroidFacet(context.getProjectDescriptor().jpsProject) || AndroidJpsUtil.isLightBuild(context)) { + public void build(@NotNull AndroidProjectBuildTarget target, @NotNull CompileContext context) throws ProjectBuildException { + if (target.getKind() != AndroidProjectBuildTarget.AndroidBuilderKind.DEX && AndroidJpsUtil.isLightBuild(context)) { return; } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java index 6a6ef138e9fd..8369e61f2246 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidJpsUtil.java @@ -47,7 +47,7 @@ import java.util.regex.Matcher; /** * @author Eugene.Kudelevsky */ -class AndroidJpsUtil { +public class AndroidJpsUtil { private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.android.AndroidJpsUtil"); @NonNls public static final String ANDROID_STORAGE_DIR = "android"; diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java index 796056b38e69..0af07a13e330 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidLibraryPackagingBuilder.java @@ -30,7 +30,7 @@ public class AndroidLibraryPackagingBuilder extends ModuleLevelBuilder { @Override public ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException { - if (chunk.isTests() || !AndroidJpsUtil.containsAndroidFacet(chunk) || AndroidJpsUtil.isLightBuild(context)) { + if (chunk.containsTests() || !AndroidJpsUtil.containsAndroidFacet(chunk) || AndroidJpsUtil.isLightBuild(context)) { return ExitCode.NOTHING_DONE; } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java index ecdf0a5db7e4..39d983394894 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidPackagingBuilder.java @@ -16,6 +16,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.JpsPathUtil; import org.jetbrains.jps.ProjectPaths; +import org.jetbrains.jps.android.builder.AndroidProjectBuildTarget; import org.jetbrains.jps.android.model.JpsAndroidModuleExtension; import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; import org.jetbrains.jps.incremental.*; @@ -36,11 +37,15 @@ import java.util.*; /** * @author Eugene.Kudelevsky */ -public class AndroidPackagingBuilder extends ProjectLevelBuilder { +public class AndroidPackagingBuilder extends TargetBuilder { @NonNls private static final String BUILDER_NAME = "android-packager"; @NonNls private static final String RELEASE_SUFFIX = ".release"; @NonNls private static final String UNSIGNED_SUFFIX = ".unsigned"; + public AndroidPackagingBuilder() { + super(Collections.singletonList(AndroidProjectBuildTarget.TargetType.INSTANCE)); + } + @Override public String getName() { return BUILDER_NAME; @@ -52,8 +57,8 @@ public class AndroidPackagingBuilder extends ProjectLevelBuilder { } @Override - public void build(CompileContext context) throws ProjectBuildException { - if (!AndroidJpsUtil.containsAndroidFacet(context.getProjectDescriptor().jpsProject) || AndroidJpsUtil.isLightBuild(context)) { + public void build(@NotNull AndroidProjectBuildTarget target, @NotNull CompileContext context) throws ProjectBuildException { + if (target.getKind() != AndroidProjectBuildTarget.AndroidBuilderKind.PACKAGING || AndroidJpsUtil.isLightBuild(context)) { return; } final Collection modules = context.getProjectDescriptor().jpsProject.getModules(); diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java index 635956ee8010..72d30d967864 100644 --- a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/AndroidSourceGeneratingBuilder.java @@ -72,7 +72,7 @@ public class AndroidSourceGeneratingBuilder extends ModuleLevelBuilder { @Override public ModuleLevelBuilder.ExitCode build(CompileContext context, ModuleChunk chunk) throws ProjectBuildException { - if (chunk.isTests() || !AndroidJpsUtil.containsAndroidFacet(chunk)) { + if (chunk.containsTests() || !AndroidJpsUtil.containsAndroidFacet(chunk)) { return ExitCode.NOTHING_DONE; } diff --git a/plugins/android/jps-plugin/src/org/jetbrains/jps/android/builder/AndroidProjectBuildTarget.java b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/builder/AndroidProjectBuildTarget.java new file mode 100644 index 000000000000..5eaee9632097 --- /dev/null +++ b/plugins/android/jps-plugin/src/org/jetbrains/jps/android/builder/AndroidProjectBuildTarget.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.jps.android.builder; + +import org.jetbrains.android.util.AndroidCommonUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.android.AndroidJpsUtil; +import org.jetbrains.jps.builders.*; +import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; +import org.jetbrains.jps.incremental.ModuleBuildTarget; +import org.jetbrains.jps.incremental.ModuleRootsIndex; +import org.jetbrains.jps.model.JpsModel; +import org.jetbrains.jps.model.module.JpsModule; + +import java.util.*; + +/** + * @author nik + */ +public class AndroidProjectBuildTarget extends BuildTarget { + public enum AndroidBuilderKind {DEX, PACKAGING} + private final AndroidBuilderKind myKind; + private final JpsModel myModel; + + public AndroidProjectBuildTarget(@NotNull AndroidBuilderKind kind, JpsModel model) { + super(TargetType.INSTANCE); + myKind = kind; + myModel = model; + } + + @Override + public String getId() { + return myKind.name(); + } + + public AndroidBuilderKind getKind() { + return myKind; + } + + @Override + public Collection> computeDependencies() { + List> result = new ArrayList>(); + for (JpsModule module : myModel.getProject().getModules()) { + if (AndroidJpsUtil.getExtension(module) != null) { + result.add(new ModuleBuildTarget(module, JavaModuleBuildTargetType.PRODUCTION)); + } + } + return result; + } + + @NotNull + @Override + public List computeRootDescriptors(JpsModel model, ModuleRootsIndex index) { + return Collections.emptyList(); + } + + @Nullable + @Override + public BuildRootDescriptor findRootDescriptor(String rootId, BuildRootIndex rootIndex) { + return null; + } + + @NotNull + @Override + public String getPresentableName() { + return "Android " + myKind.name(); + } + + public static class TargetType extends BuildTargetType { + public static final TargetType INSTANCE = new TargetType(); + + public TargetType() { + super(AndroidCommonUtils.PROJECT_BUILD_TARGET_TYPE_ID); + } + + @NotNull + @Override + public List computeAllTargets(@NotNull JpsModel model) { + if (!AndroidJpsUtil.containsAndroidFacet(model.getProject())) { + return Collections.emptyList(); + } + return Arrays.asList(new AndroidProjectBuildTarget(AndroidBuilderKind.DEX, model), + new AndroidProjectBuildTarget(AndroidBuilderKind.PACKAGING, model)); + } + + @NotNull + @Override + public BuildTargetLoader createLoader(@NotNull final JpsModel model) { + return new BuildTargetLoader() { + @Nullable + @Override + public AndroidProjectBuildTarget createTarget(@NotNull String targetId) { + return new AndroidProjectBuildTarget(AndroidBuilderKind.valueOf(targetId), model); + } + }; + } + } +} diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index f85102e42ab7..e9ed727fa56d 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -325,6 +325,7 @@ + diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index 2fe2c0e46086..2726a66a8d69 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -603,7 +603,11 @@ public class AndroidCompileUtil { } public static boolean isFullBuild(@NotNull CompileContext context) { - final RunConfiguration c = CompileStepBeforeRun.getRunConfiguration(context); + return isFullBuild(context.getCompileScope()); + } + + public static boolean isFullBuild(@NotNull CompileScope scope) { + final RunConfiguration c = CompileStepBeforeRun.getRunConfiguration(scope); return c == null || !AndroidCommonUtils.isTestConfiguration(c.getType().getId()); } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidTargetBuildScopeProvider.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidTargetBuildScopeProvider.java new file mode 100644 index 000000000000..156eab6d70da --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidTargetBuildScopeProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.android.compiler; + +import com.intellij.compiler.impl.BuildTargetScopeProvider; +import com.intellij.facet.ProjectFacetManager; +import com.intellij.openapi.compiler.CompileScope; +import com.intellij.openapi.compiler.CompilerFilter; +import com.intellij.openapi.project.Project; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.android.util.AndroidCommonUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope; + +import java.util.Collections; +import java.util.List; + +/** + * @author nik + */ +public class AndroidTargetBuildScopeProvider extends BuildTargetScopeProvider { + @NotNull + @Override + public List getBuildTargetScopes(@NotNull CompileScope baseScope, @NotNull CompilerFilter filter, @NotNull Project project) { + if (AndroidCompileUtil.isFullBuild(baseScope) && ProjectFacetManager.getInstance(project).hasFacets(AndroidFacet.ID)) { + return Collections + .singletonList(TargetTypeBuildScope.newBuilder().setTypeId(AndroidCommonUtils.PROJECT_BUILD_TARGET_TYPE_ID).setAllTargets(true).build()); + } + return Collections.emptyList(); + } +} diff --git a/plugins/devkit/devkit.iml b/plugins/devkit/devkit.iml index 4e4c26d53abd..77509ce030da 100644 --- a/plugins/devkit/devkit.iml +++ b/plugins/devkit/devkit.iml @@ -26,6 +26,7 @@ + diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 182fa6230174..3b0d3c24432a 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -16,6 +16,7 @@ + getBuildTargetScopes(@NotNull CompileScope baseScope, @NotNull CompilerFilter filter, @NotNull Project project) { + List pluginArtifactTargetIds = new ArrayList(); + for (Module module : baseScope.getAffectedModules()) { + if (PluginModuleType.isOfType(module)) { + pluginArtifactTargetIds.add(module.getName()+":plugin"); + } + } + + if (pluginArtifactTargetIds.isEmpty()) { + return Collections.emptyList(); + } + return Collections.singletonList(TargetTypeBuildScope.newBuilder().setTypeId(ArtifactBuildTargetType.INSTANCE.getTypeId()).addAllTargetId(pluginArtifactTargetIds).build()); + } +} diff --git a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java index ce0b625bd11c..325d92c2ec4e 100644 --- a/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java +++ b/plugins/groovy/jps-plugin/src/org/jetbrains/jps/incremental/groovy/GroovyBuilder.java @@ -167,7 +167,7 @@ public class GroovyBuilder extends ModuleLevelBuilder { } @Override - public void cleanupResources(CompileContext context, ModuleChunk chunk) { + public void cleanupChunkResources(CompileContext context) { JavaBuilderUtil.cleanupChunkResources(context); STUB_TO_SRC.set(context, null); } @@ -288,7 +288,7 @@ public class GroovyBuilder extends ModuleLevelBuilder { // IMPORTANT! must be the first in classpath cp.add(getGroovyRtRoot().getPath()); - for (File file : context.getProjectPaths().getCompilationClasspathFiles(chunk, chunk.isTests(), false, false)) { + for (File file : context.getProjectPaths().getCompilationClasspathFiles(chunk, chunk.containsTests(), false, false)) { cp.add(FileUtil.toCanonicalPath(file.getPath())); } return new ArrayList(cp);