diff --git a/java/java-tests/testData/compileServer/incremental/uiDesigner/simple/build.log b/java/java-tests/testData/compileServer/incremental/uiDesigner/simple/build.log index 7f83624edded..7f1ecc31429a 100644 --- a/java/java-tests/testData/compileServer/incremental/uiDesigner/simple/build.log +++ b/java/java-tests/testData/compileServer/incremental/uiDesigner/simple/build.log @@ -1,6 +1,9 @@ +Cleaning output files: +out/production/Simple/xxx/MyForm.class +End of files Compiling files: src/xxx/MyForm.java End of files Compiling forms: src/xxx/MyForm.form -End of files +End of files \ No newline at end of file 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 7a1ba3e660fa..952dd4f7d975 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/BuildTarget.java @@ -42,9 +42,9 @@ import java.util.List; * @author nik */ public abstract class BuildTarget { - private final BuildTargetType myTargetType; + private final BuildTargetType> myTargetType; - protected BuildTarget(BuildTargetType targetType) { + protected BuildTarget(BuildTargetType> targetType) { myTargetType = targetType; } @@ -54,7 +54,7 @@ public abstract class BuildTarget { */ public abstract String getId(); - public final BuildTargetType getTargetType() { + public final BuildTargetType> getTargetType() { return myTargetType; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/builders/ModuleBasedTarget.java b/jps/jps-builders/src/org/jetbrains/jps/builders/ModuleBasedTarget.java index 17b0bd6566e6..4bc1b72e0a7d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/builders/ModuleBasedTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/builders/ModuleBasedTarget.java @@ -27,7 +27,7 @@ import org.jetbrains.jps.model.module.JpsModule; public abstract class ModuleBasedTarget extends BuildTarget { protected final JpsModule myModule; - public ModuleBasedTarget(ModuleBasedBuildTargetType targetType, @NotNull JpsModule module) { + public ModuleBasedTarget(ModuleBasedBuildTargetType> targetType, @NotNull JpsModule module) { super(targetType); myModule = module; } 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 95523120a9e8..f0737f19321f 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/FSOperations.java @@ -23,10 +23,7 @@ import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; -import org.jetbrains.jps.builders.BuildRootDescriptor; -import org.jetbrains.jps.builders.BuildRootIndex; -import org.jetbrains.jps.builders.BuildTarget; -import org.jetbrains.jps.builders.FileProcessor; +import org.jetbrains.jps.builders.*; import org.jetbrains.jps.builders.impl.BuildTargetChunk; import org.jetbrains.jps.builders.java.JavaBuilderUtil; import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor; @@ -42,9 +39,7 @@ import java.io.FileFilter; import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Set; +import java.util.*; /** * @author Eugene Zhuravlev @@ -98,6 +93,88 @@ public class FSOperations { } } + public interface DirtyFilesHolderBuilder> { + /** + * Marks specified files dirty if the file is not deleted + * If the file was marked dirty as a result of this operation or had been already marked dirty, + * the file is stored internally in the builder + */ + DirtyFilesHolderBuilder markDirtyFile(T target, File file) throws IOException; + + /** + * @return an object accumulating information about files marked with this builder + * Use returned object for further processing of marked files. For example, the object can be passed to + * {@link BuildOperations#cleanOutputsCorrespondingToChangedFiles(CompileContext, DirtyFilesHolder)} + * to clean outputs corresponding marked sources + */ + DirtyFilesHolder create(); + } + + /** + * @param context + * @param round desired compilation round at which these dirty marks should be visible + * @return a builder object that marks dirty files and collects data about files marked + */ + public static > DirtyFilesHolderBuilder createDirtyFilesHolderBuilder(CompileContext context, final CompilationRound round) { + return new DirtyFilesHolderBuilder() { + private final Map>> dirtyFiles = new HashMap<>(); + @Override + public DirtyFilesHolderBuilder markDirtyFile(T target, File file) throws IOException { + final ProjectDescriptor pd = context.getProjectDescriptor(); + final R rd = pd.getBuildRootIndex().findParentDescriptor(file, Collections.singleton(target.getTargetType()), context); + if (rd != null) { + if (pd.fsState.markDirtyIfNotDeleted(context, round, file, rd, pd.getProjectStamps().getStampStorage()) || pd.fsState.isMarkedForRecompilation(context, round, rd, file)) { + Map> targetFiles = dirtyFiles.get(target); + if (targetFiles == null) { + targetFiles = new HashMap<>(); + dirtyFiles.put(target, targetFiles); + } + Set rootFiles = targetFiles.get(rd); + if (rootFiles == null) { + rootFiles = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY); + targetFiles.put(rd, rootFiles); + } + rootFiles.add(file); + } + } + return this; + } + + @Override + public DirtyFilesHolder create() { + return new DirtyFilesHolder() { + @Override + public void processDirtyFiles(@NotNull FileProcessor processor) throws IOException { + for (Map.Entry>> entry : dirtyFiles.entrySet()) { + final T target = entry.getKey(); + for (Map.Entry> targetEntry: entry.getValue().entrySet()) { + final R rd = targetEntry.getKey(); + for (File file : targetEntry.getValue()) { + processor.apply(target, file, rd); + } + } + } + } + + @Override + public boolean hasDirtyFiles() { + return !dirtyFiles.isEmpty(); + } + + @Override + public boolean hasRemovedFiles() { + return false; + } + + @Override + public @NotNull Collection getRemovedFiles(@NotNull T target) { + return Collections.emptyList(); + } + }; + } + }; + } + public static void markDeleted(CompileContext context, File file) throws IOException { final JavaSourceRootDescriptor rd = context.getProjectDescriptor().getBuildRootIndex().findJavaRootDescriptor(context, file); if (rd != null) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBasedBuildTarget.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBasedBuildTarget.java index aa0d1b01adc2..497341a8309d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBasedBuildTarget.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactBasedBuildTarget.java @@ -25,7 +25,7 @@ import org.jetbrains.jps.model.artifact.JpsArtifact; public abstract class ArtifactBasedBuildTarget extends BuildTarget { private final JpsArtifact myArtifact; - protected ArtifactBasedBuildTarget(BuildTargetType targetType, @NotNull JpsArtifact artifact) { + protected ArtifactBasedBuildTarget(BuildTargetType> targetType, @NotNull JpsArtifact artifact) { super(targetType); myArtifact = artifact; } diff --git a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java index 60041ac7a072..3d80ab4c62b3 100644 --- a/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java +++ b/plugins/ui-designer/jps-plugin/src/org/jetbrains/jps/uiDesigner/compiler/FormsBindingManager.java @@ -118,6 +118,8 @@ public class FormsBindingManager extends FormsBuilder { final JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getCompilerConfiguration(project); final JpsCompilerExcludes excludes = configuration.getCompilerExcludes(); + final FSOperations.DirtyFilesHolderBuilder holderBuilder = FSOperations.createDirtyFilesHolderBuilder(context, CompilationRound.CURRENT); + // force compilation of bound source file if the form is dirty for (final Map.Entry entry : formsToCompile.entrySet()) { final File form = entry.getKey(); @@ -126,7 +128,7 @@ public class FormsBindingManager extends FormsBuilder { for (File boundSource : sources) { if (!excludes.isExcluded(boundSource)) { addBinding(boundSource, form, srcToForms); - FSOperations.markDirty(context, CompilationRound.CURRENT, boundSource); + holderBuilder.markDirtyFile(target, boundSource); context.getScope().markIndirectlyAffected(target, boundSource); filesToCompile.put(boundSource, target); exitCode = ExitCode.OK; @@ -145,7 +147,8 @@ public class FormsBindingManager extends FormsBuilder { final File formFile = new File(formPath); if (!excludes.isExcluded(formFile) && formFile.exists()) { addBinding(srcFile, formFile, srcToForms); - FSOperations.markDirty(context, CompilationRound.CURRENT, formFile); + holderBuilder.markDirtyFile(target, formFile); + context.getScope().markIndirectlyAffected(target, formFile); formsToCompile.put(formFile, target); exitCode = ExitCode.OK; @@ -153,6 +156,8 @@ public class FormsBindingManager extends FormsBuilder { } } } + + BuildOperations.cleanOutputsCorrespondingToChangedFiles(context, holderBuilder.create()); } FORMS_TO_COMPILE.set(context, srcToForms.isEmpty()? null : srcToForms);