diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java index 87d7e6bd36a8..326d2d4e5558 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java @@ -20,6 +20,8 @@ import org.jetbrains.jps.api.*; import org.jetbrains.jps.idea.IdeaProjectLoader; import org.jetbrains.jps.idea.SystemOutErrorReporter; import org.jetbrains.jps.incremental.*; +import org.jetbrains.jps.incremental.artifacts.ArtifactSourceTimestampStorage; +import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactRootDescriptor; import org.jetbrains.jps.incremental.fs.BuildFSState; import org.jetbrains.jps.incremental.fs.RootDescriptor; import org.jetbrains.jps.incremental.messages.*; @@ -180,9 +182,7 @@ final class BuildSession implements Runnable, CanceledStatus { try { final boolean shouldApplyEvent = loadFsState(fsState, dataStorageRoot, myInitialFSDelta); - if (shouldApplyEvent && buildType == BuildType.MAKE && !containsChanges(myInitialFSDelta) && !fsState.hasWorkToDo() - && artifacts.isEmpty()//todo[nik] currently changes in artifacts source files aren't registered in the delta - ) { + if (shouldApplyEvent && buildType == BuildType.MAKE && !containsChanges(myInitialFSDelta) && !fsState.hasWorkToDo()) { applyFSEvent(null, myInitialFSDelta); return; } @@ -333,6 +333,7 @@ final class BuildSession implements Runnable, CanceledStatus { if (pd != null) { final Timestamps timestamps = pd.timestamps.getStorage(); + ArtifactSourceTimestampStorage artifactTimestamps = pd.dataManager.getArtifactsBuildData().getTimestampStorage(); for (String deleted : event.getDeletedPathsList()) { final File file = new File(deleted); @@ -343,10 +344,18 @@ final class BuildSession implements Runnable, CanceledStatus { } pd.fsState.registerDeleted(rd.module, file, rd.isTestRoot, timestamps); } - else { + else if (Utils.IS_TEST_MODE) { + LOG.info("Skipping deleted path: " + file.getPath()); + } + + Collection descriptor = pd.getArtifactRootsIndex().getDescriptors(file); + if (!descriptor.isEmpty()) { if (Utils.IS_TEST_MODE) { - LOG.info("Skipping deleted path: " + file.getPath()); + LOG.info("Applying deleted path from fs event to artifacts: " + file.getPath()); } + for (ArtifactRootDescriptor rootDescriptor : descriptor) + pd.fsState.registerDeleted(rootDescriptor.getArtifactName(), rootDescriptor.getRootId().getArtifactId(), deleted, + artifactTimestamps); } } for (String changed : event.getChangedPathsList()) { @@ -358,9 +367,17 @@ final class BuildSession implements Runnable, CanceledStatus { } pd.fsState.markDirty(null, file, rd, timestamps); } - else { + else if (Utils.IS_TEST_MODE) { + LOG.info("Skipping dirty path: " + file.getPath()); + } + + Collection descriptors = pd.getArtifactRootsIndex().getDescriptors(file); + if (!descriptors.isEmpty()) { if (Utils.IS_TEST_MODE) { - LOG.info("Skipping dirty path: " + file.getPath()); + LOG.info("Applying dirty path from fs event to artifacts: " + file.getPath()); + } + for (ArtifactRootDescriptor descriptor : descriptors) { + pd.fsState.markDirty(descriptor, changed, artifactTimestamps); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ProjectDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ProjectDescriptor.java index 2307bf946a47..9bf8818dcc75 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ProjectDescriptor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ProjectDescriptor.java @@ -4,6 +4,7 @@ import org.jetbrains.jps.Project; import org.jetbrains.jps.incremental.BuildLoggingManager; import org.jetbrains.jps.incremental.CompilerEncodingConfiguration; import org.jetbrains.jps.incremental.ModuleRootsIndex; +import org.jetbrains.jps.incremental.artifacts.ArtifactRootsIndex; import org.jetbrains.jps.incremental.fs.BuildFSState; import org.jetbrains.jps.incremental.storage.BuildDataManager; import org.jetbrains.jps.incremental.storage.ProjectTimestamps; @@ -31,6 +32,7 @@ public final class ProjectDescriptor { public final BuildDataManager dataManager; private final BuildLoggingManager myLoggingManager; public ModuleRootsIndex rootsIndex; + private final ArtifactRootsIndex myArtifactRootsIndex; private int myUseCounter = 1; private Set> myProjectJavaSdks; private CompilerEncodingConfiguration myEncodingConfiguration; @@ -48,7 +50,8 @@ public final class ProjectDescriptor { this.timestamps = timestamps; this.dataManager = dataManager; myLoggingManager = loggingManager; - this.rootsIndex = new ModuleRootsIndex(jpsProject, dataManager); + rootsIndex = new ModuleRootsIndex(jpsProject, dataManager); + myArtifactRootsIndex = new ArtifactRootsIndex(jpsModel, project, dataManager, rootsIndex); myProjectJavaSdks = new HashSet>(); myEncodingConfiguration = new CompilerEncodingConfiguration(project.getFilePathToCharset(), project.getProjectCharset(), rootsIndex); for (JpsModule module : jpsProject.getModules()) { @@ -73,6 +76,10 @@ public final class ProjectDescriptor { return myLoggingManager; } + public ArtifactRootsIndex getArtifactRootsIndex() { + return myArtifactRootsIndex; + } + public synchronized void incUsageCounter() { myUseCounter++; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/AllProjectScope.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/AllProjectScope.java index 2506a5b293e8..ce71f3cbe444 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/AllProjectScope.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/AllProjectScope.java @@ -13,16 +13,12 @@ import java.util.Set; * Date: 9/17/11 */ public class AllProjectScope extends CompileScope { - - private final boolean myIsForcedCompilation; - public AllProjectScope(Project project, JpsProject jpsProject, Set artifacts, boolean forcedCompilation) { - super(project, jpsProject, artifacts); - myIsForcedCompilation = forcedCompilation; + super(project, jpsProject, artifacts, forcedCompilation); } public boolean isRecompilationForced(@NotNull String moduleName) { - return myIsForcedCompilation; + return myForcedCompilation; } public boolean isAffected(@NotNull String moduleName) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildListener.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildListener.java index 41889ae61518..791b1d8a581c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildListener.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/BuildListener.java @@ -15,4 +15,6 @@ public interface BuildListener extends EventListener{ * @param paths collection of pairs [output root->relative path to generated file] */ void filesGenerated(Collection> paths); + + void filesDeleted(Collection paths); } 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 35468b51f103..e564e19814a3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/Builder.java @@ -14,6 +14,6 @@ public abstract class Builder { public void buildStarted(CompileContext context) { } - public void buildFinished(CompileContext ICompileContext) { + public void buildFinished(CompileContext context) { } } 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 338dcdfe5f6e..fd37944f0a73 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContextImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileContextImpl.java @@ -9,6 +9,7 @@ import org.jetbrains.jps.*; import org.jetbrains.jps.api.CanceledStatus; import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.messages.BuildMessage; +import org.jetbrains.jps.incremental.messages.FileDeletedEvent; import org.jetbrains.jps.incremental.messages.FileGeneratedEvent; import org.jetbrains.jps.incremental.messages.ProgressMessage; import org.jetbrains.jps.model.module.JpsModule; @@ -209,6 +210,10 @@ public class CompileContextImpl extends UserDataHolderBase implements CompileCon myListeners.getMulticaster().filesGenerated(paths); } } + else if (msg instanceof FileDeletedEvent) { + Collection paths = ((FileDeletedEvent)msg).getFilePaths(); + myListeners.getMulticaster().filesDeleted(paths); + } } @Override diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileScope.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileScope.java index f76334f75a12..e749f6208af1 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileScope.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/CompileScope.java @@ -19,17 +19,23 @@ public abstract class CompileScope { private final Project myProject; private final JpsProject myJpsProject; private final Set myArtifacts; + protected final boolean myForcedCompilation; - protected CompileScope(@NotNull Project project, JpsProject jpsProject, Set artifacts) { + protected CompileScope(@NotNull Project project, JpsProject jpsProject, Set artifacts, boolean forcedCompilation) { myProject = project; myJpsProject = jpsProject; myArtifacts = artifacts; + myForcedCompilation = forcedCompilation; } public boolean isAffected(JpsArtifact artifact) { return myArtifacts.contains(artifact); } + public boolean isRecompilationForced(JpsArtifact artifact) { + return myForcedCompilation && myArtifacts.contains(artifact); + } + public abstract boolean isAffected(String moduleName, @NotNull File file); public abstract boolean isAffected(@NotNull String moduleName); 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 a87367decdf3..5899be97d1b2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -21,10 +21,7 @@ import org.jetbrains.jps.incremental.fs.RootDescriptor; import org.jetbrains.jps.incremental.java.ExternalJavacDescriptor; import org.jetbrains.jps.incremental.java.JavaBuilder; import org.jetbrains.jps.incremental.java.JavaBuilderLogger; -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.messages.UptoDateFilesSavedEvent; +import org.jetbrains.jps.incremental.messages.*; import org.jetbrains.jps.incremental.storage.*; import org.jetbrains.jps.model.java.JpsJavaClasspathKind; import org.jetbrains.jps.model.java.JpsJavaExtensionService; @@ -300,10 +297,11 @@ public class IncProjectBuilder { final SourceToOutputMapping map = context.getProjectDescriptor().dataManager.getSourceToOutputMap(moduleName, forTests); for (String srcPath : map.getKeys()) { final Collection outs = map.getState(srcPath); - if (outs != null) { + if (outs != null && !outs.isEmpty()) { for (String out : outs) { new File(out).delete(); } + context.processMessage(new FileDeletedEvent(outs)); } } } @@ -655,27 +653,26 @@ public class IncProjectBuilder { // deleting outputs corresponding to non-existing source final Collection outputs = sourceToOutputStorage.getState(deletedSource); - if (outputs != null) { + if (outputs != null && !outputs.isEmpty()) { final JavaBuilderLogger logger = context.getLoggingManager().getJavaBuilderLogger(); if (logger.isEnabled()) { - if (outputs.size() > 0) { - final String[] buffer = new String[outputs.size()]; - int i = 0; - for (final String o : outputs) { - buffer[i++] = o; - } - Arrays.sort(buffer); - logger.log("Cleaning output files:"); - for (final String o : buffer) { - logger.log(o); - } - logger.log("End of files"); + final String[] buffer = new String[outputs.size()]; + int i = 0; + for (final String o : outputs) { + buffer[i++] = o; } + Arrays.sort(buffer); + logger.log("Cleaning output files:"); + for (final String o : buffer) { + logger.log(o); + } + logger.log("End of files"); } for (String output : outputs) { new File(output).delete(); } + context.processMessage(new FileDeletedEvent(outputs)); } // check if deleted source was associated with a form @@ -825,6 +822,9 @@ public class IncProjectBuilder { } new File(output).delete(); } + if (!outputs.isEmpty()) { + context.processMessage(new FileDeletedEvent(outputs)); + } srcToOut.remove(srcPath); } return true; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesAndFilesScope.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesAndFilesScope.java index f46be3c90a17..fae15b37025c 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesAndFilesScope.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesAndFilesScope.java @@ -20,13 +20,11 @@ public class ModulesAndFilesScope extends CompileScope { private final Set myModules; private final Map> myFiles; - private final boolean myForcedCompilation; public ModulesAndFilesScope(Project project, JpsProject jpsProject, Collection modules, Map> files, Set artifacts, boolean isForcedCompilation) { - super(project, jpsProject, artifacts); + super(project, jpsProject, artifacts, isForcedCompilation); myFiles = files; - myForcedCompilation = isForcedCompilation; myModules = new HashSet(); for (JpsModule module : modules) { myModules.add(module.getName()); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesScope.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesScope.java index b00ec3522bdf..768262268f47 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesScope.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/ModulesScope.java @@ -15,17 +15,14 @@ import java.util.Set; * Date: 9/17/11 */ public class ModulesScope extends CompileScope { - private final Set myModules; - private final boolean myForcedCompilation; public ModulesScope(Project project, JpsProject jpsProject, Set modules, Set artifacts, boolean isForcedCompilation) { - super(project, jpsProject, artifacts); + super(project, jpsProject, artifacts, isForcedCompilation); myModules = new HashSet(); for (JpsModule module : modules) { myModules.add(module.getName()); } - myForcedCompilation = isForcedCompilation; } public boolean isRecompilationForced(@NotNull String moduleName) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactFilesDelta.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactFilesDelta.java new file mode 100644 index 000000000000..59ccfd324195 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactFilesDelta.java @@ -0,0 +1,115 @@ +package org.jetbrains.jps.incremental.artifacts; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.io.IOUtil; +import gnu.trove.THashSet; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.util.*; + +/** + * @author nik + */ +public class ArtifactFilesDelta { + private final Set myDeletedPaths = Collections.synchronizedSet(new THashSet(FileUtil.PATH_HASHING_STRATEGY)); + private final Map> myPathsToRecompile = Collections.synchronizedMap(new HashMap>()); + + public void save(DataOutput out) throws IOException { + out.writeInt(myDeletedPaths.size()); + for (String path : myDeletedPaths) { + IOUtil.writeString(path, out); + } + out.writeInt(myPathsToRecompile.size()); + for (Map.Entry> entry : myPathsToRecompile.entrySet()) { + out.writeInt(entry.getKey()); + Set paths = entry.getValue(); + out.writeInt(paths.size()); + for (String path : paths) { + IOUtil.writeString(path, out); + } + } + } + + public void load(DataInput in) throws IOException { + myDeletedPaths.clear(); + int deletedCount = in.readInt(); + while (deletedCount-- > 0) { + myDeletedPaths.add(IOUtil.readString(in)); + } + myPathsToRecompile.clear(); + int changedCount = in.readInt(); + while (changedCount-- > 0) { + int rootIndex = in.readInt(); + int filesCount = in.readInt(); + Set changed = new THashSet(FileUtil.PATH_HASHING_STRATEGY); + while (filesCount-- > 0) { + changed.add(IOUtil.readString(in)); + } + myPathsToRecompile.put(rootIndex, changed); + } + } + + public void clearDeletedPaths() { + myDeletedPaths.clear(); + } + + public boolean hasChanges() { + return !myPathsToRecompile.isEmpty() || !myDeletedPaths.isEmpty(); + } + + public boolean markRecompile(Integer rootIndex, String filePath) { + boolean added = addToRecompile(rootIndex, filePath); + if (added) { + synchronized (myDeletedPaths) { + if (!myDeletedPaths.isEmpty()) { + myDeletedPaths.remove(filePath); + } + } + } + return added; + } + + private boolean addToRecompile(Integer rootIndex, String filePath) { + myDeletedPaths.remove(filePath); + synchronized (myPathsToRecompile) { + Set changed = myPathsToRecompile.get(rootIndex); + if (changed == null) { + changed = new THashSet(FileUtil.PATH_HASHING_STRATEGY); + myPathsToRecompile.put(rootIndex, changed); + } + return changed.add(filePath); + } + } + + public void addDeleted(String filePath) { + synchronized (myPathsToRecompile) { + for (Set paths : myPathsToRecompile.values()) { + paths.remove(filePath); + } + } + myDeletedPaths.add(filePath); + } + + @Nullable + public Set clearRecompile(int index) { + return myPathsToRecompile.remove(index); + } + + public Set getAndClearDeletedPaths() { + synchronized (myDeletedPaths) { + try { + return new HashSet(myDeletedPaths); + } + finally { + myDeletedPaths.clear(); + } + } + } + + public Map> getFilesToRecompile() { + return myPathsToRecompile; + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactRootId.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactRootId.java new file mode 100644 index 000000000000..676c7b1e8490 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactRootId.java @@ -0,0 +1,54 @@ +package org.jetbrains.jps.incremental.artifacts; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + * @author nik + */ +public class ArtifactRootId { + private final int myArtifactId; + private final int myRootIndex; + + public ArtifactRootId(int artifactId, int rootIndex) { + myArtifactId = artifactId; + myRootIndex = rootIndex; + } + + public ArtifactRootId(DataInput input) throws IOException { + myArtifactId = input.readInt(); + myRootIndex = input.readInt(); + } + + public int getArtifactId() { + return myArtifactId; + } + + public int getRootIndex() { + return myRootIndex; + } + + public void save(DataOutput out) throws IOException { + out.writeInt(myArtifactId); + out.writeInt(myRootIndex); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ArtifactRootId id = (ArtifactRootId)o; + return myArtifactId == id.myArtifactId && myRootIndex == id.myRootIndex; + } + + @Override + public int hashCode() { + return 31 * myArtifactId + myRootIndex; + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactRootsIndex.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactRootsIndex.java new file mode 100644 index 000000000000..0a8559c2fc43 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactRootsIndex.java @@ -0,0 +1,76 @@ +package org.jetbrains.jps.incremental.artifacts; + +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.Project; +import org.jetbrains.jps.ProjectPaths; +import org.jetbrains.jps.incremental.ModuleRootsIndex; +import org.jetbrains.jps.incremental.artifacts.builders.LayoutElementBuildersRegistry; +import org.jetbrains.jps.incremental.artifacts.instructions.*; +import org.jetbrains.jps.incremental.storage.BuildDataManager; +import org.jetbrains.jps.model.JpsModel; +import org.jetbrains.jps.model.artifact.JpsArtifact; +import org.jetbrains.jps.model.artifact.JpsArtifactService; +import org.jetbrains.jps.model.artifact.elements.JpsCompositePackagingElement; + +import java.io.File; +import java.util.*; + +/** + * @author nik + */ +public class ArtifactRootsIndex { + private MultiMap myRootToDescriptorMap; + private Map myIdToDescriptorMap; + private Map myInstructions; + + public ArtifactRootsIndex(JpsModel model, Project project, BuildDataManager manager, ModuleRootsIndex rootsIndex) { + myRootToDescriptorMap = new MultiMap(); + myInstructions = new HashMap(); + myIdToDescriptorMap = new HashMap(); + ArtifactsBuildData data = manager.getArtifactsBuildData(); + for (JpsArtifact artifact : JpsArtifactService.getInstance().getArtifacts(model.getProject())) { + int artifactId = data.getArtifactId(artifact); + ArtifactInstructionsBuilderImpl builder = new ArtifactInstructionsBuilderImpl(rootsIndex, project.getIgnoredFilePatterns(), artifactId, artifact.getName()); + final JpsCompositePackagingElement rootElement = artifact.getRootElement(); + ArtifactInstructionsBuilderContext context = new ArtifactInstructionsBuilderContextImpl(model, rootsIndex, new ProjectPaths(model.getProject())); + String outputPath = StringUtil.notNullize(artifact.getOutputPath());//todo[nik] implement simplified instructions generation which only collect roots + final CopyToDirectoryInstructionCreator instructionCreator = new CopyToDirectoryInstructionCreator(builder, outputPath); + LayoutElementBuildersRegistry.getInstance().generateInstructions(rootElement, instructionCreator, context); + myInstructions.put(artifact, builder); + for (Pair pair : builder.getInstructions()) { + ArtifactRootDescriptor descriptor = pair.getFirst(); + myRootToDescriptorMap.putValue(descriptor.getRootFile(), descriptor); + myIdToDescriptorMap.put(descriptor.getRootId(), descriptor); + } + } + } + + @NotNull + public Collection getDescriptors(@NotNull File file) { + File current = file; + Collection result = null; + while (current != null) { + Collection descriptors = myRootToDescriptorMap.get(current); + if (!descriptors.isEmpty()) { + if (result == null) { + result = descriptors; + } + else { + result = new ArrayList(result); + result.addAll(descriptors); + } + } + current = FileUtil.getParentFile(current); + } + return result != null ? result : Collections.emptyList(); + } + + @NotNull + public ArtifactInstructionsBuilder getInstructionsBuilder(@NotNull JpsArtifact artifact) { + return myInstructions.get(artifact); + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceFilesState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceFilesState.java index 9708faf52f97..897e84d43ce3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceFilesState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceFilesState.java @@ -1,20 +1,20 @@ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.IntArrayList; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.Project; -import org.jetbrains.jps.ProjectPaths; -import org.jetbrains.jps.incremental.ModuleRootsIndex; -import org.jetbrains.jps.incremental.artifacts.builders.LayoutElementBuildersRegistry; -import org.jetbrains.jps.incremental.artifacts.instructions.*; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.cmdline.ProjectDescriptor; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactInstructionsBuilder; +import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactRootDescriptor; +import org.jetbrains.jps.incremental.artifacts.instructions.DestinationInfo; +import org.jetbrains.jps.incremental.artifacts.instructions.SourceFileFilter; +import org.jetbrains.jps.incremental.fs.BuildFSState; +import org.jetbrains.jps.incremental.messages.UptoDateFilesSavedEvent; import org.jetbrains.jps.incremental.storage.BuildDataManager; import org.jetbrains.jps.incremental.storage.CompositeStorageOwner; import org.jetbrains.jps.incremental.storage.StorageOwner; -import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.artifact.JpsArtifact; -import org.jetbrains.jps.model.artifact.elements.JpsCompositePackagingElement; import java.io.File; import java.io.IOException; @@ -24,28 +24,19 @@ import java.util.*; * @author nik */ public class ArtifactSourceFilesState extends CompositeStorageOwner { - private final Project myProject; private final JpsArtifact myArtifact; private final int myArtifactId; - private final JpsModel myModel; - private final ModuleRootsIndex myRootsIndex; private final ArtifactSourceTimestampStorage myTimestampStorage; - private Map myChangedFiles = new HashMap(); - private Set myDeletedFiles = new HashSet(); - private ArtifactInstructionsBuilder myInstructionsBuilder; private ArtifactSourceToOutputMapping mySrcOutMapping; private ArtifactOutputToSourceMapping myOutSrcMapping; private final File mySrcOutMappingsFile; private File myOutSrcMappingsFile; + private final ProjectDescriptor myProjectDescriptor; - public ArtifactSourceFilesState(JpsArtifact artifact, int artifactId, Project project, - JpsModel model, ModuleRootsIndex rootsIndex, - ArtifactSourceTimestampStorage timestampStorage, + public ArtifactSourceFilesState(JpsArtifact artifact, int artifactId, ProjectDescriptor projectDescriptor, ArtifactSourceTimestampStorage timestampStorage, File mappingsDir) { - myProject = project; + myProjectDescriptor = projectDescriptor; myArtifact = artifact; - myModel = model; - myRootsIndex = rootsIndex; myTimestampStorage = timestampStorage; myArtifactId = artifactId; mySrcOutMappingsFile = new File(new File(mappingsDir, String.valueOf(artifactId)), "src-out"); @@ -71,39 +62,42 @@ public class ArtifactSourceFilesState extends CompositeStorageOwner { return Arrays.asList(mySrcOutMapping, myOutSrcMapping); } - public Map getChangedFiles() { - return myChangedFiles; - } - - public Set getDeletedFiles() { - return myDeletedFiles; - } - - public void initState(final BuildDataManager dataManager) throws IOException { - final Set currentPaths = new HashSet(); - myChangedFiles.clear(); - myDeletedFiles.clear(); - getOrCreateInstructions().processRoots(new ArtifactRootProcessor() { - @Override - public boolean process(ArtifactSourceRoot root, DestinationInfo destinations) throws IOException { - final File rootFile = root.getRootFile(); - if (rootFile.exists()) { - processRecursively(rootFile, root.getRootIndex(), dataManager, root.getFilter(), currentPaths); + public void ensureFsStateInitialized(final BuildDataManager dataManager, final CompileContext context) throws IOException { + ArtifactInstructionsBuilder builder = myProjectDescriptor.getArtifactRootsIndex().getInstructionsBuilder(myArtifact); + BuildFSState fsState = myProjectDescriptor.fsState; + String artifactName = myArtifact.getName(); + if (context.isProjectRebuild() || context.getScope().isRecompilationForced(myArtifact)) { + markDirtyFiles(builder, dataManager, null, true); + } + else if (fsState.markInitialScanPerformed(artifactName)) { + final Set currentPaths = new HashSet(); + fsState.clearDeletedPaths(artifactName); + markDirtyFiles(builder, dataManager, currentPaths, false); + final ArtifactSourceToOutputMapping mapping = getOrCreateSrcOutMapping(); + final Iterator iterator = mapping.getKeysIterator(); + while (iterator.hasNext()) { + String path = iterator.next(); + if (!currentPaths.contains(path)) { + fsState.registerDeleted(artifactName, myArtifactId, path, myTimestampStorage); } - return true; - } - }); - final ArtifactSourceToOutputMapping mapping = getOrCreateSrcOutMapping(); - final Iterator iterator = mapping.getKeysIterator(); - while (iterator.hasNext()) { - String path = iterator.next(); - if (!currentPaths.contains(path)) { - myDeletedFiles.add(path); } } } - private void processRecursively(File file, int rootIndex, BuildDataManager dataManager, SourceFileFilter filter, Set currentPaths) throws IOException { + private void markDirtyFiles(ArtifactInstructionsBuilder builder, BuildDataManager dataManager, @Nullable Set currentPaths, + final boolean forceMarkDirty) throws IOException { + for (Pair pair : builder.getInstructions()) { + ArtifactRootDescriptor descriptor = pair.getFirst(); + myProjectDescriptor.fsState.clearRecompile(descriptor); + final File rootFile = descriptor.getRootFile(); + if (rootFile.exists()) { + processRecursively(rootFile, descriptor, dataManager, descriptor.getFilter(), currentPaths, forceMarkDirty); + } + } + } + + private void processRecursively(File file, ArtifactRootDescriptor descriptor, BuildDataManager dataManager, SourceFileFilter filter, + @Nullable Set currentPaths, final boolean forceMarkDirty) throws IOException { final String filePath = FileUtil.toSystemIndependentName(FileUtil.toCanonicalPath(file.getPath())); if (!filter.accept(filePath, dataManager)) return; @@ -111,15 +105,17 @@ public class ArtifactSourceFilesState extends CompositeStorageOwner { final File[] children = file.listFiles(); if (children != null) { for (File child : children) { - processRecursively(child, rootIndex, dataManager, filter, currentPaths); + processRecursively(child, descriptor, dataManager, filter, currentPaths, forceMarkDirty); } } } else { - currentPaths.add(filePath); + if (currentPaths != null) { + currentPaths.add(filePath); + } final ArtifactSourceTimestampStorage.PerArtifactTimestamp[] state = myTimestampStorage.getState(filePath); boolean upToDate = false; - if (state != null) { + if (!forceMarkDirty && state != null) { for (ArtifactSourceTimestampStorage.PerArtifactTimestamp artifactTimestamp : state) { if (artifactTimestamp.myArtifactId == myArtifactId && artifactTimestamp.myTimestamp == file.lastModified()) { upToDate = true; @@ -128,70 +124,24 @@ public class ArtifactSourceFilesState extends CompositeStorageOwner { } } if (!upToDate) { - myDeletedFiles.remove(filePath); - IntArrayList list = myChangedFiles.get(filePath); - if (list == null) { - list = new IntArrayList(1); - myChangedFiles.put(filePath, list); - } - list.add(rootIndex); + myProjectDescriptor.fsState.markDirty(descriptor, filePath, myTimestampStorage); } } } - public ArtifactInstructionsBuilder getOrCreateInstructions() { - if (myInstructionsBuilder == null) { - myInstructionsBuilder = computeInstructions(); + public void markUpToDate(CompileContext context) throws IOException { + BuildFSState fsState = myProjectDescriptor.fsState; + if (context.isProjectRebuild()) { + fsState.markInitialScanPerformed(myArtifact.getName()); } - return myInstructionsBuilder; - } - - private ArtifactInstructionsBuilder computeInstructions() { - final JpsCompositePackagingElement rootElement = myArtifact.getRootElement(); - ArtifactInstructionsBuilderContext context = new ArtifactInstructionsBuilderContextImpl(myModel, myRootsIndex, new ProjectPaths(myModel.getProject())); - final ArtifactInstructionsBuilderImpl instructionsBuilder = new ArtifactInstructionsBuilderImpl(myRootsIndex, myProject.getIgnoredFilePatterns()); - final CopyToDirectoryInstructionCreator instructionCreator = new CopyToDirectoryInstructionCreator(instructionsBuilder, myArtifact.getOutputPath()); - LayoutElementBuildersRegistry.getInstance().generateInstructions(rootElement, instructionCreator, context); - return instructionsBuilder; - } - - public void updateTimestamps() throws IOException { - for (String filePath : myDeletedFiles) { - final ArtifactSourceTimestampStorage.PerArtifactTimestamp[] state = myTimestampStorage.getState(filePath); - if (state == null) continue; - for (int i = 0, length = state.length; i < length; i++) { - if (state[i].myArtifactId == myArtifactId) { - final ArtifactSourceTimestampStorage.PerArtifactTimestamp[] newState = ArrayUtil.remove(state, i); - myTimestampStorage.update(filePath, newState.length > 0 ? newState : null); - break; - } - } + ArtifactInstructionsBuilder builder = myProjectDescriptor.getArtifactRootsIndex().getInstructionsBuilder(myArtifact); + boolean marked = false; + for (Pair pair : builder.getInstructions()) { + ArtifactRootDescriptor descriptor = pair.getFirst(); + marked |= fsState.markAllUpToDate(descriptor, myTimestampStorage, context.getCompilationStartStamp()); } - for (String filePath : myChangedFiles.keySet()) { - final ArtifactSourceTimestampStorage.PerArtifactTimestamp[] state = myTimestampStorage.getState(filePath); - File file = new File(FileUtil.toSystemDependentName(filePath)); - final long timestamp = file.lastModified(); - myTimestampStorage.update(filePath, updateTimestamp(state, timestamp)); + if (marked) { + context.processMessage(UptoDateFilesSavedEvent.INSTANCE); } } - - public void markUpToDate() { - myDeletedFiles.clear(); - myChangedFiles.clear(); - } - - @NotNull - private ArtifactSourceTimestampStorage.PerArtifactTimestamp[] updateTimestamp(ArtifactSourceTimestampStorage.PerArtifactTimestamp[] oldState, long timestamp) { - final ArtifactSourceTimestampStorage.PerArtifactTimestamp newItem = new ArtifactSourceTimestampStorage.PerArtifactTimestamp(myArtifactId, timestamp); - if (oldState == null) { - return new ArtifactSourceTimestampStorage.PerArtifactTimestamp[]{newItem}; - } - for (int i = 0, length = oldState.length; i < length; i++) { - if (oldState[i].myArtifactId == myArtifactId) { - oldState[i] = newItem; - return oldState; - } - } - return ArrayUtil.append(oldState, newItem); - } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceTimestampStorage.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceTimestampStorage.java index 39e39efc6680..cfe9f6745bcf 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceTimestampStorage.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactSourceTimestampStorage.java @@ -1,8 +1,10 @@ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.util.ArrayUtil; import com.intellij.util.io.DataExternalizer; import com.intellij.util.io.EnumeratorStringDescriptor; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.incremental.storage.AbstractStateStorage; import java.io.DataInput; @@ -47,6 +49,40 @@ public class ArtifactSourceTimestampStorage extends AbstractStateStorage 0 ? newState : null); + break; + } + } + } + + public void update(final int artifactId, String filePath, long timestamp) throws IOException { + PerArtifactTimestamp[] oldState = getState(filePath); + update(filePath, updateTimestamp(oldState, timestamp, artifactId)); + } + + @NotNull + private static PerArtifactTimestamp[] updateTimestamp(PerArtifactTimestamp[] oldState, + long timestamp, final int artifactId) { + final PerArtifactTimestamp newItem = new PerArtifactTimestamp( + artifactId, timestamp); + if (oldState == null) { + return new PerArtifactTimestamp[]{newItem}; + } + for (int i = 0, length = oldState.length; i < length; i++) { + if (oldState[i].myArtifactId == artifactId) { + oldState[i] = newItem; + return oldState; + } + } + return ArrayUtil.append(oldState, newItem); + } + public static class PerArtifactTimestamp { public final int myArtifactId; public final long myTimestamp; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactsBuildData.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactsBuildData.java index a9fbbab4efe0..c1d939c4fcc2 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactsBuildData.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/ArtifactsBuildData.java @@ -2,11 +2,9 @@ package org.jetbrains.jps.incremental.artifacts; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.jps.Project; -import org.jetbrains.jps.incremental.ModuleRootsIndex; +import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.storage.CompositeStorageOwner; import org.jetbrains.jps.incremental.storage.StorageOwner; -import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.artifact.JpsArtifact; import java.io.File; @@ -36,16 +34,20 @@ public class ArtifactsBuildData extends CompositeStorageOwner { } } - public ArtifactSourceFilesState getOrCreateState(JpsArtifact artifact, Project project, JpsModel model, ModuleRootsIndex index) { + public ArtifactSourceFilesState getOrCreateState(JpsArtifact artifact, ProjectDescriptor projectDescriptor) { ArtifactSourceFilesState state = myArtifactState.get(artifact); if (state == null) { - final int artifactId = myPersistentData.getId(artifact.getName()); - state = new ArtifactSourceFilesState(artifact, artifactId, project, model, index, myTimestampStorage, myMappingsDir); + final int artifactId = getArtifactId(artifact); + state = new ArtifactSourceFilesState(artifact, artifactId, projectDescriptor, myTimestampStorage, myMappingsDir); myArtifactState.put(artifact, state); } return state; } + public int getArtifactId(JpsArtifact artifact) { + return myPersistentData.getId(artifact.getName()); + } + public void clean() throws IOException { myTimestampStorage.wipe(); myPersistentData.clean(); @@ -71,4 +73,8 @@ public class ArtifactsBuildData extends CompositeStorageOwner { protected Iterable getChildStorages() { return ContainerUtil.concat(myArtifactState.values(), Arrays.asList(myTimestampStorage, myPersistentData)); } + + public ArtifactSourceTimestampStorage getTimestampStorage() { + return myTimestampStorage; + } } 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 5419a1453ee7..353b7bf33334 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 @@ -1,22 +1,20 @@ package org.jetbrains.jps.incremental.artifacts; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.containers.IntArrayList; import com.intellij.util.containers.MultiMap; import gnu.trove.THashSet; import gnu.trove.TIntObjectHashMap; import org.jetbrains.jps.cmdline.ProjectDescriptor; -import org.jetbrains.jps.incremental.CompileContext; -import org.jetbrains.jps.incremental.ProjectBuildException; -import org.jetbrains.jps.incremental.ProjectLevelBuilder; +import org.jetbrains.jps.incremental.*; import org.jetbrains.jps.incremental.artifacts.impl.ArtifactSorter; import org.jetbrains.jps.incremental.artifacts.impl.JarsBuilder; import org.jetbrains.jps.incremental.artifacts.instructions.*; +import org.jetbrains.jps.incremental.fs.BuildFSState; 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.messages.UptoDateFilesSavedEvent; import org.jetbrains.jps.model.artifact.JpsArtifact; import org.jetbrains.jps.model.artifact.JpsArtifactService; @@ -67,11 +65,12 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { private static void buildArtifact(JpsArtifact artifact, final CompileContext context) throws ProjectBuildException { final ProjectDescriptor pd = context.getProjectDescriptor(); try { - final ArtifactSourceFilesState state = pd.dataManager.getArtifactsBuildData().getOrCreateState(artifact, pd.project, pd.jpsModel, pd.rootsIndex); - state.initState(pd.dataManager); - final Set deletedFiles = state.getDeletedFiles(); - final Map changedFiles = state.getChangedFiles(); - if (deletedFiles.isEmpty() && changedFiles.isEmpty()) { + final ArtifactSourceFilesState state = pd.dataManager.getArtifactsBuildData().getOrCreateState(artifact, pd); + state.ensureFsStateInitialized(pd.dataManager, context); + final Collection deletedFiles = pd.fsState.getAndClearDeletedPaths(artifact.getName()); + final Map> filesToRecompile = pd.fsState.getFilesToRecompile(artifact.getName()); + if (deletedFiles.isEmpty() && filesToRecompile.isEmpty()) { + state.markUpToDate(context); return; } @@ -97,27 +96,28 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { } Set changedOutputPaths = new THashSet(); - for (Map.Entry entry : changedFiles.entrySet()) { - final IntArrayList roots = entry.getValue(); - final String sourcePath = entry.getKey(); - for (int i = 0; i < roots.size(); i++) { - addFileToProcess(filesToProcess, roots.get(i), sourcePath, deletedFiles); - } - final List outputPaths = srcOutMapping.getState(sourcePath); - if (outputPaths != null) { - changedOutputPaths.addAll(outputPaths); - for (String outputPath : outputPaths) { - final List sources = outSrcMapping.getState(outputPath); - if (sources != null) { - for (ArtifactOutputToSourceMapping.SourcePathAndRootIndex source : sources) { - addFileToProcess(filesToProcess, source.getRootIndex(), source.getPath(), deletedFiles); + for (Map.Entry> entry : filesToRecompile.entrySet()) { + int rootIndex = entry.getKey(); + for (String sourcePath : entry.getValue()) { + addFileToProcess(filesToProcess, rootIndex, sourcePath, deletedFiles); + final List outputPaths = srcOutMapping.getState(sourcePath); + if (outputPaths != null) { + changedOutputPaths.addAll(outputPaths); + for (String outputPath : outputPaths) { + final List sources = outSrcMapping.getState(outputPath); + if (sources != null) { + for (ArtifactOutputToSourceMapping.SourcePathAndRootIndex source : sources) { + addFileToProcess(filesToProcess, source.getRootIndex(), source.getPath(), deletedFiles); + } } } } } } - for (String sourcePath : changedFiles.keySet()) { - srcOutMapping.remove(sourcePath); + for (Set paths : filesToRecompile.values()) { + for (String sourcePath : paths) { + srcOutMapping.remove(sourcePath); + } } for (String outputPath : changedOutputPaths) { outSrcMapping.remove(outputPath); @@ -126,19 +126,20 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { deleteOutdatedFiles(filesToDelete, context, srcOutMapping, outSrcMapping); context.checkCanceled(); - final ArtifactInstructionsBuilder instructions = state.getOrCreateInstructions(); + final ArtifactInstructionsBuilder instructions = pd.getArtifactRootsIndex().getInstructionsBuilder(artifact); final Set changedJars = new THashSet(); instructions.processRoots(new ArtifactRootProcessor() { @Override - public boolean process(ArtifactSourceRoot root, DestinationInfo destination) throws IOException { + public boolean process(ArtifactRootDescriptor descriptor, DestinationInfo destination) throws IOException { if (context.getCancelStatus().isCanceled()) return false; - final Set sourcePaths = filesToProcess.get(root.getRootIndex()); + final Set sourcePaths = filesToProcess.get(descriptor.getRootIndex()); if (sourcePaths == null) return true; for (String sourcePath : sourcePaths) { if (destination instanceof ExplodedDestinationInfo) { - root.copyFromRoot(sourcePath, root.getRootIndex(), destination.getOutputPath(), context, srcOutMapping, outSrcMapping); + descriptor.copyFromRoot(sourcePath, descriptor.getRootIndex(), destination.getOutputPath(), context, + srcOutMapping, outSrcMapping); } else if (outSrcMapping.getState(destination.getOutputFilePath()) == null) { outSrcMapping.update(destination.getOutputFilePath(), Collections.emptyList()); @@ -152,13 +153,9 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { JarsBuilder builder = new JarsBuilder(changedJars, context, srcOutMapping, outSrcMapping, instructions); final boolean processed = builder.buildJars(); - if (!processed) { - return; + if (processed && !Utils.errorsDetected(context) && !context.getCancelStatus().isCanceled()) { + state.markUpToDate(context); } - - state.updateTimestamps(); - state.markUpToDate(); - context.processMessage(UptoDateFilesSavedEvent.INSTANCE); } catch (IOException e) { throw new ProjectBuildException(e); @@ -168,7 +165,7 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { private static void addFileToProcess(TIntObjectHashMap> filesToProcess, final int rootIndex, final String path, - Set deletedFiles) { + Collection deletedFiles) { if (deletedFiles.contains(path)) { return; } @@ -219,6 +216,35 @@ public class IncArtifactBuilder extends ProjectLevelBuilder { } } + @Override + public void buildStarted(final CompileContext context) { + context.addBuildListener(new BuildListener() { + @Override + public void filesGenerated(Collection> paths) { + BuildFSState fsState = context.getProjectDescriptor().fsState; + ArtifactRootsIndex rootsIndex = context.getProjectDescriptor().getArtifactRootsIndex(); + for (Pair pair : paths) { + File file = new File(pair.getFirst(), pair.getSecond()); + for (ArtifactRootDescriptor descriptor : rootsIndex.getDescriptors(file)) { + fsState.markRecompile(descriptor, FileUtil.toSystemIndependentName(file.getAbsolutePath())); + } + } + } + + @Override + public void filesDeleted(Collection paths) { + BuildFSState state = context.getProjectDescriptor().fsState; + ArtifactRootsIndex index = context.getProjectDescriptor().getArtifactRootsIndex(); + for (String path : paths) { + File file = new File(FileUtil.toSystemDependentName(path)); + for (ArtifactRootDescriptor descriptor : index.getDescriptors(file)) { + state.registerDeleted(descriptor.getArtifactName(), path); + } + } + } + }); + } + @Override public String getName() { return BUILDER_NAME; diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java index f8ab346c98d0..6bc6206f3f6b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java @@ -33,10 +33,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.JpsPathUtil; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ProjectBuildException; -import org.jetbrains.jps.incremental.artifacts.ArtifactBuilderLogger; -import org.jetbrains.jps.incremental.artifacts.ArtifactOutputToSourceMapping; -import org.jetbrains.jps.incremental.artifacts.ArtifactSourceToOutputMapping; -import org.jetbrains.jps.incremental.artifacts.IncArtifactBuilder; +import org.jetbrains.jps.incremental.artifacts.*; import org.jetbrains.jps.incremental.artifacts.instructions.*; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; @@ -162,21 +159,21 @@ public class JarsBuilder { for (Pair pair : jar.getContent()) { final String relativePath = pair.getFirst(); - if (pair.getSecond() instanceof ArtifactSourceRoot) { - final ArtifactSourceRoot root = (ArtifactSourceRoot)pair.getSecond(); - final int rootIndex = root.getRootIndex(); + if (pair.getSecond() instanceof ArtifactRootDescriptor) { + final ArtifactRootDescriptor descriptor = (ArtifactRootDescriptor)pair.getSecond(); + final int rootIndex = descriptor.getRootIndex(); final ArtifactBuilderLogger logger = myContext.getLoggingManager().getArtifactBuilderLogger(); - if (root instanceof FileBasedArtifactSourceRoot) { - addFileToJar(jarOutputStream, jarFile, root.getRootFile(), root.getFilter(), relativePath, targetJarPath, writtenPaths, + if (descriptor instanceof FileBasedArtifactRootDescriptor) { + addFileToJar(jarOutputStream, jarFile, descriptor.getRootFile(), descriptor.getFilter(), relativePath, targetJarPath, writtenPaths, rootIndex); } else { - final String filePath = FileUtil.toSystemIndependentName(root.getRootFile().getAbsolutePath()); + final String filePath = FileUtil.toSystemIndependentName(descriptor.getRootFile().getAbsolutePath()); logger.fileCopied(filePath); mySrcOutMapping.appendData(filePath, Collections.singletonList(targetJarPath)); myOutSrcMapping.appendData(targetJarPath, Collections .singletonList(new ArtifactOutputToSourceMapping.SourcePathAndRootIndex(filePath, rootIndex))); - extractFileAndAddToJar(jarOutputStream, (JarBasedArtifactSourceRoot)root, relativePath, writtenPaths); + extractFileAndAddToJar(jarOutputStream, (JarBasedArtifactRootDescriptor)descriptor, relativePath, writtenPaths); } } else { @@ -207,15 +204,15 @@ public class JarsBuilder { @Nullable private Manifest loadManifest(JarInfo jar, String targetJarPath) throws IOException { for (Pair pair : jar.getContent()) { - if (pair.getSecond() instanceof ArtifactSourceRoot) { + if (pair.getSecond() instanceof ArtifactRootDescriptor) { final String rootPath = pair.getFirst(); if (!JarFile.MANIFEST_NAME.startsWith(rootPath)) { continue; } final String manifestPath = JpsPathUtil.trimForwardSlashes(JarFile.MANIFEST_NAME.substring(rootPath.length())); - final ArtifactSourceRoot root = (ArtifactSourceRoot)pair.getSecond(); - if (root instanceof FileBasedArtifactSourceRoot) { - final File manifestFile = new File(root.getRootFile(), manifestPath); + final ArtifactRootDescriptor descriptor = (ArtifactRootDescriptor)pair.getSecond(); + if (descriptor instanceof FileBasedArtifactRootDescriptor) { + final File manifestFile = new File(descriptor.getRootFile(), manifestPath); if (manifestFile.exists()) { final String fullManifestPath = FileUtil.toSystemIndependentName(manifestFile.getAbsolutePath()); myContext.getLoggingManager().getArtifactBuilderLogger().fileCopied(fullManifestPath); @@ -226,11 +223,11 @@ public class JarsBuilder { } else { final Ref manifestRef = Ref.create(null); - ((JarBasedArtifactSourceRoot)root).processEntries(new JarBasedArtifactSourceRoot.EntryProcessor() { + ((JarBasedArtifactRootDescriptor)descriptor).processEntries(new JarBasedArtifactRootDescriptor.EntryProcessor() { @Override public void process(@Nullable InputStream inputStream, @NotNull String relativePath) throws IOException { if (manifestRef.isNull() && relativePath.equals(manifestPath) && inputStream != null) { - manifestRef.set(createManifest(inputStream, root.getRootFile())); + manifestRef.set(createManifest(inputStream, descriptor.getRootFile())); } } }); @@ -261,11 +258,11 @@ public class JarsBuilder { } } - private static void extractFileAndAddToJar(final JarOutputStream jarOutputStream, final JarBasedArtifactSourceRoot root, + private static void extractFileAndAddToJar(final JarOutputStream jarOutputStream, final JarBasedArtifactRootDescriptor root, final String relativeOutputPath, final Set writtenPaths) throws IOException { final long timestamp = root.getRootFile().lastModified(); - root.processEntries(new JarBasedArtifactSourceRoot.EntryProcessor() { + root.processEntries(new JarBasedArtifactRootDescriptor.EntryProcessor() { @Override public void process(@Nullable InputStream inputStream, @NotNull String relativePath) throws IOException { String pathInJar = addParentDirectories(jarOutputStream, writtenPaths, JpsPathUtil.appendToPath(relativeOutputPath, relativePath)); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java index 98b77d9a592f..101b16e9b45b 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactCompilerInstructionCreatorBase.java @@ -55,7 +55,7 @@ public abstract class ArtifactCompilerInstructionCreatorBase implements Artifact addDirectoryCopyInstructions(myInstructionsBuilder.createJarBasedRoot(jarFile, pathInJar, filter)); } - protected abstract void addDirectoryCopyInstructions(ArtifactSourceRoot root); + protected abstract void addDirectoryCopyInstructions(ArtifactRootDescriptor descriptor); @Override public abstract ArtifactCompilerInstructionCreatorBase subFolder(@NotNull String directoryName); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilder.java index b76651b760c1..4c86d0fe7a20 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilder.java @@ -1,10 +1,15 @@ package org.jetbrains.jps.incremental.artifacts.instructions; +import com.intellij.openapi.util.Pair; + import java.io.IOException; +import java.util.List; /** * @author nik */ public interface ArtifactInstructionsBuilder { void processRoots(ArtifactRootProcessor processor) throws IOException; + + List> getInstructions(); } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java index e71fae026463..ed743b6b4687 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactInstructionsBuilderImpl.java @@ -18,29 +18,33 @@ import java.util.Map; */ public class ArtifactInstructionsBuilderImpl implements ArtifactInstructionsBuilder { private final Map myJarByPath; - private final List> myInstructions; + private final List> myInstructions; private final ModuleRootsIndex myRootsIndex; private final IgnoredFilePatterns myIgnoredFilePatterns; + private final int myArtifactId; + private final String myArtifactName; private int myRootIndex; - public ArtifactInstructionsBuilderImpl(ModuleRootsIndex rootsIndex, IgnoredFilePatterns patterns) { + public ArtifactInstructionsBuilderImpl(ModuleRootsIndex rootsIndex, IgnoredFilePatterns patterns, int artifactId, String artifactName) { myRootsIndex = rootsIndex; myIgnoredFilePatterns = patterns; + myArtifactId = artifactId; + myArtifactName = artifactName; myJarByPath = new HashMap(); - myInstructions = new ArrayList>(); + myInstructions = new ArrayList>(); } public IgnoredFilePatterns getIgnoredFilePatterns() { return myIgnoredFilePatterns; } - public boolean addDestination(@NotNull ArtifactSourceRoot root, @NotNull DestinationInfo destinationInfo) { - if (destinationInfo instanceof ExplodedDestinationInfo && root instanceof FileBasedArtifactSourceRoot - && root.getRootFile().equals(new File(FileUtil.toSystemDependentName(destinationInfo.getOutputFilePath())))) { + public boolean addDestination(@NotNull ArtifactRootDescriptor descriptor, @NotNull DestinationInfo destinationInfo) { + if (destinationInfo instanceof ExplodedDestinationInfo && descriptor instanceof FileBasedArtifactRootDescriptor + && descriptor.getRootFile().equals(new File(FileUtil.toSystemDependentName(destinationInfo.getOutputFilePath())))) { return false; } - myInstructions.add(Pair.create(root, destinationInfo)); + myInstructions.add(Pair.create(descriptor, destinationInfo)); return true; } @@ -58,21 +62,26 @@ public class ArtifactInstructionsBuilderImpl implements ArtifactInstructionsBuil @Override public void processRoots(ArtifactRootProcessor processor) throws IOException { - for (Pair pair : myInstructions) { + for (Pair pair : myInstructions) { if (!processor.process(pair.getFirst(), pair.getSecond())) { break; } } } - public FileBasedArtifactSourceRoot createFileBasedRoot(@NotNull File file, - @NotNull SourceFileFilter filter) { - return new FileBasedArtifactSourceRoot(file, filter, myRootIndex++); + @Override + public List> getInstructions() { + return myInstructions; } - public JarBasedArtifactSourceRoot createJarBasedRoot(@NotNull File jarFile, + public FileBasedArtifactRootDescriptor createFileBasedRoot(@NotNull File file, + @NotNull SourceFileFilter filter) { + return new FileBasedArtifactRootDescriptor(file, filter, myRootIndex++, myArtifactId, myArtifactName); + } + + public JarBasedArtifactRootDescriptor createJarBasedRoot(@NotNull File jarFile, @NotNull String pathInJar, @NotNull SourceFileFilter filter) { - return new JarBasedArtifactSourceRoot(jarFile, pathInJar, filter, myRootIndex++); + return new JarBasedArtifactRootDescriptor(jarFile, pathInJar, filter, myRootIndex, myArtifactId, myArtifactName); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactSourceRoot.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootDescriptor.java similarity index 56% rename from jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactSourceRoot.java rename to jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootDescriptor.java index db4a510b2da5..81031f4d385a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactSourceRoot.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootDescriptor.java @@ -3,6 +3,7 @@ package org.jetbrains.jps.incremental.artifacts.instructions; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.artifacts.ArtifactOutputToSourceMapping; +import org.jetbrains.jps.incremental.artifacts.ArtifactRootId; import org.jetbrains.jps.incremental.artifacts.ArtifactSourceToOutputMapping; import java.io.File; @@ -11,17 +12,31 @@ import java.io.IOException; /** * @author nik */ -public abstract class ArtifactSourceRoot { +public abstract class ArtifactRootDescriptor { + protected final File myRoot; private final SourceFileFilter myFilter; - private final int myRootIndex; + private final ArtifactRootId myRootId; + private final String myArtifactName; - protected ArtifactSourceRoot(@NotNull SourceFileFilter filter, int index) { + protected ArtifactRootDescriptor(File root, @NotNull SourceFileFilter filter, int index, int artifactId, String artifactName) { + myRoot = root; myFilter = filter; - myRootIndex = index; + myArtifactName = artifactName; + myRootId = new ArtifactRootId(artifactId, index); + } + + public final String getArtifactName() { + return myArtifactName; + } + + public final ArtifactRootId getRootId() { + return myRootId; } @NotNull - public abstract File getRootFile(); + public final File getRootFile() { + return myRoot; + } public abstract void copyFromRoot(String filePath, int rootIndex, String outputPath, @@ -33,6 +48,6 @@ public abstract class ArtifactSourceRoot { } public int getRootIndex() { - return myRootIndex; + return myRootId.getRootIndex(); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootProcessor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootProcessor.java index 66c278688176..1446005f57da 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootProcessor.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/ArtifactRootProcessor.java @@ -1,11 +1,10 @@ package org.jetbrains.jps.incremental.artifacts.instructions; import java.io.IOException; -import java.util.Collection; /** * @author nik */ public interface ArtifactRootProcessor { - boolean process(ArtifactSourceRoot root, DestinationInfo destinations) throws IOException; + boolean process(ArtifactRootDescriptor descriptor, DestinationInfo destinations) throws IOException; } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/CopyToDirectoryInstructionCreator.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/CopyToDirectoryInstructionCreator.java index d7acc17c2970..12cf0acb83db 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/CopyToDirectoryInstructionCreator.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/CopyToDirectoryInstructionCreator.java @@ -35,8 +35,8 @@ public class CopyToDirectoryInstructionCreator extends ArtifactCompilerInstructi } @Override - protected void addDirectoryCopyInstructions(ArtifactSourceRoot root) { - myInstructionsBuilder.addDestination(root, new ExplodedDestinationInfo(myOutputPath)); + protected void addDirectoryCopyInstructions(ArtifactRootDescriptor descriptor) { + myInstructionsBuilder.addDestination(descriptor, new ExplodedDestinationInfo(myOutputPath)); } public CopyToDirectoryInstructionCreator subFolder(@NotNull String directoryName) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactSourceRoot.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactRootDescriptor.java similarity index 78% rename from jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactSourceRoot.java rename to jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactRootDescriptor.java index 7fcad2410594..c8293b1266c3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactSourceRoot.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/FileBasedArtifactRootDescriptor.java @@ -14,23 +14,18 @@ import java.util.Collections; /** * @author nik */ -public class FileBasedArtifactSourceRoot extends ArtifactSourceRoot { - private final File myFile; - - public FileBasedArtifactSourceRoot(@NotNull File file, @NotNull SourceFileFilter filter, int index) { - super(filter, index); - myFile = file; - } - - @NotNull - @Override - public File getRootFile() { - return myFile; +public class FileBasedArtifactRootDescriptor extends ArtifactRootDescriptor { + public FileBasedArtifactRootDescriptor(@NotNull File file, + @NotNull SourceFileFilter filter, + int index, + int artifactId, + String artifactName) { + super(file, filter, index, artifactId, artifactName); } @Override public String toString() { - return myFile.getPath(); + return myRoot.getPath(); } public void copyFromRoot(String filePath, diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactSourceRoot.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java similarity index 85% rename from jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactSourceRoot.java rename to jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java index 65dbf6a25960..7657e0180841 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactSourceRoot.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarBasedArtifactRootDescriptor.java @@ -19,25 +19,21 @@ import java.util.zip.ZipFile; /** * @author nik */ -public class JarBasedArtifactSourceRoot extends ArtifactSourceRoot { - private final File myJarFile; +public class JarBasedArtifactRootDescriptor extends ArtifactRootDescriptor { private final String myPathInJar; - public JarBasedArtifactSourceRoot(@NotNull File jarFile, @NotNull String pathInJar, @NotNull SourceFileFilter filter, int index) { - super(filter, index); - myJarFile = jarFile; + public JarBasedArtifactRootDescriptor(@NotNull File jarFile, + @NotNull String pathInJar, + @NotNull SourceFileFilter filter, + int index, + int artifactId, String artifactName) { + super(jarFile, filter, index, artifactId, artifactName); myPathInJar = pathInJar; } - @NotNull - @Override - public File getRootFile() { - return myJarFile; - } - @Override public String toString() { - return myJarFile.getPath() + JarPathUtil.JAR_SEPARATOR + myPathInJar; + return myRoot.getPath() + JarPathUtil.JAR_SEPARATOR + myPathInJar; } public void processEntries(EntryProcessor processor) throws IOException { @@ -47,7 +43,7 @@ public class JarBasedArtifactSourceRoot extends ArtifactSourceRoot { prefix = ""; } - ZipFile zipFile = new ZipFile(myJarFile); + ZipFile zipFile = new ZipFile(myRoot); try { final Enumeration entries = zipFile.entries(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarInfo.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarInfo.java index 27c1d5c45a01..54576b283363 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarInfo.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/JarInfo.java @@ -34,8 +34,8 @@ public class JarInfo { myContent = new ArrayList>(); } - public void addContent(String pathInJar, ArtifactSourceRoot sourceFile) { - myContent.add(Pair.create(pathInJar, (Object)sourceFile)); + public void addContent(String pathInJar, ArtifactRootDescriptor descriptor) { + myContent.add(Pair.create(pathInJar, (Object)descriptor)); } public void addJar(String pathInJar, JarInfo jarInfo) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/PackIntoArchiveInstructionCreator.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/PackIntoArchiveInstructionCreator.java index 9b006d987174..a6a052ab6a88 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/PackIntoArchiveInstructionCreator.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/PackIntoArchiveInstructionCreator.java @@ -36,17 +36,17 @@ public class PackIntoArchiveInstructionCreator extends ArtifactCompilerInstructi } @Override - protected void addDirectoryCopyInstructions(ArtifactSourceRoot root) { - addCopyInstruction(myPathInJar, root); + protected void addDirectoryCopyInstructions(ArtifactRootDescriptor descriptor) { + addCopyInstruction(myPathInJar, descriptor); } public void addFileCopyInstruction(@NotNull File file, @NotNull String outputFileName) { addCopyInstruction(childPathInJar(outputFileName), myInstructionsBuilder.createFileBasedRoot(file, SourceFileFilter.ALL)); } - private void addCopyInstruction(String pathInJar, final ArtifactSourceRoot root) { - if (myInstructionsBuilder.addDestination(root, new JarDestinationInfo(pathInJar, myJarInfo, myJarDestination))) { - myJarInfo.addContent(pathInJar, root); + private void addCopyInstruction(String pathInJar, final ArtifactRootDescriptor descriptor) { + if (myInstructionsBuilder.addDestination(descriptor, new JarDestinationInfo(pathInJar, myJarInfo, myJarDestination))) { + myJarInfo.addContent(pathInJar, descriptor); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SkipAllInstructionCreator.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SkipAllInstructionCreator.java index 41798ad19b28..a1ec40e864ef 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SkipAllInstructionCreator.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/instructions/SkipAllInstructionCreator.java @@ -31,7 +31,7 @@ public class SkipAllInstructionCreator extends ArtifactCompilerInstructionCreato } @Override - protected void addDirectoryCopyInstructions(ArtifactSourceRoot root) { + protected void addDirectoryCopyInstructions(ArtifactRootDescriptor descriptor) { } public SkipAllInstructionCreator subFolder(@NotNull String directoryName) { diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java index 4d1b30c3f996..7078e29cc5ca 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/BuildFSState.java @@ -11,6 +11,9 @@ import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.CompileScope; import org.jetbrains.jps.incremental.FileProcessor; import org.jetbrains.jps.incremental.Utils; +import org.jetbrains.jps.incremental.artifacts.ArtifactFilesDelta; +import org.jetbrains.jps.incremental.artifacts.ArtifactSourceTimestampStorage; +import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactRootDescriptor; import org.jetbrains.jps.incremental.storage.Timestamps; import org.jetbrains.jps.model.module.JpsModule; @@ -45,6 +48,11 @@ public class BuildFSState extends FSState { return myAlwaysScanFS || super.markInitialScanPerformed(moduleName, forTests); } + @Override + public boolean markInitialScanPerformed(String artifactName) { + return myAlwaysScanFS || super.markInitialScanPerformed(artifactName); + } + @Override public Map> getSourcesToRecompile(@NotNull CompileContext context, final String moduleName, boolean forTests) { final FilesDelta lastRoundDelta = getRoundDelta(LAST_ROUND_DELTA_KEY, context); @@ -54,6 +62,10 @@ public class BuildFSState extends FSState { return super.getSourcesToRecompile(context, moduleName, forTests); } + public Map> getFilesToRecompile(String artifactName) { + return getDelta(artifactName).getFilesToRecompile(); + } + @Override public boolean markDirty(@Nullable CompileContext context, File file, final RootDescriptor rd, @Nullable Timestamps tsStorage) throws IOException { final FilesDelta roundDelta = getRoundDelta(CURRENT_ROUND_DELTA_KEY, context); @@ -174,6 +186,27 @@ public class BuildFSState extends FSState { return marked; } + public boolean markAllUpToDate(ArtifactRootDescriptor descriptor, ArtifactSourceTimestampStorage storage, long compilationStartStamp) + throws IOException { + boolean marked = false; + ArtifactFilesDelta delta = getDelta(descriptor.getArtifactName()); + Set paths = delta.clearRecompile(descriptor.getRootIndex()); + if (paths != null) { + for (String path : paths) { + File file = new File(FileUtil.toSystemDependentName(path)); + long stamp = file.lastModified(); + if (stamp > compilationStartStamp + 1000) {//todo[nik] this is added to fix tests on Linux + delta.markRecompile(descriptor.getRootIndex(), path); + } + else { + marked = true; + storage.update(descriptor.getRootId().getArtifactId(), path, stamp); + } + } + } + return marked; + } + @NotNull private static Set getContextModules(@Nullable CompileContext context) { return context != null? CONTEXT_MODULES_KEY.get(context, Collections.emptySet()) : Collections.emptySet(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java index 1e7657b2dd37..fe37825d982d 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FSState.java @@ -4,9 +4,12 @@ import com.intellij.util.io.IOUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.artifacts.ArtifactFilesDelta; +import org.jetbrains.jps.incremental.artifacts.ArtifactSourceTimestampStorage; +import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactRootDescriptor; import org.jetbrains.jps.incremental.storage.Timestamps; -import java.io.DataInput; +import java.io.DataInputStream; import java.io.DataOutput; import java.io.File; import java.io.IOException; @@ -21,6 +24,8 @@ public class FSState { private final Map myTestDeltas = Collections.synchronizedMap(new HashMap()); protected final Set myInitialTestsScanPerformed = Collections.synchronizedSet(new HashSet()); protected final Set myInitialProductionScanPerformed = Collections.synchronizedSet(new HashSet()); + private final Map myArtifactDeltas = Collections.synchronizedMap(new HashMap()); + private final Set myArtifactInitialScanPerformed = Collections.synchronizedSet(new HashSet()); public void save(DataOutput out) throws IOException { out.writeInt(myInitialTestsScanPerformed.size()); @@ -34,9 +39,14 @@ public class FSState { IOUtil.writeString(moduleName, out); getDelta(moduleName, false).save(out); } + out.writeInt(myArtifactInitialScanPerformed.size()); + for (String artifactName : myArtifactInitialScanPerformed) { + IOUtil.writeString(artifactName, out); + getDelta(artifactName).save(out); + } } - public void load(DataInput in) throws IOException { + public void load(DataInputStream in) throws IOException { int testsDeltaCount = in.readInt(); while (testsDeltaCount-- > 0) { final String moduleName = IOUtil.readString(in); @@ -50,17 +60,41 @@ public class FSState { getDelta(moduleName, false).load(in); myInitialProductionScanPerformed.add(moduleName); } + if (in.available() > 0) {//todo[nik] remove this check after version change + int artifactRootsCount = in.readInt(); + while (artifactRootsCount-- > 0) { + String name = IOUtil.readString(in); + getDelta(name).load(in); + myArtifactInitialScanPerformed.add(name); + } + } + } + + protected ArtifactFilesDelta getDelta(String artifactName) { + synchronized (myArtifactDeltas) { + ArtifactFilesDelta delta = myArtifactDeltas.get(artifactName); + if (delta == null) { + delta = new ArtifactFilesDelta(); + myArtifactDeltas.put(artifactName, delta); + } + return delta; + } } public void clearAll() { myProductionDeltas.clear(); myTestDeltas.clear(); + myArtifactDeltas.clear(); } public final void clearRecompile(final RootDescriptor rd) { getDelta(rd.module, rd.isTestRoot).clearRecompile(rd.root); } + public void clearRecompile(ArtifactRootDescriptor descriptor) { + getDelta(descriptor.getArtifactName()).clearRecompile(descriptor.getRootIndex()); + } + public boolean markDirty(@Nullable CompileContext context, final File file, final RootDescriptor rd, final @Nullable Timestamps tsStorage) throws IOException { final FilesDelta mainDelta = getDelta(rd.module, rd.isTestRoot); final boolean marked = mainDelta.markRecompile(rd.root, file); @@ -81,6 +115,15 @@ public class FSState { return marked; } + public void markDirty(ArtifactRootDescriptor descriptor, String filePath, ArtifactSourceTimestampStorage timestamps) throws IOException { + markRecompile(descriptor, filePath); + timestamps.removeTimestamp(filePath, descriptor.getRootId().getArtifactId()); + } + + public void markRecompile(ArtifactRootDescriptor descriptor, String filePath) { + getDelta(descriptor.getArtifactName()).markRecompile(descriptor.getRootIndex(), filePath); + } + public void registerDeleted(final String moduleName, final File file, final boolean forTests, @Nullable Timestamps tsStorage) throws IOException { getDelta(moduleName, forTests).addDeleted(file); if (tsStorage != null) { @@ -88,6 +131,16 @@ public class FSState { } } + public void registerDeleted(final String artifactName, final int artifactId, String filePath, + ArtifactSourceTimestampStorage timestampStorage) throws IOException { + registerDeleted(artifactName, filePath); + timestampStorage.removeTimestamp(filePath, artifactId); + } + + public void registerDeleted(String artifactName, String filePath) { + getDelta(artifactName).addDeleted(filePath); + } + public Map> getSourcesToRecompile(@NotNull CompileContext context, final String moduleName, boolean forTests) { return getDelta(moduleName, forTests).getSourcesToRecompile(); } @@ -99,6 +152,13 @@ public class FSState { } } + public void clearDeletedPaths(String artifactName) { + ArtifactFilesDelta delta = myArtifactDeltas.get(artifactName); + if (delta != null) { + delta.clearDeletedPaths(); + } + } + private Map getDeltas(boolean forTests) { return forTests ? myTestDeltas : myProductionDeltas; } @@ -111,6 +171,14 @@ public class FSState { return Collections.emptyList(); } + public Collection getAndClearDeletedPaths(String artifactName) { + ArtifactFilesDelta delta = myArtifactDeltas.get(artifactName); + if (delta != null) { + return delta.getAndClearDeletedPaths(); + } + return Collections.emptyList(); + } + @NotNull protected final FilesDelta getDelta(final String moduleName, boolean forTests) { Map deltas = getDeltas(forTests); @@ -125,17 +193,26 @@ public class FSState { } public boolean hasWorkToDo() { - return hasWorkToDo(false) || hasWorkToDo(true); + return hasWorkToDoWithModules(false) || hasWorkToDoWithModules(true) || hasWorkToDoWithArtifacts(); } - private boolean hasWorkToDo(final boolean forTests) { + private boolean hasWorkToDoWithArtifacts() { + for (Map.Entry entry : myArtifactDeltas.entrySet()) { + if (!myArtifactInitialScanPerformed.contains(entry.getKey()) || entry.getValue().hasChanges()) { + return true; + } + } + return false; + } + + private boolean hasWorkToDoWithModules(final boolean forTests) { for (Map.Entry entry : getDeltas(forTests).entrySet()) { final String moduleName = entry.getKey(); - if (!myInitialProductionScanPerformed.contains(moduleName) || !myInitialTestsScanPerformed.contains(moduleName)) { + if (!getInitialScanPerformedSet(forTests).contains(moduleName)) { return true; } final FilesDelta delta = entry.getValue(); - if (delta.hasPathsToDelete() || delta.hasSourcesToRecompile()) { + if (delta.hasChanges()) { return true; } } @@ -143,6 +220,14 @@ public class FSState { } public boolean markInitialScanPerformed(final String moduleName, boolean forTests) { - return (forTests ? myInitialTestsScanPerformed : myInitialProductionScanPerformed).add(moduleName); + return getInitialScanPerformedSet(forTests).add(moduleName); + } + + private Set getInitialScanPerformedSet(boolean forTests) { + return forTests ? myInitialTestsScanPerformed : myInitialProductionScanPerformed; + } + + public boolean markInitialScanPerformed(String artifactName) { + return myArtifactInitialScanPerformed.add(artifactName); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java index e23bced10072..b4b68d4f65c3 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/fs/FilesDelta.java @@ -64,6 +64,10 @@ final class FilesDelta { } } + public boolean hasChanges() { + return hasPathsToDelete() || hasSourcesToRecompile(); + } + private static Set createSetOfFiles() { return new THashSet(new TObjectHashingStrategy() { @@ -151,7 +155,7 @@ final class FilesDelta { return myFilesToRecompile; } - public boolean hasSourcesToRecompile() { + private boolean hasSourcesToRecompile() { synchronized (myFilesToRecompile) { if(!myFilesToRecompile.isEmpty()) { for (Set files : myFilesToRecompile.values()) { @@ -164,16 +168,10 @@ final class FilesDelta { return false; } - public boolean hasPathsToDelete() { + private boolean hasPathsToDelete() { return !myDeletedPaths.isEmpty(); } - public Set getDeletedPaths() { - synchronized (myDeletedPaths) { - return myDeletedPaths.isEmpty()? Collections.emptySet() : new HashSet(myDeletedPaths); - } - } - @Nullable public Set clearRecompile(File root) { return myFilesToRecompile.remove(root); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/messages/FileDeletedEvent.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/messages/FileDeletedEvent.java new file mode 100644 index 000000000000..f33056538659 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/messages/FileDeletedEvent.java @@ -0,0 +1,19 @@ +package org.jetbrains.jps.incremental.messages; + +import java.util.Collection; + +/** + * @author nik + */ +public class FileDeletedEvent extends BuildMessage { + private Collection myFilePaths; + + public FileDeletedEvent(Collection filePaths) { + super("", Kind.INFO); + myFilePaths = filePaths; + } + + public Collection getFilePaths() { + return myFilePaths; + } +} 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 22968b3a63e6..812c07bc2808 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 @@ -286,6 +286,10 @@ public class AndroidDexBuilder extends ProjectLevelBuilder { dirtyOutputDirs.add(path.first); } } + + @Override + public void filesDeleted(Collection paths) { + } }; context.putUserData(DIRTY_OUTPUT_DIRS, dirtyOutputDirs); context.putUserData(BUILD_LISTENER_KEY, listener);