external artifact builder: get list of changed files from IDEA instead of scanning fs on each build

This commit is contained in:
nik
2012-07-30 15:11:32 +04:00
parent 64c920cb60
commit 0107c6c3b8
34 changed files with 733 additions and 287 deletions
@@ -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<ArtifactRootDescriptor> 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<ArtifactRootDescriptor> 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);
}
}
}
@@ -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<JpsTypedLibrary<JpsSdkProperties>> 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<JpsTypedLibrary<JpsSdkProperties>>();
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++;
}
@@ -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<JpsArtifact> 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) {
@@ -15,4 +15,6 @@ public interface BuildListener extends EventListener{
* @param paths collection of pairs [output root->relative path to generated file]
*/
void filesGenerated(Collection<Pair<String, String>> paths);
void filesDeleted(Collection<String> paths);
}
@@ -14,6 +14,6 @@ public abstract class Builder {
public void buildStarted(CompileContext context) {
}
public void buildFinished(CompileContext ICompileContext) {
public void buildFinished(CompileContext context) {
}
}
@@ -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<String> paths = ((FileDeletedEvent)msg).getFilePaths();
myListeners.getMulticaster().filesDeleted(paths);
}
}
@Override
@@ -19,17 +19,23 @@ public abstract class CompileScope {
private final Project myProject;
private final JpsProject myJpsProject;
private final Set<JpsArtifact> myArtifacts;
protected final boolean myForcedCompilation;
protected CompileScope(@NotNull Project project, JpsProject jpsProject, Set<JpsArtifact> artifacts) {
protected CompileScope(@NotNull Project project, JpsProject jpsProject, Set<JpsArtifact> 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);
@@ -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<String> 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<String> 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;
@@ -20,13 +20,11 @@ public class ModulesAndFilesScope extends CompileScope {
private final Set<String> myModules;
private final Map<String, Set<File>> myFiles;
private final boolean myForcedCompilation;
public ModulesAndFilesScope(Project project, JpsProject jpsProject, Collection<JpsModule> modules, Map<String, Set<File>> files,
Set<JpsArtifact> artifacts, boolean isForcedCompilation) {
super(project, jpsProject, artifacts);
super(project, jpsProject, artifacts, isForcedCompilation);
myFiles = files;
myForcedCompilation = isForcedCompilation;
myModules = new HashSet<String>();
for (JpsModule module : modules) {
myModules.add(module.getName());
@@ -15,17 +15,14 @@ import java.util.Set;
* Date: 9/17/11
*/
public class ModulesScope extends CompileScope {
private final Set<String> myModules;
private final boolean myForcedCompilation;
public ModulesScope(Project project, JpsProject jpsProject, Set<JpsModule> modules, Set<JpsArtifact> artifacts, boolean isForcedCompilation) {
super(project, jpsProject, artifacts);
super(project, jpsProject, artifacts, isForcedCompilation);
myModules = new HashSet<String>();
for (JpsModule module : modules) {
myModules.add(module.getName());
}
myForcedCompilation = isForcedCompilation;
}
public boolean isRecompilationForced(@NotNull String moduleName) {
@@ -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<String> myDeletedPaths = Collections.synchronizedSet(new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY));
private final Map<Integer, Set<String>> myPathsToRecompile = Collections.synchronizedMap(new HashMap<Integer, Set<String>>());
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<Integer, Set<String>> entry : myPathsToRecompile.entrySet()) {
out.writeInt(entry.getKey());
Set<String> 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<String> changed = new THashSet<String>(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<String> changed = myPathsToRecompile.get(rootIndex);
if (changed == null) {
changed = new THashSet<String>(FileUtil.PATH_HASHING_STRATEGY);
myPathsToRecompile.put(rootIndex, changed);
}
return changed.add(filePath);
}
}
public void addDeleted(String filePath) {
synchronized (myPathsToRecompile) {
for (Set<String> paths : myPathsToRecompile.values()) {
paths.remove(filePath);
}
}
myDeletedPaths.add(filePath);
}
@Nullable
public Set<String> clearRecompile(int index) {
return myPathsToRecompile.remove(index);
}
public Set<String> getAndClearDeletedPaths() {
synchronized (myDeletedPaths) {
try {
return new HashSet<String>(myDeletedPaths);
}
finally {
myDeletedPaths.clear();
}
}
}
public Map<Integer, Set<String>> getFilesToRecompile() {
return myPathsToRecompile;
}
}
@@ -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;
}
}
@@ -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<File, ArtifactRootDescriptor> myRootToDescriptorMap;
private Map<ArtifactRootId, ArtifactRootDescriptor> myIdToDescriptorMap;
private Map<JpsArtifact, ArtifactInstructionsBuilder> myInstructions;
public ArtifactRootsIndex(JpsModel model, Project project, BuildDataManager manager, ModuleRootsIndex rootsIndex) {
myRootToDescriptorMap = new MultiMap<File, ArtifactRootDescriptor>();
myInstructions = new HashMap<JpsArtifact, ArtifactInstructionsBuilder>();
myIdToDescriptorMap = new HashMap<ArtifactRootId, ArtifactRootDescriptor>();
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<ArtifactRootDescriptor, DestinationInfo> pair : builder.getInstructions()) {
ArtifactRootDescriptor descriptor = pair.getFirst();
myRootToDescriptorMap.putValue(descriptor.getRootFile(), descriptor);
myIdToDescriptorMap.put(descriptor.getRootId(), descriptor);
}
}
}
@NotNull
public Collection<ArtifactRootDescriptor> getDescriptors(@NotNull File file) {
File current = file;
Collection<ArtifactRootDescriptor> result = null;
while (current != null) {
Collection<ArtifactRootDescriptor> descriptors = myRootToDescriptorMap.get(current);
if (!descriptors.isEmpty()) {
if (result == null) {
result = descriptors;
}
else {
result = new ArrayList<ArtifactRootDescriptor>(result);
result.addAll(descriptors);
}
}
current = FileUtil.getParentFile(current);
}
return result != null ? result : Collections.<ArtifactRootDescriptor>emptyList();
}
@NotNull
public ArtifactInstructionsBuilder getInstructionsBuilder(@NotNull JpsArtifact artifact) {
return myInstructions.get(artifact);
}
}
@@ -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<String, IntArrayList> myChangedFiles = new HashMap<String, IntArrayList>();
private Set<String> myDeletedFiles = new HashSet<String>();
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<String, IntArrayList> getChangedFiles() {
return myChangedFiles;
}
public Set<String> getDeletedFiles() {
return myDeletedFiles;
}
public void initState(final BuildDataManager dataManager) throws IOException {
final Set<String> currentPaths = new HashSet<String>();
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<String> currentPaths = new HashSet<String>();
fsState.clearDeletedPaths(artifactName);
markDirtyFiles(builder, dataManager, currentPaths, false);
final ArtifactSourceToOutputMapping mapping = getOrCreateSrcOutMapping();
final Iterator<String> 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<String> 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<String> currentPaths) throws IOException {
private void markDirtyFiles(ArtifactInstructionsBuilder builder, BuildDataManager dataManager, @Nullable Set<String> currentPaths,
final boolean forceMarkDirty) throws IOException {
for (Pair<ArtifactRootDescriptor, DestinationInfo> 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<String> 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<ArtifactRootDescriptor, DestinationInfo> 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);
}
}
@@ -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<String,
update(filePath, null);
}
public void removeTimestamp(String filePath, final int artifactId) throws IOException {
final PerArtifactTimestamp[] state = getState(filePath);
if (state == null) return;
for (int i = 0, length = state.length; i < length; i++) {
if (state[i].myArtifactId == artifactId) {
final PerArtifactTimestamp[] newState = ArrayUtil.remove(state, i);
update(filePath, newState.length > 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;
@@ -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<? extends StorageOwner> getChildStorages() {
return ContainerUtil.concat(myArtifactState.values(), Arrays.asList(myTimestampStorage, myPersistentData));
}
public ArtifactSourceTimestampStorage getTimestampStorage() {
return myTimestampStorage;
}
}
@@ -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<String> deletedFiles = state.getDeletedFiles();
final Map<String,IntArrayList> changedFiles = state.getChangedFiles();
if (deletedFiles.isEmpty() && changedFiles.isEmpty()) {
final ArtifactSourceFilesState state = pd.dataManager.getArtifactsBuildData().getOrCreateState(artifact, pd);
state.ensureFsStateInitialized(pd.dataManager, context);
final Collection<String> deletedFiles = pd.fsState.getAndClearDeletedPaths(artifact.getName());
final Map<Integer, Set<String>> 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<String> changedOutputPaths = new THashSet<String>();
for (Map.Entry<String, IntArrayList> 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<String> outputPaths = srcOutMapping.getState(sourcePath);
if (outputPaths != null) {
changedOutputPaths.addAll(outputPaths);
for (String outputPath : outputPaths) {
final List<ArtifactOutputToSourceMapping.SourcePathAndRootIndex> sources = outSrcMapping.getState(outputPath);
if (sources != null) {
for (ArtifactOutputToSourceMapping.SourcePathAndRootIndex source : sources) {
addFileToProcess(filesToProcess, source.getRootIndex(), source.getPath(), deletedFiles);
for (Map.Entry<Integer, Set<String>> entry : filesToRecompile.entrySet()) {
int rootIndex = entry.getKey();
for (String sourcePath : entry.getValue()) {
addFileToProcess(filesToProcess, rootIndex, sourcePath, deletedFiles);
final List<String> outputPaths = srcOutMapping.getState(sourcePath);
if (outputPaths != null) {
changedOutputPaths.addAll(outputPaths);
for (String outputPath : outputPaths) {
final List<ArtifactOutputToSourceMapping.SourcePathAndRootIndex> 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<String> 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<JarInfo> changedJars = new THashSet<JarInfo>();
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<String> sourcePaths = filesToProcess.get(root.getRootIndex());
final Set<String> 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.<ArtifactOutputToSourceMapping.SourcePathAndRootIndex>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<Set<String>> filesToProcess,
final int rootIndex,
final String path,
Set<String> deletedFiles) {
Collection<String> 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<Pair<String, String>> paths) {
BuildFSState fsState = context.getProjectDescriptor().fsState;
ArtifactRootsIndex rootsIndex = context.getProjectDescriptor().getArtifactRootsIndex();
for (Pair<String, String> 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<String> 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;
@@ -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<String, Object> 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<String, Object> 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<Manifest> 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<String> 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));
@@ -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);
@@ -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<Pair<ArtifactRootDescriptor, DestinationInfo>> getInstructions();
}
@@ -18,29 +18,33 @@ import java.util.Map;
*/
public class ArtifactInstructionsBuilderImpl implements ArtifactInstructionsBuilder {
private final Map<String, JarInfo> myJarByPath;
private final List<Pair<ArtifactSourceRoot, DestinationInfo>> myInstructions;
private final List<Pair<ArtifactRootDescriptor, DestinationInfo>> 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<String, JarInfo>();
myInstructions = new ArrayList<Pair<ArtifactSourceRoot, DestinationInfo>>();
myInstructions = new ArrayList<Pair<ArtifactRootDescriptor, DestinationInfo>>();
}
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<ArtifactSourceRoot, DestinationInfo> pair : myInstructions) {
for (Pair<ArtifactRootDescriptor, DestinationInfo> 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<Pair<ArtifactRootDescriptor, DestinationInfo>> 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);
}
}
@@ -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();
}
}
@@ -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;
}
@@ -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) {
@@ -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,
@@ -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<? extends ZipEntry> entries = zipFile.entries();
@@ -34,8 +34,8 @@ public class JarInfo {
myContent = new ArrayList<Pair<String, Object>>();
}
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) {
@@ -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);
}
}
@@ -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) {
@@ -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<File, Set<File>> 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<Integer, Set<String>> 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<String> 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<String> getContextModules(@Nullable CompileContext context) {
return context != null? CONTEXT_MODULES_KEY.get(context, Collections.<String>emptySet()) : Collections.<String>emptySet();
@@ -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<String, FilesDelta> myTestDeltas = Collections.synchronizedMap(new HashMap<String, FilesDelta>());
protected final Set<String> myInitialTestsScanPerformed = Collections.synchronizedSet(new HashSet<String>());
protected final Set<String> myInitialProductionScanPerformed = Collections.synchronizedSet(new HashSet<String>());
private final Map<String, ArtifactFilesDelta> myArtifactDeltas = Collections.synchronizedMap(new HashMap<String, ArtifactFilesDelta>());
private final Set<String> myArtifactInitialScanPerformed = Collections.synchronizedSet(new HashSet<String>());
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<File, Set<File>> 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<String, FilesDelta> getDeltas(boolean forTests) {
return forTests ? myTestDeltas : myProductionDeltas;
}
@@ -111,6 +171,14 @@ public class FSState {
return Collections.emptyList();
}
public Collection<String> 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<String, FilesDelta> 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<String, ArtifactFilesDelta> entry : myArtifactDeltas.entrySet()) {
if (!myArtifactInitialScanPerformed.contains(entry.getKey()) || entry.getValue().hasChanges()) {
return true;
}
}
return false;
}
private boolean hasWorkToDoWithModules(final boolean forTests) {
for (Map.Entry<String, FilesDelta> 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<String> getInitialScanPerformedSet(boolean forTests) {
return forTests ? myInitialTestsScanPerformed : myInitialProductionScanPerformed;
}
public boolean markInitialScanPerformed(String artifactName) {
return myArtifactInitialScanPerformed.add(artifactName);
}
}
@@ -64,6 +64,10 @@ final class FilesDelta {
}
}
public boolean hasChanges() {
return hasPathsToDelete() || hasSourcesToRecompile();
}
private static Set<File> createSetOfFiles() {
return new THashSet<File>(new TObjectHashingStrategy<File>() {
@@ -151,7 +155,7 @@ final class FilesDelta {
return myFilesToRecompile;
}
public boolean hasSourcesToRecompile() {
private boolean hasSourcesToRecompile() {
synchronized (myFilesToRecompile) {
if(!myFilesToRecompile.isEmpty()) {
for (Set<File> files : myFilesToRecompile.values()) {
@@ -164,16 +168,10 @@ final class FilesDelta {
return false;
}
public boolean hasPathsToDelete() {
private boolean hasPathsToDelete() {
return !myDeletedPaths.isEmpty();
}
public Set<String> getDeletedPaths() {
synchronized (myDeletedPaths) {
return myDeletedPaths.isEmpty()? Collections.<String>emptySet() : new HashSet<String>(myDeletedPaths);
}
}
@Nullable
public Set<File> clearRecompile(File root) {
return myFilesToRecompile.remove(root);
@@ -0,0 +1,19 @@
package org.jetbrains.jps.incremental.messages;
import java.util.Collection;
/**
* @author nik
*/
public class FileDeletedEvent extends BuildMessage {
private Collection<String> myFilePaths;
public FileDeletedEvent(Collection<String> filePaths) {
super("", Kind.INFO);
myFilePaths = filePaths;
}
public Collection<String> getFilePaths() {
return myFilePaths;
}
}
@@ -286,6 +286,10 @@ public class AndroidDexBuilder extends ProjectLevelBuilder {
dirtyOutputDirs.add(path.first);
}
}
@Override
public void filesDeleted(Collection<String> paths) {
}
};
context.putUserData(DIRTY_OUTPUT_DIRS, dirtyOutputDirs);
context.putUserData(BUILD_LISTENER_KEY, listener);