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 2750aefcfdf5..a4fd3e2f263f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java @@ -7,6 +7,7 @@ import org.jetbrains.jps.indices.IgnoredFileIndex; import org.jetbrains.jps.indices.ModuleExcludeIndex; import org.jetbrains.jps.model.JpsModel; +import java.io.File; import java.io.PrintWriter; import java.util.Collection; import java.util.List; @@ -44,6 +45,9 @@ public abstract class BuildTarget { @NotNull public abstract String getPresentableName(); + @Nullable + public abstract File getOutputDir(BuildDataPaths paths); + @Override public String toString() { return getPresentableName(); 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 189e121d34ac..adb6a8b674d8 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -305,31 +305,19 @@ public class IncProjectBuilder { } private void clearOutputs(CompileContext context) throws ProjectBuildException, IOException { - final MultiMap rootsToDelete = new MultiMapBasedOnSet(); - final Set annotationOutputs = new HashSet(); // separate collection because no root intersection checks needed for annotation generated sources + final MultiMap> rootsToDelete = new MultiMapBasedOnSet>(); final Set allSourceRoots = new HashSet(); final ProjectPaths paths = context.getProjectPaths(); - 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); - } - } + ProjectDescriptor projectDescriptor = context.getProjectDescriptor(); + for (BuildTarget target : projectDescriptor.getBuildTargetIndex().getAllTargets()) { + File outputDir = target.getOutputDir(projectDescriptor.dataManager.getDataPaths()); + if (outputDir != null) { + rootsToDelete.putValue(outputDir, target); } } - ProjectDescriptor projectDescriptor = context.getProjectDescriptor(); for (BuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) { for (BuildTarget target : projectDescriptor.getBuildTargetIndex().getAllTargets(type)) { for (BuildRootDescriptor descriptor : projectDescriptor.getBuildRootIndex().getTargetRoots(target, context)) { @@ -340,7 +328,7 @@ public class IncProjectBuilder { // check that output and source roots are not overlapping final List filesToDelete = new ArrayList(); - for (Map.Entry> entry : rootsToDelete.entrySet()) { + for (Map.Entry>> entry : rootsToDelete.entrySet()) { context.checkCanceled(); boolean okToDelete = true; final File outputRoot = entry.getKey(); @@ -366,12 +354,24 @@ public class IncProjectBuilder { else { context.processMessage(new CompilerMessage(BUILD_NAME, BuildMessage.Kind.WARNING, "Output path " + outputRoot.getPath() + " intersects with a source root. The output cannot be cleaned.")); // clean only those files we are aware of - for (ModuleBuildTarget target : entry.getValue()) { + for (BuildTarget target : entry.getValue()) { clearOutputFiles(context, target); } } } + final Set annotationOutputs = new HashSet(); // separate collection because no root intersection checks needed for annotation generated sources + for (JavaModuleBuildTargetType type : JavaModuleBuildTargetType.ALL_TYPES) { + for (ModuleBuildTarget target : projectDescriptor.getBuildTargetIndex().getAllTargets(type)) { + 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); + } + } + } + } for (File annotationOutput : annotationOutputs) { // do not delete output root itself to avoid lots of unnecessary "roots_changed" events in IDEA final File[] children = annotationOutput.listFiles(); @@ -649,26 +649,23 @@ public class IncProjectBuilder { } private static void createClasspathIndex(final ModuleChunk chunk) { - final Set outputPaths = new LinkedHashSet(); + final Set outputDirs = new THashSet(FileUtil.FILE_HASHING_STRATEGY); for (ModuleBuildTarget target : chunk.getTargets()) { - final File outputDir = JpsJavaExtensionService.getInstance().getOutputDirectory(target.getModule(), target.isTests()); - if (outputDir != null) { - outputPaths.add(outputDir); - } - } - for (File outputRoot : outputPaths) { - try { - BufferedWriter writer = new BufferedWriter(new FileWriter(new File(outputRoot, CLASSPATH_INDEX_FINE_NAME))); + File outputDir = target.getOutputDir(); + if (outputDir != null && outputDirs.add(outputDir)) { try { - writeIndex(writer, outputRoot, ""); + BufferedWriter writer = new BufferedWriter(new FileWriter(new File(outputDir, CLASSPATH_INDEX_FINE_NAME))); + try { + writeIndex(writer, outputDir, ""); + } + finally { + writer.close(); + } } - finally { - writer.close(); + catch (IOException e) { + // Ignore. Failed to create optional classpath index } } - catch (IOException e) { - // Ignore. Failed to create optional classpath index - } } } @@ -964,7 +961,7 @@ public class IncProjectBuilder { if (context.isMake()) { if (pd.fsState.markInitialScanPerformed(target)) { boolean forceMarkDirty = false; - final File currentOutput = context.getProjectPaths().getModuleOutputDir(target.getModule(), target.isTests()); + final File currentOutput = target.getOutputDir(); if (currentOutput != null) { final Pair outputsPair = pd.dataManager.getOutputRootsLayout().getState(target.getModuleName()); if (outputsPair != null) { @@ -1011,7 +1008,7 @@ public class IncProjectBuilder { } private static void updateOutputRootsLayout(CompileContext context, ModuleBuildTarget target) throws IOException { - final File currentOutput = context.getProjectPaths().getModuleOutputDir(target.getModule(), target.isTests()); + final File currentOutput = target.getOutputDir(); if (currentOutput == null) { return; } 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 99f5a4bc23a9..16803ac7a2a1 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModuleBuildTarget.java @@ -3,12 +3,12 @@ package org.jetbrains.jps.incremental; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.builders.storage.BuildDataPaths; -import org.jetbrains.jps.util.JpsPathUtil; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.BuildTarget; import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; +import org.jetbrains.jps.builders.storage.BuildDataPaths; import org.jetbrains.jps.indices.IgnoredFileIndex; import org.jetbrains.jps.indices.ModuleExcludeIndex; import org.jetbrains.jps.model.JpsModel; @@ -19,6 +19,7 @@ import org.jetbrains.jps.model.java.JpsJavaDependenciesEnumerator; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.model.module.JpsTypedModuleSourceRoot; +import org.jetbrains.jps.util.JpsPathUtil; import java.io.File; import java.util.ArrayList; @@ -41,6 +42,17 @@ public class ModuleBuildTarget extends BuildTarget { myModule = module; } + @Nullable + public File getOutputDir() { + return JpsJavaExtensionService.getInstance().getOutputDirectory(myModule, myTargetType.isTests()); + } + + @Nullable + @Override + public File getOutputDir(BuildDataPaths paths) { + return getOutputDir(); + } + @NotNull public JpsModule getModule() { return myModule; 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 5cbffd1bc4f3..e4b4d5487548 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,8 +1,10 @@ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.BuildRootDescriptor; import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.BuildTarget; @@ -17,6 +19,7 @@ import org.jetbrains.jps.model.artifact.JpsArtifact; import org.jetbrains.jps.model.artifact.elements.JpsArtifactOutputPackagingElement; import org.jetbrains.jps.model.artifact.elements.JpsPackagingElement; +import java.io.File; import java.io.PrintWriter; import java.util.Collection; import java.util.LinkedHashSet; @@ -110,4 +113,11 @@ public class ArtifactBuildTarget extends BuildTarget { public String getPresentableName() { return "Artifact '" + myArtifact.getName() + "'"; } + + @Nullable + @Override + public File getOutputDir(BuildDataPaths paths) { + String outputPath = myArtifact.getOutputPath(); + return !StringUtil.isEmpty(outputPath) ? new File(FileUtil.toSystemDependentName(outputPath)) : null; + } } 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 a9c87ffb6413..6b2a6cc45b16 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 @@ -368,11 +368,13 @@ public class JavaBuilder extends ModuleLevelBuilder { instrumentForms(context, chunk, chunkSourcePath, finder, forms, outputSink); JpsUiDesignerConfiguration configuration = JpsUiDesignerExtensionService.getInstance().getUiDesignerConfiguration( pd.getProject()); - if (configuration != null && configuration.isCopyFormsRuntimeToOutput() && !chunk.containsTests()) { - for (JpsModule module : chunk.getModules()) { - final File outputDir = paths.getModuleOutputDir(module, false); - if (outputDir != null) { - CopyResourcesUtil.copyFormsRuntime(outputDir.getAbsolutePath(), false); + if (configuration != null && configuration.isCopyFormsRuntimeToOutput()) { + for (ModuleBuildTarget target : chunk.getTargets()) { + if (!target.isTests()) { + final File outputDir = target.getOutputDir(); + if (outputDir != null) { + CopyResourcesUtil.copyFormsRuntime(outputDir.getAbsolutePath(), false); + } } } } @@ -888,7 +890,7 @@ public class JavaBuilder extends ModuleLevelBuilder { private static Map> buildOutputDirectoriesMap(CompileContext context, ModuleChunk chunk) { final Map> map = new LinkedHashMap>(); for (ModuleBuildTarget target : chunk.getTargets()) { - final File outputDir = JpsJavaExtensionService.getInstance().getOutputDirectory(target.getModule(), target.isTests()); + final File outputDir = target.getOutputDir(); if (outputDir == null) { continue; } 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 fc8d6dd00d27..506004b2853e 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 @@ -2,10 +2,10 @@ package org.jetbrains.jps.incremental.artifacts; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.PathUtil; -import org.jetbrains.jps.util.JpsPathUtil; import org.jetbrains.jps.model.artifact.JpsArtifact; import org.jetbrains.jps.model.library.JpsLibrary; import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.util.JpsPathUtil; import java.io.File; import java.io.IOException; @@ -269,4 +269,15 @@ public class ArtifactBuilderTest extends ArtifactBuilderTestCase { } } + public void testClearOutputOnRebuild() throws IOException { + String file = createFile("d/a.txt"); + JpsArtifact a = addArtifact(root().parentDirCopy(file)); + buildAll(); + new File(a.getOutputPath(), "b.txt").createNewFile(); + buildAllAndAssertUpToDate(); + assertOutput(a, fs().file("a.txt").file("b.txt")); + + rebuildAll(); + assertOutput(a, fs().file("a.txt")); + } } diff --git a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java index db50e4297b2f..e805d539edbf 100644 --- a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java +++ b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/compiler/JpsJavaCompilerConfigurationImpl.java @@ -18,7 +18,7 @@ import java.util.*; public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase implements JpsJavaCompilerConfiguration { public static final JpsElementChildRole ROLE = JpsElementChildRoleBase.create("compiler configuration"); private boolean myAddNotNullAssertions = true; - private boolean myClearOutputDirectoryOnRebuild; + private boolean myClearOutputDirectoryOnRebuild = true; private JpsCompilerExcludes myCompilerExcludes = new JpsCompilerExcludesImpl(); private List myResourcePatterns = new ArrayList(); private List myAnnotationProcessingProfiles = new ArrayList(); 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 index 63be2b1e1b27..3f079a4e6cfd 100644 --- 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 @@ -29,6 +29,7 @@ import org.jetbrains.jps.indices.ModuleExcludeIndex; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.module.JpsModule; +import java.io.File; import java.util.*; /** @@ -105,7 +106,13 @@ public class AndroidProjectBuildTarget extends BuildTarget public String getPresentableName() { return "Android " + myKind.name(); } - + + @Nullable + @Override + public File getOutputDir(BuildDataPaths paths) { + return null; + } + public static class TargetType extends BuildTargetType { public static final TargetType INSTANCE = new TargetType(); 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 c6a67bc14898..6eb82bcd3908 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 @@ -189,7 +189,7 @@ public class GroovyBuilder extends ModuleLevelBuilder { private static Map getCanonicalModuleOutputs(CompileContext context, ModuleChunk chunk) { Map finalOutputs = new HashMap(); for (ModuleBuildTarget target : chunk.getTargets()) { - File moduleOutputDir = context.getProjectPaths().getModuleOutputDir(target.getModule(), target.isTests()); + File moduleOutputDir = target.getOutputDir(); if (moduleOutputDir == null) { context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Output directory not specified for module " + target.getModuleName())); return null;