artifact builder for compile server

This commit is contained in:
nik
2012-02-03 16:27:54 +04:00
parent 9705ff9eb7
commit bc8ca6f5a4
49 changed files with 2455 additions and 99 deletions
@@ -3,8 +3,10 @@ package org.jetbrains.jps.incremental;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.artifacts.Artifact;
import java.io.File;
import java.util.Set;
/**
* @author Eugene Zhuravlev
@@ -14,8 +16,8 @@ public class AllProjectScope extends CompileScope {
private final boolean myIsForcedCompilation;
public AllProjectScope(Project project, boolean forcedCompilation) {
super(project);
public AllProjectScope(Project project, Set<Artifact> artifacts, boolean forcedCompilation) {
super(project, artifacts);
myIsForcedCompilation = forcedCompilation;
}
@@ -7,4 +7,8 @@ public abstract class Builder {
public abstract String getName();
public abstract String getDescription();
public static enum ExitCode {
OK, ABORT, ADDITIONAL_PASS_REQUIRED
}
}
@@ -1,5 +1,6 @@
package org.jetbrains.jps.incremental;
import org.jetbrains.jps.idea.OwnServiceLoader;
import org.jetbrains.jps.incremental.groovy.GroovyBuilder;
import org.jetbrains.jps.incremental.java.JavaBuilder;
import org.jetbrains.jps.incremental.resources.ResourcesBuilder;
@@ -16,7 +17,8 @@ public class BuilderRegistry {
private static class Holder {
static final BuilderRegistry ourInstance = new BuilderRegistry();
}
private final Map<BuilderCategory, List<ModuleLevelBuilder>> myBuilders = new HashMap<BuilderCategory, List<ModuleLevelBuilder>>();
private final Map<BuilderCategory, List<ModuleLevelBuilder>> myModuleLevelBuilders = new HashMap<BuilderCategory, List<ModuleLevelBuilder>>();
private final List<ProjectLevelBuilder> myProjectLevelBuilders = new ArrayList<ProjectLevelBuilder>();
private ExecutorService myTasksExecutor;
public static BuilderRegistry getInstance() {
@@ -25,7 +27,7 @@ public class BuilderRegistry {
private BuilderRegistry() {
for (BuilderCategory category : BuilderCategory.values()) {
myBuilders.put(category, new ArrayList<ModuleLevelBuilder>());
myModuleLevelBuilders.put(category, new ArrayList<ModuleLevelBuilder>());
}
final Runtime runtime = Runtime.getRuntime();
myTasksExecutor = Executors.newFixedThreadPool(runtime.availableProcessors());
@@ -35,16 +37,21 @@ public class BuilderRegistry {
}
});
final OwnServiceLoader<ProjectLevelBuilderService> loader = OwnServiceLoader.load(ProjectLevelBuilderService.class);
for (ProjectLevelBuilderService service : loader) {
myProjectLevelBuilders.add(service.createBuilder());
}
// todo: some builder registration mechanism for plugins needed
myBuilders.get(BuilderCategory.TRANSLATOR).add(new GroovyBuilder(true));
myBuilders.get(BuilderCategory.TRANSLATOR).add(new JavaBuilder(myTasksExecutor));
myBuilders.get(BuilderCategory.TRANSLATOR).add(new ResourcesBuilder());
myBuilders.get(BuilderCategory.TRANSLATOR).add(new GroovyBuilder(false));
myModuleLevelBuilders.get(BuilderCategory.TRANSLATOR).add(new GroovyBuilder(true));
myModuleLevelBuilders.get(BuilderCategory.TRANSLATOR).add(new JavaBuilder(myTasksExecutor));
myModuleLevelBuilders.get(BuilderCategory.TRANSLATOR).add(new ResourcesBuilder());
myModuleLevelBuilders.get(BuilderCategory.TRANSLATOR).add(new GroovyBuilder(false));
}
public int getTotalBuilderCount() {
public int getModuleLevelBuilderCount() {
int count = 0;
for (BuilderCategory category : BuilderCategory.values()) {
count += getBuilders(category).size();
@@ -61,7 +68,11 @@ public class BuilderRegistry {
}
public List<ModuleLevelBuilder> getBuilders(BuilderCategory category){
return Collections.unmodifiableList(myBuilders.get(category)); // todo
return Collections.unmodifiableList(myModuleLevelBuilders.get(category)); // todo
}
public List<ProjectLevelBuilder> getProjectLevelBuilders() {
return myProjectLevelBuilders;
}
public void shutdown() {
@@ -283,6 +283,10 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler
return myRootsIndex.getModuleRoots(module);
}
public ModuleRootsIndex getRootsIndex() {
return myRootsIndex;
}
public void setDone(float done) {
myDone = done;
//processMessage(new ProgressMessage("", done));
@@ -4,20 +4,27 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.artifacts.Artifact;
import java.io.File;
import java.util.Set;
/**
* @author Eugene Zhuravlev
* Date: 1/15/12
*/
public abstract class CompileScope {
@NotNull
private final Project myProject;
private final Set<Artifact> myArtifacts;
protected CompileScope(@NotNull Project project) {
protected CompileScope(@NotNull Project project, Set<Artifact> artifacts) {
myProject = project;
myArtifacts = artifacts;
}
public boolean isAffected(Artifact artifact) {
return myArtifacts.contains(artifact);
}
public abstract boolean isAffected(Module module, @NotNull File file);
@@ -6,6 +6,7 @@ import com.intellij.util.io.PersistentEnumerator;
import org.jetbrains.jps.*;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.api.RequestFuture;
import org.jetbrains.jps.artifacts.Artifact;
import org.jetbrains.jps.incremental.java.ExternalJavacDescriptor;
import org.jetbrains.jps.incremental.java.JavaBuilder;
import org.jetbrains.jps.incremental.messages.BuildMessage;
@@ -49,7 +50,7 @@ public class IncProjectBuilder {
private float myModulesProcessed = 0.0f;
private final float myTotalModulesWork;
private final int myTotalBuilderCount;
private final int myTotalModuleLevelBuilderCount;
public IncProjectBuilder(ProjectDescriptor pd, BuilderRegistry builderRegistry, CanceledStatus cs) {
myProjectDescriptor = pd;
@@ -58,7 +59,7 @@ public class IncProjectBuilder {
myProductionChunks = new ProjectChunks(pd.project, ClasspathKind.PRODUCTION_COMPILE);
myTestChunks = new ProjectChunks(pd.project, ClasspathKind.TEST_COMPILE);
myTotalModulesWork = (float)pd.rootsIndex.getTotalModuleCount() * 2; /* multiply by 2 to reflect production and test sources */
myTotalBuilderCount = builderRegistry.getTotalBuilderCount();
myTotalModuleLevelBuilderCount = builderRegistry.getModuleLevelBuilderCount();
}
public void addMessageHandler(MessageHandler handler) {
@@ -79,7 +80,7 @@ public class IncProjectBuilder {
"Internal caches are corrupted or have outdated format, forcing project rebuild: " +
e.getMessage()));
flushContext(context);
context = createContext(new AllProjectScope(scope.getProject(), true), false, true);
context = createContext(new AllProjectScope(scope.getProject(), Collections.<Artifact>emptySet(), true), false, true);
runBuild(context);
}
else {
@@ -165,6 +166,9 @@ public class IncProjectBuilder {
context.processMessage(new ProgressMessage("Building test sources"));
buildChunks(context, myTestChunks);
context.processMessage(new ProgressMessage("Building project"));
runProjectLevelBuilders(context);
context.processMessage(new ProgressMessage("Running 'after' tasks"));
runTasks(context, myBuilderRegistry.getAfterTasks());
@@ -333,7 +337,7 @@ public class IncProjectBuilder {
context.onChunkBuildStart(chunk);
for (BuilderCategory category : BuilderCategory.values()) {
runBuilders(context, chunk, category);
runModuleLevelBuilders(context, chunk, category);
}
}
catch (ProjectBuildException e) {
@@ -364,14 +368,14 @@ public class IncProjectBuilder {
}
}
private void runBuilders(final CompileContext context, ModuleChunk chunk, BuilderCategory category) throws ProjectBuildException {
private void runModuleLevelBuilders(final CompileContext context, ModuleChunk chunk, BuilderCategory category) throws ProjectBuildException {
final List<ModuleLevelBuilder> builders = myBuilderRegistry.getBuilders(category);
if (builders.isEmpty()) {
return;
}
boolean rebuildFromScratchRequested = false;
float stageCount = myTotalBuilderCount;
float stageCount = myTotalModuleLevelBuilderCount;
int stagesPassed = 0;
final int modulesInChunk = chunk.getModules().size();
@@ -397,7 +401,7 @@ public class IncProjectBuilder {
if (!nextPassRequired) {
// recalculate basis
myModulesProcessed -= (stagesPassed * modulesInChunk) / stageCount;
stageCount += myTotalBuilderCount;
stageCount += myTotalModuleLevelBuilderCount;
myModulesProcessed += (stagesPassed * modulesInChunk) / stageCount;
}
nextPassRequired = true;
@@ -432,6 +436,15 @@ public class IncProjectBuilder {
while (nextPassRequired);
}
private void runProjectLevelBuilders(CompileContext context) throws ProjectBuildException {
for (ProjectLevelBuilder builder : myBuilderRegistry.getProjectLevelBuilders()) {
builder.build(context);
if (myCancelStatus.isCanceled()) {
throw new ProjectBuildException(CANCELED_MESSAGE);
}
}
}
private static void syncOutputFiles(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException {
final BuildDataManager dataManager = context.getDataManager();
final boolean compilingTests = context.isCompilingTests();
@@ -4,6 +4,7 @@ import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.PathUtil;
import org.jetbrains.jps.Project;
import java.io.File;
@@ -17,6 +18,7 @@ public class ModuleRootsIndex {
private final Map<File, RootDescriptor> myRootToModuleMap = new HashMap<File, RootDescriptor>();
private final Map<Module, List<RootDescriptor>> myModuleToRootsMap = new HashMap<Module, List<RootDescriptor>>();
private final int myTotalModuleCount;
private final Set<File> myExcludedRoots = new HashSet<File>();
public ModuleRootsIndex(Project project) {
final Collection<Module> allModules = project.getModules().values();
@@ -39,6 +41,10 @@ public class ModuleRootsIndex {
myRootToModuleMap.put(root, descriptor);
moduleRoots.add(descriptor);
}
for (String r : module.getExcludes()) {
final File root = new File(FileUtil.toCanonicalPath(r));
myExcludedRoots.add(root);
}
}
}
@@ -69,4 +75,8 @@ public class ModuleRootsIndex {
}
return null;
}
public boolean isExcluded(File file) {
return PathUtil.isUnder(myExcludedRoots, file);
}
}
@@ -3,6 +3,7 @@ package org.jetbrains.jps.incremental;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.artifacts.Artifact;
import java.io.File;
import java.util.Collection;
@@ -20,8 +21,9 @@ public class ModulesAndFilesScope extends CompileScope {
private final Map<Module, Set<File>> myFiles;
private final boolean myForcedCompilation;
public ModulesAndFilesScope(Project project, Collection<Module> modules, Map<Module, Set<File>> files, boolean isForcedCompilation) {
super(project);
public ModulesAndFilesScope(Project project, Collection<Module> modules, Map<Module, Set<File>> files, Set<Artifact> artifacts,
boolean isForcedCompilation) {
super(project, artifacts);
myFiles = files;
myForcedCompilation = isForcedCompilation;
myModules = new HashSet<Module>(modules);
@@ -3,6 +3,7 @@ package org.jetbrains.jps.incremental;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.artifacts.Artifact;
import java.io.File;
import java.util.Set;
@@ -16,8 +17,8 @@ public class ModulesScope extends CompileScope {
private final Set<Module> myModules;
private final boolean myForcedCompilation;
public ModulesScope(Project project, Set<Module> modules, boolean isForcedCompilation) {
super(project);
public ModulesScope(Project project, Set<Module> modules, Set<Artifact> artifacts, boolean isForcedCompilation) {
super(project, artifacts);
myModules = modules;
myForcedCompilation = isForcedCompilation;
}
@@ -4,17 +4,9 @@ package org.jetbrains.jps.incremental;
* @author nik
*/
public abstract class ProjectLevelBuilder extends Builder {
private final ProjectLevelBuilderCategory myCategory;
protected ProjectLevelBuilder(ProjectLevelBuilderCategory category) {
myCategory = category;
protected ProjectLevelBuilder() {
}
public abstract void build(CompileContext context);
public abstract void build(CompileContext context) throws ProjectBuildException;
public ProjectLevelBuilderCategory getCategory() {
return myCategory;
}
public static enum ProjectLevelBuilderCategory { TRANSLATOR, PACKAGER }
}
@@ -0,0 +1,11 @@
package org.jetbrains.jps.incremental;
import org.jetbrains.annotations.NotNull;
/**
* @author nik
*/
public abstract class ProjectLevelBuilderService {
@NotNull
public abstract ProjectLevelBuilder createBuilder();
}
@@ -0,0 +1,16 @@
package org.jetbrains.jps.incremental.artifacts;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.incremental.ProjectLevelBuilder;
import org.jetbrains.jps.incremental.ProjectLevelBuilderService;
/**
* @author nik
*/
public class ArtifactBuilderService extends ProjectLevelBuilderService {
@NotNull
@Override
public ProjectLevelBuilder createBuilder() {
return new IncArtifactBuilder();
}
}
@@ -0,0 +1,114 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.io.IOUtil;
import gnu.trove.TIntHashSet;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author nik
*/
public class ArtifactCompilerPersistentData {
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.generic.ArtifactCompilerPersistentData");
private static final int VERSION = 0;
private File myFile;
private Map<String, Integer> myArtifact2Id = new HashMap<String, Integer>();
private TIntHashSet myUsedIds = new TIntHashSet();
private boolean myVersionChanged;
public ArtifactCompilerPersistentData(File cacheStoreDirectory) throws IOException {
myFile = new File(cacheStoreDirectory, "info");
if (!myFile.exists()) {
LOG.info("Artifacts compiler info file doesn't exist: " + myFile.getAbsolutePath());
myVersionChanged = true;
return;
}
DataInputStream input = new DataInputStream(new BufferedInputStream(new FileInputStream(myFile)));
try {
final int version = input.readInt();
if (version != VERSION) {
LOG.info("Artifacts compiler version changed (" + myFile.getAbsolutePath() + "): " + version + " -> " + VERSION);
myVersionChanged = true;
return;
}
int size = input.readInt();
while (size-- > 0) {
final String artifactName = IOUtil.readString(input);
final int id = input.readInt();
myArtifact2Id.put(artifactName, id);
myUsedIds.add(id);
}
}
finally {
input.close();
}
}
public boolean isVersionChanged() {
return myVersionChanged;
}
public void save() throws IOException {
final DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(myFile)));
try {
output.writeInt(VERSION);
output.writeInt(myArtifact2Id.size());
for (Map.Entry<String, Integer> entry : myArtifact2Id.entrySet()) {
IOUtil.writeString(entry.getKey(), output);
output.writeInt(entry.getValue());
}
}
finally {
output.close();
}
}
public int getId(@NotNull String artifactName) {
if (myArtifact2Id.containsKey(artifactName)) {
return myArtifact2Id.get(artifactName);
}
int id = 0;
while (myUsedIds.contains(id)) {
id++;
}
myArtifact2Id.put(artifactName, id);
myUsedIds.add(id);
return id;
}
public Set<String> getAllArtifacts() {
return myArtifact2Id.keySet();
}
public int removeArtifact(String target) {
return myArtifact2Id.remove(target);
}
public void clean() {
FileUtil.delete(myFile);
}
}
@@ -0,0 +1,189 @@
package org.jetbrains.jps.incremental.artifacts;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.artifacts.Artifact;
import org.jetbrains.jps.artifacts.LayoutElement;
import org.jetbrains.jps.incremental.ModuleRootsIndex;
import org.jetbrains.jps.incremental.artifacts.builders.LayoutElementBuildersRegistry;
import org.jetbrains.jps.incremental.artifacts.instructions.*;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author nik
*/
public class ArtifactSourceFilesState {
private final Project myProject;
private final Artifact myArtifact;
private final int myArtifactId;
private final ModuleRootsIndex myRootsIndex;
private final ArtifactSourceTimestampStorage myTimestampStorage;
private Set<String> myChangedFiles = new HashSet<String>();
private Set<String> myDeletedFiles = new HashSet<String>();
private ArtifactInstructionsBuilder myInstructionsBuilder;
private ArtifactSourceToOutputMapping myMapping;
private final AtomicBoolean myInitialized = new AtomicBoolean();
private final File myMappingsFile;
public ArtifactSourceFilesState(Artifact artifact, int artifactId, Project project,
ModuleRootsIndex rootsIndex,
ArtifactSourceTimestampStorage timestampStorage,
File artifactsDataDir) {
myProject = project;
myArtifact = artifact;
myRootsIndex = rootsIndex;
myTimestampStorage = timestampStorage;
myArtifactId = artifactId;
myMappingsFile = new File(artifactsDataDir, "mappings" + File.separator + artifactId);
}
public ArtifactSourceToOutputMapping getOrCreateMapping() throws Exception {
if (myMapping == null) {
myMapping = new ArtifactSourceToOutputMapping(myMappingsFile);
}
return myMapping;
}
public void clean() {
if (myMapping != null) {
myMapping.wipe();
}
}
public Set<String> getChangedFiles() {
return myChangedFiles;
}
public Set<String> getDeletedFiles() {
return myDeletedFiles;
}
public void initState() throws Exception {
/*
if (!myInitialized.compareAndSet(false, true)) {
return;
}
*/
final Set<String> currentPaths = new HashSet<String>();
myChangedFiles.clear();
myDeletedFiles.clear();
getOrCreateInstructions().processRoots(new ArtifactRootProcessor() {
@Override
public void process(ArtifactSourceRoot root, Collection<DestinationInfo> destinations) throws Exception {
final File rootFile = root.getRootFile();
if (rootFile.exists()) {
processRecursively(rootFile, root.getFilter(), currentPaths);
}
}
});
final ArtifactSourceToOutputMapping mapping = getOrCreateMapping();
final Iterator<String> iterator = mapping.getKeysIterator();
while (iterator.hasNext()) {
String path = iterator.next();
if (!currentPaths.contains(path)) {
myDeletedFiles.add(path);
}
}
}
public ArtifactSourceTimestampStorage getTimestampStorage() {
return myTimestampStorage;
}
private void processRecursively(File file, SourceFileFilter filter, Set<String> currentPaths) throws Exception {
final String filePath = FileUtil.toSystemIndependentName(FileUtil.toCanonicalPath(file.getPath()));
if (!filter.accept(filePath)) return;
if (file.isDirectory()) {
final File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
processRecursively(child, filter, currentPaths);
}
}
}
else {
currentPaths.add(filePath);
final ArtifactSourceTimestampStorage.PerArtifactTimestamp[] state = myTimestampStorage.getState(filePath);
boolean upToDate = false;
if (state != null) {
for (ArtifactSourceTimestampStorage.PerArtifactTimestamp artifactTimestamp : state) {
if (artifactTimestamp.myArtifactId == myArtifactId && artifactTimestamp.myTimestamp == file.lastModified()) {
upToDate = true;
break;
}
}
}
if (!upToDate) {
myDeletedFiles.remove(filePath);
myChangedFiles.add(filePath);
}
}
}
public ArtifactInstructionsBuilder getOrCreateInstructions() {
if (myInstructionsBuilder == null) {
myInstructionsBuilder = computeInstructions();
}
return myInstructionsBuilder;
}
private ArtifactInstructionsBuilder computeInstructions() {
final LayoutElement rootElement = myArtifact.getRootElement();
ArtifactInstructionsBuilderContext context = new ArtifactInstructionsBuilderContextImpl(myProject, new ProjectPaths(myProject));
final ArtifactInstructionsBuilderImpl instructionsBuilder = new ArtifactInstructionsBuilderImpl(myRootsIndex);
final CopyToDirectoryInstructionCreator instructionCreator = new CopyToDirectoryInstructionCreator(instructionsBuilder, myArtifact.getOutputPath());
LayoutElementBuildersRegistry.getInstance().generateInstructions(rootElement, instructionCreator, context);
return instructionsBuilder;
}
public void updateTimestamps(Set<String> deletedFiles, Set<String> changedFiles) throws Exception {
for (String filePath : deletedFiles) {
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) {
ArrayUtil.remove(state, i);
myTimestampStorage.update(filePath, state);
break;
}
}
}
for (String filePath : changedFiles) {
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) {
File file = new File(FileUtil.toSystemDependentName(filePath));
state[i] = new ArtifactSourceTimestampStorage.PerArtifactTimestamp(myArtifactId, file.lastModified());
myTimestampStorage.update(filePath, state);
break;
}
}
}
myDeletedFiles.clear();
myChangedFiles.clear();
}
public void close() throws IOException {
if (myMapping != null) {
myMapping.close();
}
}
public void flush(boolean memoryCachesOnly) {
if (myMapping != null) {
myMapping.flush(memoryCachesOnly);
}
}
}
@@ -0,0 +1,57 @@
package org.jetbrains.jps.incremental.artifacts;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.jps.incremental.storage.AbstractStateStorage;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
/**
* @author nik
*/
public class ArtifactSourceTimestampStorage extends AbstractStateStorage<String, ArtifactSourceTimestampStorage.PerArtifactTimestamp[]> {
private static final DataExternalizer<PerArtifactTimestamp[]> TIMESTAMP_EXTERNALIZER = new DataExternalizer<PerArtifactTimestamp[]>() {
@Override
public void save(DataOutput out, PerArtifactTimestamp[] value) throws IOException {
out.writeInt(value.length);
for (PerArtifactTimestamp timestamp : value) {
out.writeInt(timestamp.myArtifactId);
out.writeLong(timestamp.myTimestamp);
}
}
@Override
public PerArtifactTimestamp[] read(DataInput in) throws IOException {
final int size = in.readInt();
final PerArtifactTimestamp[] value = new PerArtifactTimestamp[size];
for (int i = 0; i < size; i++) {
final int artifactId = in.readInt();
final long timestamp = in.readLong();
value[i] = new PerArtifactTimestamp(artifactId, timestamp);
}
return value;
}
};
public ArtifactSourceTimestampStorage(@NonNls File storePath) throws Exception {
super(storePath, new EnumeratorStringDescriptor(), TIMESTAMP_EXTERNALIZER);
}
public void markDirty(String filePath) throws Exception {
update(filePath, null);
}
public static class PerArtifactTimestamp {
public final int myArtifactId;
public final long myTimestamp;
public PerArtifactTimestamp(int artifactId, long timestamp) {
myArtifactId = artifactId;
myTimestamp = timestamp;
}
}
}
@@ -0,0 +1,44 @@
package org.jetbrains.jps.incremental.artifacts;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.IOUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.jps.incremental.storage.AbstractStateStorage;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
/**
* @author nik
*/
public class ArtifactSourceToOutputMapping extends AbstractStateStorage<String, String[]> {
private static DataExternalizer<String[]> STRING_ARRAY_EXTERNALIZER = new DataExternalizer<String[]>() {
private final byte[] myBuffer = IOUtil.allocReadWriteUTFBuffer();
@Override
public void save(DataOutput out, String[] value) throws IOException {
out.writeInt(value.length);
for (String path : value) {
IOUtil.writeUTFFast(myBuffer, out, path);
}
}
@Override
public String[] read(DataInput in) throws IOException {
final int size = in.readInt();
String[] result = new String[size];
for (int i = 0; i < size; i++) {
final String path = IOUtil.readUTFFast(myBuffer, in);
result[i] = path;
}
return result;
}
};
public ArtifactSourceToOutputMapping(@NonNls File storePath) throws Exception {
super(storePath, new EnumeratorStringDescriptor(), STRING_ARRAY_EXTERNALIZER);
}
}
@@ -0,0 +1,61 @@
package org.jetbrains.jps.incremental.artifacts;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.artifacts.Artifact;
import org.jetbrains.jps.incremental.ModuleRootsIndex;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author nik
*/
public class ArtifactsBuildData {
private Map<Artifact, ArtifactSourceFilesState> myArtifactState;
private final ArtifactSourceTimestampStorage myTimestampStorage;
private ArtifactCompilerPersistentData myPersistentData;
private final File myArtifactsDataDir;
public ArtifactsBuildData(File artifactsDataDir) throws Exception {
myArtifactsDataDir = artifactsDataDir;
myTimestampStorage = new ArtifactSourceTimestampStorage(new File(artifactsDataDir, "timestamps"));
myArtifactState = new HashMap<Artifact, ArtifactSourceFilesState>();
myPersistentData = new ArtifactCompilerPersistentData(artifactsDataDir);
}
public ArtifactSourceFilesState getOrCreateState(Artifact artifact, Project project, ModuleRootsIndex index) {
ArtifactSourceFilesState state = myArtifactState.get(artifact);
if (state == null) {
final int artifactId = myPersistentData.getId(artifact.getName());
state = new ArtifactSourceFilesState(artifact, artifactId, project, index, myTimestampStorage, myArtifactsDataDir);
myArtifactState.put(artifact, state);
}
return state;
}
public void clean() {
myTimestampStorage.wipe();
myPersistentData.clean();
for (ArtifactSourceFilesState state : myArtifactState.values()) {
state.clean();
}
FileUtil.delete(myArtifactsDataDir);
}
public void close() throws IOException {
myTimestampStorage.close();
for (ArtifactSourceFilesState state : myArtifactState.values()) {
state.close();
}
}
public void flush(boolean memoryCachesOnly) {
myTimestampStorage.flush(memoryCachesOnly);
for (ArtifactSourceFilesState state : myArtifactState.values()) {
state.flush(memoryCachesOnly);
}
}
}
@@ -0,0 +1,210 @@
package org.jetbrains.jps.incremental.artifacts;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.jetbrains.jps.PathUtil;
import org.jetbrains.jps.artifacts.Artifact;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ProjectBuildException;
import org.jetbrains.jps.incremental.ProjectLevelBuilder;
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.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.storage.BuildDataManager;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author nik
*/
public class IncArtifactBuilder extends ProjectLevelBuilder {
public static final String BUILDER_NAME = "artifacts";
public IncArtifactBuilder() {
super();
}
@Override
public void build(CompileContext context) throws ProjectBuildException {
Set<Artifact> affected = new HashSet<Artifact>();
for (Artifact artifact : context.getProject().getArtifacts().values()) {
if (context.getScope().isAffected(artifact)) {
affected.add(artifact);
}
}
final Set<Artifact> toBuild = ArtifactSorter.addIncludedArtifacts(affected, context.getProject());
Map<String, Artifact> artifactsMap = new HashMap<String, Artifact>();
for (Artifact artifact : toBuild) {
artifactsMap.put(artifact.getName(), artifact);
}
final ArtifactSorter sorter = new ArtifactSorter(context.getProject());
final Map<String, String> selfIncludingNameMap = sorter.getArtifactToSelfIncludingNameMap();
for (String artifactName : sorter.getArtifactsSortedByInclusion()) {
final Artifact artifact = artifactsMap.get(artifactName);
if (artifact != null) {
final String selfIncluding = selfIncludingNameMap.get(artifactName);
if (selfIncluding != null) {
String name = selfIncluding.equals(artifact.getName()) ? "it" : "'" + selfIncluding + "' artifact";
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot build '" + artifactName + "' artifact: " + name + " includes itself in the output layout"));
break;
}
if (StringUtil.isEmpty(artifact.getOutputPath())) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, "Cannot build '" + artifactName + "' artifact: output path is not specified"));
break;
}
buildArtifact(artifact, context);
}
}
}
private static void buildArtifact(Artifact artifact, CompileContext context) throws ProjectBuildException {
final BuildDataManager dataManager = context.getDataManager();
try {
final ArtifactSourceFilesState state = dataManager.getArtifactsBuildData().getOrCreateState(artifact,
context.getProject(), context.getRootsIndex());
state.initState();
final Set<String> deletedFiles = state.getDeletedFiles();
final Set<String> changedFiles = state.getChangedFiles();
if (deletedFiles.isEmpty() && changedFiles.isEmpty()) {
return;
}
context.processMessage(new ProgressMessage("Building artifact '" + artifact.getName() + "'..."));
final ArtifactSourceToOutputMapping mapping = state.getOrCreateMapping();
final Set<String> deletedJars = deleteOutdatedFiles(deletedFiles, context, mapping);
final ArtifactInstructionsBuilder instructions = state.getOrCreateInstructions();
final Set<JarInfo> changedJars = new THashSet<JarInfo>();
for (String deletedJar : deletedJars) {
ContainerUtil.addIfNotNull(instructions.getJarInfo(deletedJar), changedJars);
}
Map<String, String[]> updatedMappings = new HashMap<String, String[]>();
for (final String filePath : changedFiles) {
final List<String> outputs = new SmartList<String>();
instructions.processContainingRoots(filePath, new ArtifactRootProcessor() {
@Override
public void process(ArtifactSourceRoot root, Collection<DestinationInfo> destinations) throws Exception {
for (DestinationInfo destination : destinations) {
if (destination instanceof ExplodedDestinationInfo) {
copyFromRoot(root, filePath, destination.getOutputPath(), outputs);
}
else {
outputs.add(destination.getOutputFilePath());
changedJars.add(((JarDestinationInfo)destination).getJarInfo());
}
}
}
});
updatedMappings.put(filePath, ArrayUtil.toStringArray(outputs));
}
JarsBuilder builder = new JarsBuilder(changedJars, null, context);
final boolean processed = builder.buildJars(new THashSet<String>());
if (!processed) {
return;
}
state.updateTimestamps(deletedFiles, changedFiles);
for (Map.Entry<String, String[]> entry : updatedMappings.entrySet()) {
mapping.appendData(entry.getKey(), entry.getValue());
}
context.processMessage(UptoDateFilesSavedEvent.INSTANCE);
}
catch (Exception e) {
throw new ProjectBuildException(e);
}
}
private static void copyFromRoot(ArtifactSourceRoot root, String path, String outputPath, List<String> outputs) throws IOException {
if (root instanceof FileBasedArtifactSourceRoot) {
final File file = new File(FileUtil.toSystemDependentName(path));
String targetPath;
if (!file.equals(root.getRootFile())) {
final String relativePath = FileUtil.getRelativePath(FileUtil.toSystemIndependentName(root.getRootFile().getPath()), path, '/');
targetPath = PathUtil.appendToPath(outputPath, relativePath);
}
else {
targetPath = outputPath;
}
final File targetFile = new File(FileUtil.toSystemDependentName(targetPath));
FileUtil.copyContent(file, targetFile);
outputs.add(outputPath);
}
else {
//todo[nik]
}
}
private static Set<String> deleteOutdatedFiles(Set<String> deletedFiles, CompileContext context,
ArtifactSourceToOutputMapping mapping) throws Exception {
if (deletedFiles.isEmpty()) return Collections.emptySet();
context.processMessage(new ProgressMessage("Deleting outdated files..."));
Set<String> pathsToDelete = new THashSet<String>();
for (String path : deletedFiles) {
final String[] outputPaths = mapping.getState(path);
Collections.addAll(pathsToDelete, outputPaths);
}
int notDeletedFilesCount = 0;
final THashSet<String> notDeletedJars = new THashSet<String>();
final THashSet<String> deletedJars = new THashSet<String>();
for (String fullPath : pathsToDelete) {
int end = fullPath.indexOf(JarPathUtil.JAR_SEPARATOR);
boolean isJar = end != -1;
String filePath = isJar ? fullPath.substring(0, end) : fullPath;
boolean deleted = false;
if (isJar) {
if (notDeletedJars.contains(filePath)) {
continue;
}
deleted = deletedJars.contains(filePath);
}
File file = new File(FileUtil.toSystemDependentName(filePath));
if (!deleted) {
deleted = FileUtil.delete(file);
}
if (deleted) {
if (isJar) {
deletedJars.add(filePath);
}
}
else {
if (isJar) {
notDeletedJars.add(filePath);
}
if (notDeletedFilesCount++ > 50) {
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.WARNING, "Deletion of outdated files stopped because too many files cannot be deleted"));
break;
}
context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.WARNING, "Cannot delete file '" + filePath + "'"));
}
}
return deletedJars;
}
@Override
public String getName() {
return BUILDER_NAME;
}
@Override
public String getDescription() {
return "Artifacts builder";
}
}
@@ -0,0 +1,20 @@
package org.jetbrains.jps.incremental.artifacts;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author nik
*/
public class JarPathUtil {
public static final String JAR_SEPARATOR = "!/";
@NotNull
public static File getLocalFile(@NotNull String fullPath) {
final int i = fullPath.indexOf(JAR_SEPARATOR);
String filePath = i == -1 ? fullPath : fullPath.substring(0, i);
return new File(FileUtil.toSystemDependentName(filePath));
}
}
@@ -0,0 +1,12 @@
package org.jetbrains.jps.incremental.artifacts.builders;
import org.jetbrains.jps.artifacts.LayoutElement;
import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactCompilerInstructionCreator;
import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactInstructionsBuilderContext;
/**
* @author nik
*/
public abstract class LayoutElementBuilder<E extends LayoutElement> {
public abstract void generateInstructions(E element, ArtifactCompilerInstructionCreator instructionCreator, ArtifactInstructionsBuilderContext builderContext);
}
@@ -0,0 +1,206 @@
package org.jetbrains.jps.incremental.artifacts.builders;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ClassMap;
import org.jetbrains.jps.Module;
import org.jetbrains.jps.artifacts.*;
import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactCompilerInstructionCreator;
import org.jetbrains.jps.incremental.artifacts.instructions.ArtifactInstructionsBuilderContext;
import java.io.File;
import java.util.List;
/**
* @author nik
*/
public class LayoutElementBuildersRegistry {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.artifacts.builders.LayoutElementBuildersRegistry");
private static class InstanceHolder {
static final LayoutElementBuildersRegistry ourInstance = new LayoutElementBuildersRegistry();
}
public static LayoutElementBuildersRegistry getInstance() {
return InstanceHolder.ourInstance;
}
private ClassMap<LayoutElementBuilder> myBuilders;
private LayoutElementBuildersRegistry() {
myBuilders = new ClassMap<LayoutElementBuilder>();
myBuilders.put(RootElement.class, new RootElementBuilder());
myBuilders.put(DirectoryElement.class, new DirectoryElementBuilder());
myBuilders.put(ArchiveElement.class, new ArchiveElementBuilder());
myBuilders.put(DirectoryCopyElement.class, new DirectoryCopyElementBuilder());
myBuilders.put(FileCopyElement.class, new FileCopyElementBuilder());
myBuilders.put(ExtractedDirectoryElement.class, new ExtractedDirectoryElementBuilder());
myBuilders.put(ModuleOutputElement.class, new ModuleOutputElementBuilder());
myBuilders.put(ModuleTestOutputElement.class, new ModuleTestOutputElementBuilder());
myBuilders.put(ComplexLayoutElement.class, new ComplexElementBuilder());
}
public void generateInstructions(LayoutElement layoutElement, ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
final LayoutElementBuilder builder = myBuilders.get(layoutElement.getClass());
if (builder == null) {
LOG.error("Builder not found for artifact output layout element of class " + layoutElement.getClass());
}
builder.generateInstructions(layoutElement, instructionCreator, builderContext);
}
private void generateChildrenInstructions(CompositeLayoutElement element, ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
generateInstructions(element.getChildren(), instructionCreator, builderContext);
}
private void generateSubstitutionInstructions(ComplexLayoutElement element,
ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
final List<LayoutElement> substitution = element.getSubstitution(builderContext.getProject());
if (substitution != null) {
generateInstructions(substitution, instructionCreator, builderContext);
}
}
private void generateInstructions(final List<LayoutElement> elements, ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
for (LayoutElement child : elements) {
generateInstructions(child, instructionCreator, builderContext);
}
}
private static void generateModuleOutputInstructions(String moduleName,
boolean tests,
ArtifactCompilerInstructionCreator creator,
ArtifactInstructionsBuilderContext context) {
final Module module = context.getProject().getModules().get(moduleName);
if (module != null) {
final File outputDir = context.getProjectPaths().getModuleOutputDir(module, tests);
if (outputDir != null) {
creator.addDirectoryCopyInstructions(outputDir);
}
}
}
private class RootElementBuilder extends LayoutElementBuilder<RootElement> {
@Override
public void generateInstructions(RootElement element, ArtifactCompilerInstructionCreator instructionCreator, ArtifactInstructionsBuilderContext builderContext) {
generateChildrenInstructions(element, instructionCreator, builderContext);
}
}
private class DirectoryElementBuilder extends LayoutElementBuilder<DirectoryElement> {
@Override
public void generateInstructions(DirectoryElement element,
ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
generateChildrenInstructions(element, instructionCreator.subFolder(element.getName()), builderContext);
}
}
private class ArchiveElementBuilder extends LayoutElementBuilder<ArchiveElement> {
@Override
public void generateInstructions(ArchiveElement element, ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
generateChildrenInstructions(element, instructionCreator.archive(element.getName()), builderContext);
}
}
private static class DirectoryCopyElementBuilder extends LayoutElementBuilder<DirectoryCopyElement> {
@Override
public void generateInstructions(DirectoryCopyElement element, ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
final String dirPath = element.getDirPath();
if (dirPath != null) {
final File directory = new File(FileUtil.toSystemDependentName(dirPath));
if (directory.isDirectory()) {
instructionCreator.addDirectoryCopyInstructions(directory);
}
}
}
}
private static class FileCopyElementBuilder extends LayoutElementBuilder<FileCopyElement> {
@Override
public void generateInstructions(FileCopyElement element, ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
final String filePath = element.getFilePath();
if (filePath != null) {
final File file = new File(FileUtil.toSystemDependentName(filePath));
if (file.isFile()) {
final String fileName = element.getOutputFileName();
instructionCreator.addFileCopyInstruction(file, fileName != null ? fileName : file.getName());
}
}
}
}
private static class ExtractedDirectoryElementBuilder extends LayoutElementBuilder<ExtractedDirectoryElement> {
@Override
public void generateInstructions(ExtractedDirectoryElement element,
ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
final String jarPath = element.getJarPath();
final String pathInJar = element.getPathInJar();
File jarFile = new File(FileUtil.toSystemDependentName(jarPath));
if (jarFile.isFile()) {
instructionCreator.addExtractDirectoryInstruction(jarFile, pathInJar);
}
}
}
private static class ModuleOutputElementBuilder extends LayoutElementBuilder<ModuleOutputElement> {
@Override
public void generateInstructions(ModuleOutputElement element,
ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
generateModuleOutputInstructions(element.getModuleName(), false, instructionCreator, builderContext);
}
}
private static class ModuleTestOutputElementBuilder extends LayoutElementBuilder<ModuleTestOutputElement> {
@Override
public void generateInstructions(ModuleTestOutputElement element,
ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
generateModuleOutputInstructions(element.getModuleName(), true, instructionCreator, builderContext);
}
}
private class ComplexElementBuilder extends LayoutElementBuilder<ComplexLayoutElement> {
@Override
public void generateInstructions(ComplexLayoutElement element,
ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
generateSubstitutionInstructions(element, instructionCreator, builderContext);
}
}
private class ArtifactOutputElementBuilder extends LayoutElementBuilder<ArtifactLayoutElement> {
@Override
public void generateInstructions(ArtifactLayoutElement element,
ArtifactCompilerInstructionCreator instructionCreator,
ArtifactInstructionsBuilderContext builderContext) {
final Artifact artifact = element.findArtifact(builderContext.getProject());
if (artifact == null) return;
final String outputPath = artifact.getOutputPath();
if (StringUtil.isEmpty(outputPath)) {
generateSubstitutionInstructions(element, instructionCreator, builderContext);
return;
}
final LayoutElement rootElement = artifact.getRootElement();
final File outputDir = new File(FileUtil.toSystemDependentName(outputPath));
if (rootElement instanceof ArchiveElement) {
final String fileName = ((ArchiveElement)rootElement).getName();
instructionCreator.addFileCopyInstruction(new File(outputDir, fileName), fileName);
}
else {
instructionCreator.addDirectoryCopyInstructions(outputDir);
}
}
}
}
@@ -0,0 +1,202 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.impl;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Consumer;
import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.GraphGenerator;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import groovy.lang.Closure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.artifacts.Artifact;
import org.jetbrains.jps.artifacts.ArtifactLayoutElement;
import org.jetbrains.jps.artifacts.ComplexLayoutElement;
import java.util.*;
/**
* @author nik
*/
public class ArtifactSorter {
private final Project myProject;
private Map<String, String> myArtifactToSelfIncludingName;
private List<String> mySortedArtifacts;
public ArtifactSorter(Project project) {
myProject = project;
}
public Map<String, String> getArtifactToSelfIncludingNameMap() {
if (myArtifactToSelfIncludingName == null) {
myArtifactToSelfIncludingName = computeArtifactToSelfIncludingNameMap();
}
return myArtifactToSelfIncludingName;
}
public List<String> getArtifactsSortedByInclusion() {
if (mySortedArtifacts == null) {
mySortedArtifacts = doGetSortedArtifacts();
}
return mySortedArtifacts;
}
private List<String> doGetSortedArtifacts() {
GraphGenerator<String> graph = createArtifactsGraph();
DFSTBuilder<String> builder = new DFSTBuilder<String>(graph);
builder.buildDFST();
List<String> names = new ArrayList<String>();
names.addAll(graph.getNodes());
Collections.sort(names, builder.comparator());
return names;
}
private Map<String, String> computeArtifactToSelfIncludingNameMap() {
final Map<String, String> result = new HashMap<String, String>();
final GraphGenerator<String> graph = createArtifactsGraph();
for (String artifactName : graph.getNodes()) {
final Iterator<String> in = graph.getIn(artifactName);
while (in.hasNext()) {
String next = in.next();
if (next.equals(artifactName)) {
result.put(artifactName, artifactName);
break;
}
}
}
final DFSTBuilder<String> builder = new DFSTBuilder<String>(graph);
builder.buildDFST();
if (builder.isAcyclic() && result.isEmpty()) return Collections.emptyMap();
final TIntArrayList sccs = builder.getSCCs();
sccs.forEach(new TIntProcedure() {
int myTNumber = 0;
public boolean execute(int size) {
if (size > 1) {
for (int j = 0; j < size; j++) {
final String artifactName = builder.getNodeByTNumber(myTNumber + j);
result.put(artifactName, artifactName);
}
}
myTNumber += size;
return true;
}
});
for (int i = 0; i < graph.getNodes().size(); i++) {
final String artifactName = builder.getNodeByTNumber(i);
if (!result.containsKey(artifactName)) {
final Iterator<String> in = graph.getIn(artifactName);
while (in.hasNext()) {
final String name = result.get(in.next());
if (name != null) {
result.put(artifactName, name);
}
}
}
}
return result;
}
@NotNull
public static Set<Artifact> addIncludedArtifacts(@NotNull Collection<Artifact> artifacts, @NotNull Project project) {
Set<Artifact> result = new HashSet<Artifact>();
for (Artifact artifact : artifacts) {
collectIncludedArtifacts(artifact, project, new HashSet<Artifact>(), result, true);
}
return result;
}
private static void collectIncludedArtifacts(Artifact artifact, final Project project,
final Set<Artifact> processed, final Set<Artifact> result, final boolean withOutputPathOnly) {
if (!processed.add(artifact)) {
return;
}
if (!withOutputPathOnly || !StringUtil.isEmpty(artifact.getOutputPath())) {
result.add(artifact);
}
processIncludedArtifacts(artifact, project, new Consumer<Artifact>() {
@Override
public void consume(Artifact included) {
collectIncludedArtifacts(included, project, processed, result, withOutputPathOnly);
}
});
}
private GraphGenerator<String> createArtifactsGraph() {
return GraphGenerator.create(CachingSemiGraph.create(new ArtifactsGraph(myProject)));
}
private static void processIncludedArtifacts(Artifact artifact,
final Project project,
final Consumer<Artifact> consumer) {
artifact.getRootElement().process(project, new Closure(consumer) {
@Override
public Object call(Object arguments) {
if (arguments instanceof ArtifactLayoutElement) {
final Artifact includedArtifact = ((ArtifactLayoutElement)arguments).findArtifact(project);
if (includedArtifact != null) {
consumer.consume(includedArtifact);
}
}
if (arguments instanceof ComplexLayoutElement) {
return false;
}
return true;
}
});
}
private static class ArtifactsGraph implements GraphGenerator.SemiGraph<String> {
private final Set<String> myArtifactNames;
private final Project myProject;
public ArtifactsGraph(Project project) {
myProject = project;
myArtifactNames = new LinkedHashSet<String>(project.getArtifacts().keySet());
}
@Override
public Collection<String> getNodes() {
return myArtifactNames;
}
@Override
public Iterator<String> getIn(String name) {
final Set<String> included = new LinkedHashSet<String>();
final Artifact artifact = myProject.getArtifacts().get(name);
if (artifact != null) {
final Consumer<Artifact> consumer = new Consumer<Artifact>() {
@Override
public void consume(Artifact artifact) {
if (myArtifactNames.contains(artifact.getName())) {
included.add(artifact.getName());
}
}
};
processIncludedArtifacts(artifact, myProject, consumer);
}
return included.iterator();
}
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.impl;
import com.intellij.openapi.util.Pair;
import org.jetbrains.jps.incremental.artifacts.instructions.JarDestinationInfo;
import org.jetbrains.jps.incremental.artifacts.instructions.JarInfo;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @author nik
*/
public class DependentJarsEvaluator {
private final Set<JarInfo> myJars = new LinkedHashSet<JarInfo>();
public void addJarWithDependencies(final JarInfo jarInfo) {
if (myJars.add(jarInfo)) {
for (JarDestinationInfo destination : jarInfo.getJarDestinations()) {
addJarWithDependencies(destination.getJarInfo());
}
for (Pair<String, JarInfo> pair : jarInfo.getPackedJars()) {
addJarWithDependencies(pair.getSecond());
}
}
}
public Set<JarInfo> getJars() {
return myJars;
}
}
@@ -0,0 +1,251 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.GraphGenerator;
import com.intellij.util.io.ZipUtil;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.artifacts.IncArtifactBuilder;
import org.jetbrains.jps.incremental.artifacts.instructions.*;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import java.io.*;
import java.util.*;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author nik
*/
public class JarsBuilder {
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.packagingCompiler.JarsBuilder");
private final Set<JarInfo> myJarsToBuild;
private final FileFilter myFileFilter;
private final CompileContext myContext;
private Map<JarInfo, File> myBuiltJars;
public JarsBuilder(Set<JarInfo> jarsToBuild, FileFilter fileFilter, CompileContext context) {
DependentJarsEvaluator evaluator = new DependentJarsEvaluator();
for (JarInfo jarInfo : jarsToBuild) {
evaluator.addJarWithDependencies(jarInfo);
}
myJarsToBuild = evaluator.getJars();
myFileFilter = fileFilter;
myContext = context;
}
public boolean buildJars(Set<String> writtenPaths) throws IOException {
myContext.processMessage(new ProgressMessage("Building archives..."));
final JarInfo[] sortedJars = sortJars();
if (sortedJars == null) {
return false;
}
myBuiltJars = new HashMap<JarInfo, File>();
try {
for (JarInfo jar : sortedJars) {
buildJar(jar);
}
myContext.processMessage(new ProgressMessage("Copying archives..."));
copyJars(writtenPaths);
}
finally {
deleteTemporaryJars();
}
return true;
}
private void deleteTemporaryJars() {
for (File file : myBuiltJars.values()) {
FileUtil.delete(file);
}
}
private void copyJars(final Set<String> writtenPaths) throws IOException {
for (Map.Entry<JarInfo, File> entry : myBuiltJars.entrySet()) {
File fromFile = entry.getValue();
boolean first = true;
for (DestinationInfo destination : entry.getKey().getAllDestinations()) {
if (destination instanceof ExplodedDestinationInfo) {
File toFile = new File(FileUtil.toSystemDependentName(destination.getOutputPath()));
if (first) {
first = false;
renameFile(fromFile, toFile, writtenPaths);
fromFile = toFile;
}
else {
FileUtil.copyContent(fromFile, toFile);
}
}
}
}
}
private static void renameFile(final File fromFile, final File toFile, final Set<String> writtenPaths) throws IOException {
FileUtil.rename(fromFile, toFile);
writtenPaths.add(toFile.getPath());
}
@Nullable
private JarInfo[] sortJars() {
final DFSTBuilder<JarInfo> builder = new DFSTBuilder<JarInfo>(GraphGenerator.create(CachingSemiGraph.create(new JarsGraph())));
if (!builder.isAcyclic()) {
final Pair<JarInfo, JarInfo> dependency = builder.getCircularDependency();
String message = "Cannot build: circular dependency found between '" + dependency.getFirst().getPresentableDestination() +
"' and '" + dependency.getSecond().getPresentableDestination() + "'";
myContext.processMessage(new CompilerMessage(IncArtifactBuilder.BUILDER_NAME, BuildMessage.Kind.ERROR, message));
return null;
}
JarInfo[] jars = myJarsToBuild.toArray(new JarInfo[myJarsToBuild.size()]);
Arrays.sort(jars, builder.comparator());
jars = ArrayUtil.reverseArray(jars);
return jars;
}
public Set<JarInfo> getJarsToBuild() {
return myJarsToBuild;
}
private void buildJar(final JarInfo jar) throws IOException {
if (jar.getPackedJars().isEmpty() && jar.getPackedRoots().isEmpty()) {
final String message = "Archive '" + jar.getPresentableDestination() + "' has no files so it won't be created";
myContext.processMessage(new CompilerMessage(IncArtifactBuilder.BUILDER_NAME, BuildMessage.Kind.WARNING, message));
return;
}
myContext.processMessage(new ProgressMessage("Building " + jar.getPresentableDestination() + "..."));
File jarFile = FileUtil.createTempFile("artifactCompiler", "tmp");
myBuiltJars.put(jar, jarFile);
FileUtil.createParentDirs(jarFile);
final JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)));
try {
final THashSet<String> writtenPaths = new THashSet<String>();
for (Pair<String, ArtifactSourceRoot> pair : jar.getPackedRoots()) {
final ArtifactSourceRoot root = pair.getSecond();
if (root instanceof FileBasedArtifactSourceRoot) {
addFileToJar(jarOutputStream, jarFile, root.getRootFile(), pair.getFirst(), writtenPaths);
}
else {
extractFileAndAddToJar(jarOutputStream, (JarBasedArtifactSourceRoot)root, pair.getFirst(), writtenPaths);
}
}
for (Pair<String, JarInfo> nestedJar : jar.getPackedJars()) {
File nestedJarFile = myBuiltJars.get(nestedJar.getSecond());
if (nestedJarFile != null) {
addFileToJar(jarOutputStream, jarFile, nestedJarFile, nestedJar.getFirst(), writtenPaths);
}
else {
LOG.debug("nested jar file " + nestedJar.getFirst() + " for " + jar.getPresentableDestination() + " not found");
}
}
}
finally {
jarOutputStream.close();
}
}
private void extractFileAndAddToJar(JarOutputStream jarOutputStream, JarBasedArtifactSourceRoot root, String relativePath, THashSet<String> writtenPaths)
throws IOException {
//todo[nik]
/*
relativePath = addParentDirectories(jarOutputStream, writtenPaths, relativePath);
if (!writtenPaths.add(relativePath)) return;
final BufferedInputStream input = ArtifactCompilerUtil.getJarEntryInputStream(root, myContext);
if (input == null) return;
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(root.getRootFile().lastModified());
jarOutputStream.putNextEntry(entry);
FileUtil.copy(input, jarOutputStream);
jarOutputStream.closeEntry();
*/
}
private void addFileToJar(final @NotNull JarOutputStream jarOutputStream, final @NotNull File jarFile, @NotNull File file,
@NotNull String relativePath, final @NotNull THashSet<String> writtenPaths) throws IOException {
if (!file.exists()) {
return;
}
relativePath = addParentDirectories(jarOutputStream, writtenPaths, relativePath);
ZipUtil.addFileOrDirRecursively(jarOutputStream, jarFile, file, relativePath, myFileFilter, writtenPaths);
}
private static String addParentDirectories(JarOutputStream jarOutputStream, THashSet<String> writtenPaths, String relativePath) throws IOException {
while (StringUtil.startsWithChar(relativePath, '/')) {
relativePath = relativePath.substring(1);
}
int i = relativePath.indexOf('/');
while (i != -1) {
String prefix = relativePath.substring(0, i+1);
if (!writtenPaths.contains(prefix) && prefix.length() > 1) {
addEntry(jarOutputStream, prefix);
writtenPaths.add(prefix);
}
i = relativePath.indexOf('/', i + 1);
}
return relativePath;
}
private static void addEntry(final ZipOutputStream output, @NonNls final String relativePath) throws IOException {
ZipEntry e = new ZipEntry(relativePath);
e.setMethod(ZipEntry.STORED);
e.setSize(0);
e.setCrc(0);
output.putNextEntry(e);
output.closeEntry();
}
private class JarsGraph implements GraphGenerator.SemiGraph<JarInfo> {
public Collection<JarInfo> getNodes() {
return myJarsToBuild;
}
public Iterator<JarInfo> getIn(final JarInfo n) {
Set<JarInfo> ins = new HashSet<JarInfo>();
for (JarDestinationInfo destination : n.getJarDestinations()) {
ins.add(destination.getJarInfo());
}
return ins.iterator();
}
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
/**
* @author nik
*/
public interface ArtifactCompilerInstructionCreator {
void addFileCopyInstruction(@NotNull File file, @NotNull String outputFileName);
void addDirectoryCopyInstructions(@NotNull File directory);
void addExtractDirectoryInstruction(@NotNull File jarFile, @NotNull String pathInJar);
void addDirectoryCopyInstructions(@NotNull File directory, @Nullable SourceFileFilter filter);
ArtifactCompilerInstructionCreator subFolder(@NotNull String directoryName);
ArtifactCompilerInstructionCreator archive(@NotNull String archiveFileName);
ArtifactCompilerInstructionCreator subFolderByRelativePath(@NotNull String relativeDirectoryPath);
}
@@ -0,0 +1,91 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.ModuleRootsIndex;
import org.jetbrains.jps.incremental.artifacts.JarPathUtil;
import java.io.File;
import java.util.List;
/**
* @author nik
*/
public abstract class ArtifactCompilerInstructionCreatorBase implements ArtifactCompilerInstructionCreator {
protected final ArtifactInstructionsBuilderImpl myInstructionsBuilder;
public ArtifactCompilerInstructionCreatorBase(ArtifactInstructionsBuilderImpl instructionsBuilder) {
myInstructionsBuilder = instructionsBuilder;
}
public void addDirectoryCopyInstructions(@NotNull File directoryUrl) {
addDirectoryCopyInstructions(directoryUrl, null);
}
public void addDirectoryCopyInstructions(@NotNull File directory, @Nullable SourceFileFilter filter) {
final boolean copyExcluded = myInstructionsBuilder.getRootsIndex().isExcluded(directory);
SourceFileFilter fileFilter = new SourceFileFilterImpl(filter, myInstructionsBuilder.getRootsIndex(), copyExcluded);
addDirectoryCopyInstructions(new FileBasedArtifactSourceRoot(directory, fileFilter));
}
@Override
public void addExtractDirectoryInstruction(@NotNull File jarFile, @NotNull String pathInJar) {
addDirectoryCopyInstructions(new JarBasedArtifactSourceRoot(jarFile, pathInJar, new SourceFileFilterImpl(null, myInstructionsBuilder.getRootsIndex(), false)));
}
protected abstract void addDirectoryCopyInstructions(ArtifactSourceRoot root);
@Override
public abstract ArtifactCompilerInstructionCreatorBase subFolder(@NotNull String directoryName);
public ArtifactCompilerInstructionCreator subFolderByRelativePath(@NotNull String relativeDirectoryPath) {
final List<String> folders = StringUtil.split(relativeDirectoryPath, "/");
ArtifactCompilerInstructionCreator current = this;
for (String folder : folders) {
current = current.subFolder(folder);
}
return current;
}
private static class SourceFileFilterImpl extends SourceFileFilter {
private final SourceFileFilter myBaseFilter;
private final ModuleRootsIndex myRootsIndex;
private final boolean myIncludeExcluded;
private SourceFileFilterImpl(@Nullable SourceFileFilter baseFilter, @NotNull ModuleRootsIndex rootsIndex, boolean includeExcluded) {
myBaseFilter = baseFilter;
myRootsIndex = rootsIndex;
myIncludeExcluded = includeExcluded;
}
@Override
public boolean accept(@NotNull String fullFilePath) {
if (myBaseFilter != null && !myBaseFilter.accept(fullFilePath)) return false;
//todo[nik] check FileTypeManager.isFileIgnored()
if (!myIncludeExcluded) {
final File file = JarPathUtil.getLocalFile(fullFilePath);
if (myRootsIndex.isExcluded(file)) {
return false;
}
}
return true;
}
}
}
@@ -0,0 +1,15 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.Nullable;
/**
* @author nik
*/
public interface ArtifactInstructionsBuilder {
void processRoots(ArtifactRootProcessor processor) throws Exception;
void processContainingRoots(String filePath, ArtifactRootProcessor processor) throws Exception;
@Nullable
JarInfo getJarInfo(String outputPath);
}
@@ -0,0 +1,16 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.ProjectPaths;
/**
* @author nik
*/
public interface ArtifactInstructionsBuilderContext {
@NotNull
Project getProject();
@NotNull
ProjectPaths getProjectPaths();
}
@@ -0,0 +1,30 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.ProjectPaths;
/**
* @author nik
*/
public class ArtifactInstructionsBuilderContextImpl implements ArtifactInstructionsBuilderContext {
private final Project myProject;
private final ProjectPaths myProjectPaths;
public ArtifactInstructionsBuilderContextImpl(Project project, ProjectPaths projectPaths) {
myProject = project;
myProjectPaths = projectPaths;
}
@NotNull
@Override
public Project getProject() {
return myProject;
}
@NotNull
@Override
public ProjectPaths getProjectPaths() {
return myProjectPaths;
}
}
@@ -0,0 +1,89 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.ModuleRootsIndex;
import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* @author nik
*/
public class ArtifactInstructionsBuilderImpl implements ArtifactInstructionsBuilder {
private final Map<String, ArtifactSourceRoot> mySourceByOutput;
private final Map<String, JarInfo> myJarByPath;
private final MultiMap<ArtifactSourceRoot, DestinationInfo> myInstructions;
private final ModuleRootsIndex myRootsIndex;
public ArtifactInstructionsBuilderImpl(ModuleRootsIndex rootsIndex) {
myRootsIndex = rootsIndex;
mySourceByOutput = new HashMap<String, ArtifactSourceRoot>();
myJarByPath = new HashMap<String, JarInfo>();
myInstructions = new MultiMap<ArtifactSourceRoot, DestinationInfo>();
}
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())))) {
return false;
}
if (checkOutputPath(destinationInfo.getOutputPath(), root)) {
myInstructions.putValue(root, destinationInfo);
return true;
}
return false;
}
public ModuleRootsIndex getRootsIndex() {
return myRootsIndex;
}
public boolean checkOutputPath(final String outputPath, final ArtifactSourceRoot sourceFile) {
//todo[nik] combine intersecting roots
ArtifactSourceRoot old = mySourceByOutput.get(outputPath);
if (old == null) {
mySourceByOutput.put(outputPath, sourceFile);
return true;
}
//todo[nik] show warning?
return false;
}
public boolean registerJarFile(@NotNull JarInfo jarInfo, @NotNull String outputPath) {
if (mySourceByOutput.containsKey(outputPath) || myJarByPath.containsKey(outputPath)) {
return false;
}
myJarByPath.put(outputPath, jarInfo);
return true;
}
@Override
@Nullable
public JarInfo getJarInfo(String outputPath) {
return myJarByPath.get(outputPath);
}
@Override
public void processRoots(ArtifactRootProcessor processor) throws Exception {
for (Map.Entry<ArtifactSourceRoot, Collection<DestinationInfo>> entry : myInstructions.entrySet()) {
processor.process(entry.getKey(), entry.getValue());
}
}
@Override
public void processContainingRoots(String filePath, ArtifactRootProcessor processor) throws Exception {
//todo[nik] improve?
for (Map.Entry<ArtifactSourceRoot, Collection<DestinationInfo>> entry : myInstructions.entrySet()) {
final ArtifactSourceRoot root = entry.getKey();
if (root.containsFile(filePath)) {
processor.process(root, entry.getValue());
}
}
}
}
@@ -0,0 +1,10 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import java.util.Collection;
/**
* @author nik
*/
public interface ArtifactRootProcessor {
void process(ArtifactSourceRoot root, Collection<DestinationInfo> destinations) throws Exception;
}
@@ -0,0 +1,37 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author nik
*/
public abstract class ArtifactSourceRoot {
private final SourceFileFilter myFilter;
protected ArtifactSourceRoot(@NotNull SourceFileFilter filter) {
myFilter = filter;
}
@NotNull
public abstract File getRootFile();
public abstract boolean containsFile(String filePath);
public SourceFileFilter getFilter() {
return myFilter;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
return myFilter.equals(((ArtifactSourceRoot)o).myFilter);
}
@Override
public int hashCode() {
return myFilter.hashCode();
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author nik
*/
public class CopyToDirectoryInstructionCreator extends ArtifactCompilerInstructionCreatorBase {
private final String myOutputPath;
public CopyToDirectoryInstructionCreator(ArtifactInstructionsBuilderImpl builder, String outputPath) {
super(builder);
myOutputPath = outputPath;
}
public void addFileCopyInstruction(@NotNull File file, @NotNull String outputFileName) {
myInstructionsBuilder.addDestination(new FileBasedArtifactSourceRoot(file, SourceFileFilter.ALL), new ExplodedDestinationInfo(myOutputPath + "/" + outputFileName));
}
@Override
protected void addDirectoryCopyInstructions(ArtifactSourceRoot root) {
myInstructionsBuilder.addDestination(root, new ExplodedDestinationInfo(myOutputPath));
}
public CopyToDirectoryInstructionCreator subFolder(@NotNull String directoryName) {
return new CopyToDirectoryInstructionCreator(myInstructionsBuilder, myOutputPath + "/" + directoryName);
}
public ArtifactCompilerInstructionCreator archive(@NotNull String archiveFileName) {
String jarOutputPath = myOutputPath + "/" + archiveFileName;
final JarInfo jarInfo = new JarInfo();
if (!myInstructionsBuilder.registerJarFile(jarInfo, jarOutputPath)) {
return new SkipAllInstructionCreator(myInstructionsBuilder);
}
final ExplodedDestinationInfo destination = new ExplodedDestinationInfo(jarOutputPath);
jarInfo.addDestination(destination);
return new PackIntoArchiveInstructionCreator(myInstructionsBuilder, jarInfo, "", destination);
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
/**
* @author nik
*/
public abstract class DestinationInfo {
private final String myOutputPath;
private final String myOutputFilePath;
protected DestinationInfo(@NotNull final String outputPath, @NotNull String outputFilePath) {
myOutputFilePath = outputFilePath;
myOutputPath = outputPath;
}
@NotNull
public String getOutputPath() {
return myOutputPath;
}
@NotNull
public String getOutputFilePath() {
return myOutputFilePath;
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
/**
* @author nik
*/
public class ExplodedDestinationInfo extends DestinationInfo {
public ExplodedDestinationInfo(final String outputPath) {
super(outputPath, outputPath);
}
public String toString() {
return getOutputPath();
}
}
@@ -0,0 +1,43 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author nik
*/
public class FileBasedArtifactSourceRoot extends ArtifactSourceRoot {
private final File myFile;
public FileBasedArtifactSourceRoot(@NotNull File file, @NotNull SourceFileFilter filter) {
super(filter);
myFile = file;
}
@NotNull
@Override
public File getRootFile() {
return myFile;
}
@Override
public boolean containsFile(String filePath) {
return FileUtil.isAncestor(myFile, new File(FileUtil.toSystemDependentName(filePath)), false) && getFilter().accept(filePath);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
return myFile.equals(((FileBasedArtifactSourceRoot)o).myFile);
}
@Override
public int hashCode() {
return 31 * super.hashCode() + myFile.hashCode();
}
}
@@ -0,0 +1,46 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author nik
*/
public class JarBasedArtifactSourceRoot extends ArtifactSourceRoot {
private final File myJarFile;
private final String myPathInJar;
public JarBasedArtifactSourceRoot(@NotNull File jarFile, @NotNull String pathInJar, @NotNull SourceFileFilter filter) {
super(filter);
myJarFile = jarFile;
myPathInJar = pathInJar;
}
@NotNull
@Override
public File getRootFile() {
return myJarFile;
}
@Override
public boolean containsFile(String filePath) {
return new File(FileUtil.toSystemDependentName(filePath)).equals(myJarFile);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
JarBasedArtifactSourceRoot root = (JarBasedArtifactSourceRoot)o;
return myJarFile.equals(root.myJarFile) && myPathInJar.equals(root.myPathInJar);
}
@Override
public int hashCode() {
return 31 * (31 * super.hashCode() + myJarFile.hashCode()) + myPathInJar.hashCode();
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
/**
* @author nik
*/
public class JarDestinationInfo extends DestinationInfo {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.artifacts.instructions.JarDestinationInfo");
private final String myPathInJar;
private final JarInfo myJarInfo;
public JarDestinationInfo(final String pathInJar, final JarInfo jarInfo, DestinationInfo jarDestination) {
super(appendPathInJar(jarDestination.getOutputPath(), pathInJar), jarDestination.getOutputFilePath());
LOG.assertTrue(!pathInJar.startsWith(".."), pathInJar);
myPathInJar = StringUtil.startsWithChar(pathInJar, '/') ? pathInJar : "/" + pathInJar;
myJarInfo = jarInfo;
}
private static String appendPathInJar(String outputPath, String pathInJar) {
LOG.assertTrue(outputPath.length() > 0 && outputPath.charAt(outputPath.length() - 1) != '/');
LOG.assertTrue(pathInJar.length() > 0 && pathInJar.charAt(0) != '/');
return outputPath + "!/" + pathInJar;
}
public String getPathInJar() {
return myPathInJar;
}
public JarInfo getJarInfo() {
return myJarInfo;
}
public String toString() {
return myPathInJar + "(" + getOutputPath() + ")";
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import com.intellij.openapi.util.Pair;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
* @author nik
*/
public class JarInfo {
private final List<Pair<String, ArtifactSourceRoot>> myPackedRoots;
private final LinkedHashSet<Pair<String, JarInfo>> myPackedJars;
private final List<DestinationInfo> myDestinations;
public JarInfo() {
myDestinations = new ArrayList<DestinationInfo>();
myPackedRoots = new ArrayList<Pair<String, ArtifactSourceRoot>>();
myPackedJars = new LinkedHashSet<Pair<String, JarInfo>>();
}
public void addDestination(DestinationInfo info) {
myDestinations.add(info);
if (info instanceof JarDestinationInfo) {
JarDestinationInfo destinationInfo = (JarDestinationInfo)info;
destinationInfo.getJarInfo().myPackedJars.add(Pair.create(destinationInfo.getPathInJar(), this));
}
}
public void addContent(String pathInJar, ArtifactSourceRoot sourceFile) {
myPackedRoots.add(Pair.create(pathInJar, sourceFile));
}
public List<Pair<String, ArtifactSourceRoot>> getPackedRoots() {
return myPackedRoots;
}
public LinkedHashSet<Pair<String, JarInfo>> getPackedJars() {
return myPackedJars;
}
public List<JarDestinationInfo> getJarDestinations() {
final ArrayList<JarDestinationInfo> list = new ArrayList<JarDestinationInfo>();
for (DestinationInfo destination : myDestinations) {
if (destination instanceof JarDestinationInfo) {
list.add((JarDestinationInfo)destination);
}
}
return list;
}
public List<DestinationInfo> getAllDestinations() {
return myDestinations;
}
public String getPresentableDestination() {
return !myDestinations.isEmpty() ? myDestinations.get(0).getOutputPath() : "";
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author nik
*/
public class PackIntoArchiveInstructionCreator extends ArtifactCompilerInstructionCreatorBase {
private final DestinationInfo myJarDestination;
private final JarInfo myJarInfo;
private final String myPathInJar;
public PackIntoArchiveInstructionCreator(ArtifactInstructionsBuilderImpl builder, JarInfo jarInfo,
String pathInJar, DestinationInfo jarDestination) {
super(builder);
myJarInfo = jarInfo;
myPathInJar = pathInJar;
myJarDestination = jarDestination;
}
@Override
protected void addDirectoryCopyInstructions(ArtifactSourceRoot root) {
addCopyInstruction(myPathInJar, root);
}
public void addFileCopyInstruction(@NotNull File file, @NotNull String outputFileName) {
addCopyInstruction(childPathInJar(outputFileName), new FileBasedArtifactSourceRoot(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 String childPathInJar(String fileName) {
return myPathInJar.length() == 0 ? fileName : myPathInJar + "/" + fileName;
}
public PackIntoArchiveInstructionCreator subFolder(@NotNull String directoryName) {
return new PackIntoArchiveInstructionCreator(myInstructionsBuilder, myJarInfo, childPathInJar(directoryName), myJarDestination);
}
public ArtifactCompilerInstructionCreator archive(@NotNull String archiveFileName) {
final JarInfo jarInfo = new JarInfo();
final String outputPath = myJarDestination.getOutputPath() + "/" + archiveFileName;
if (!myInstructionsBuilder.registerJarFile(jarInfo, outputPath)) {
return new SkipAllInstructionCreator(myInstructionsBuilder);
}
final JarDestinationInfo destination = new JarDestinationInfo(childPathInJar(archiveFileName), myJarInfo, myJarDestination);
jarInfo.addDestination(destination);
return new PackIntoArchiveInstructionCreator(myInstructionsBuilder, jarInfo, "", destination);
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
import java.io.File;
/**
* @author nik
*/
public class SkipAllInstructionCreator extends ArtifactCompilerInstructionCreatorBase {
public SkipAllInstructionCreator(ArtifactInstructionsBuilderImpl builder) {
super(builder);
}
public void addFileCopyInstruction(@NotNull File file, @NotNull String outputFileName) {
}
@Override
protected void addDirectoryCopyInstructions(ArtifactSourceRoot root) {
}
public SkipAllInstructionCreator subFolder(@NotNull String directoryName) {
return this;
}
public SkipAllInstructionCreator archive(@NotNull String archiveFileName) {
return this;
}
}
@@ -0,0 +1,17 @@
package org.jetbrains.jps.incremental.artifacts.instructions;
import org.jetbrains.annotations.NotNull;
/**
* @author nik
*/
public abstract class SourceFileFilter {
public static final SourceFileFilter ALL = new SourceFileFilter() {
@Override
public boolean accept(@NotNull String fullFilePath) {
return true;
}
};
public abstract boolean accept(@NotNull String fullFilePath);
}
@@ -5,6 +5,7 @@ import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.io.DataOutput;
import java.io.File;
@@ -64,7 +65,7 @@ public abstract class AbstractStateStorage<Key, T> {
}
}
public void update(Key key, T state) throws Exception {
public void update(Key key, @Nullable T state) throws Exception {
if (state != null) {
synchronized (myDataLock) {
myMap.put(key, state);
@@ -115,4 +116,12 @@ public abstract class AbstractStateStorage<Key, T> {
return new PersistentHashMap<Key,T>(file, myKeyDescriptor, myStateExternalizer);
}
public void flush(boolean memoryCachesOnly) {
if (memoryCachesOnly) {
dropMemoryCache();
}
else {
force();
}
}
}
@@ -5,6 +5,7 @@ import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.ether.dependencyView.Mappings;
import org.jetbrains.jps.incremental.Paths;
import org.jetbrains.jps.incremental.artifacts.ArtifactsBuildData;
import java.io.File;
import java.io.IOException;
@@ -27,12 +28,15 @@ public class BuildDataManager {
private final Map<String, SourceToOutputMapping> myTestSourceToOutputs = new HashMap<String, SourceToOutputMapping>();
private final SourceToFormMapping mySrcToFormMap;
private final ArtifactsBuildData myArtifactsBuildData;
private final Mappings myMappings;
public BuildDataManager(String projectName, final boolean useMemoryTempCaches) throws Exception {
myProjectName = projectName;
mySrcToFormMap = new SourceToFormMapping(new File(getSourceToFormsRoot(), "data"));
myMappings = new Mappings(getMappingsRoot(), useMemoryTempCaches);
final File artifactsDataDir = new File(Paths.getDataStorageRoot(projectName), "artifacts");
myArtifactsBuildData = new ArtifactsBuildData(artifactsDataDir);
}
public SourceToOutputMapping getSourceToOutputMap(String moduleName, boolean testSources) throws Exception {
@@ -48,6 +52,10 @@ public class BuildDataManager {
return mapping;
}
public ArtifactsBuildData getArtifactsBuildData() {
return myArtifactsBuildData;
}
public SourceToFormMapping getSourceToFormMap() {
return mySrcToFormMap;
}
@@ -58,60 +66,51 @@ public class BuildDataManager {
public void clean() throws IOException {
try {
synchronized (mySourceToOutputLock) {
try {
closeOutputToSourceStorages();
}
finally {
FileUtil.delete(getSourceToOutputsRoot());
}
}
myArtifactsBuildData.clean();
}
finally {
try {
wipeStorage(getSourceToFormsRoot(), mySrcToFormMap);
}
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
mappings.clean();
synchronized (mySourceToOutputLock) {
try {
closeOutputToSourceStorages();
}
finally {
FileUtil.delete(getSourceToOutputsRoot());
}
}
else {
FileUtil.delete(getMappingsRoot());
}
finally {
try {
wipeStorage(getSourceToFormsRoot(), mySrcToFormMap);
}
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
mappings.clean();
}
}
else {
FileUtil.delete(getMappingsRoot());
}
}
}
}
}
public void flush(boolean memoryCachesOnly) {
myArtifactsBuildData.flush(memoryCachesOnly);
synchronized (mySourceToOutputLock) {
for (Map.Entry<String, SourceToOutputMapping> entry : myProductionSourceToOutputs.entrySet()) {
final SourceToOutputMapping mapping = entry.getValue();
if (memoryCachesOnly) {
mapping.dropMemoryCache();
}
else {
mapping.force();
}
mapping.flush(memoryCachesOnly);
}
for (Map.Entry<String, SourceToOutputMapping> entry : myTestSourceToOutputs.entrySet()) {
final SourceToOutputMapping mapping = entry.getValue();
if (memoryCachesOnly) {
mapping.dropMemoryCache();
}
else {
mapping.force();
}
mapping.flush(memoryCachesOnly);
}
}
if (memoryCachesOnly) {
mySrcToFormMap.dropMemoryCache();
}
else {
mySrcToFormMap.force();
}
mySrcToFormMap.flush(memoryCachesOnly);
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
@@ -122,27 +121,32 @@ public class BuildDataManager {
public void close() throws IOException {
try {
synchronized (mySourceToOutputLock) {
closeOutputToSourceStorages();
}
myArtifactsBuildData.close();
}
finally {
try {
closeStorage(mySrcToFormMap);
synchronized (mySourceToOutputLock) {
closeOutputToSourceStorages();
}
}
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
try {
mappings.close();
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw ((IOException)cause);
try {
closeStorage(mySrcToFormMap);
}
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
try {
mappings.close();
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw ((IOException)cause);
}
throw e;
}
throw e;
}
}
}
@@ -158,7 +162,7 @@ public class BuildDataManager {
closeStorage(entry.getValue());
}
catch (IOException e) {
if (ex != null) {
if (e != null) {
ex = e;
}
}
@@ -168,7 +172,7 @@ public class BuildDataManager {
closeStorage(entry.getValue());
}
catch (IOException e) {
if (ex != null) {
if (e != null) {
ex = e;
}
}
@@ -82,7 +82,11 @@ public class ClasspathBootstrap {
cp.add(getResourcePath(FileMonitor.class)); // jna-utils.jar
cp.add(getResourcePath(ClassWriter.class)); // asm
cp.add(getResourcePath(org.objectweb.asm.commons.EmptyVisitor.class)); // asm-commons
cp.add(getResourcePath(MacroExpander.class)); // jps-model
final File jpsModel = getResourcePath(MacroExpander.class);
cp.add(jpsModel); // jps-model
cp.add(new File(jpsModel.getParentFile(), "jps-javaee"));
cp.add(new File(jpsModel.getParentFile(), "jps-gwt"));
cp.add(new File(jpsModel.getParentFile(), "jps-jpa"));
cp.add(getResourcePath(AlienFormFileException.class)); // forms-compiler
cp.add(getResourcePath(GroovyException.class)); // groovy
cp.add(getResourcePath(org.jdom.input.SAXBuilder.class)); // jdom
@@ -188,8 +188,7 @@ class ServerMessageHandler extends SimpleChannelHandler {
channelContext.setAttachment(sessionId);
final BuildType buildType = convertCompileType(compileType);
final CompilationTask task = new CompilationTask(
sessionId, channelContext, projectId, buildType, compileRequest.getModuleNameList(), compileRequest.getFilePathList()
);
sessionId, channelContext, projectId, buildType, compileRequest.getModuleNameList(), Collections.<String>emptySet(), compileRequest.getFilePathList());
final RunnableFuture future = getCompileTaskExecutor(projectId).submit(task);
myBuildsInProgress.add(new Pair<RunnableFuture, CompilationTask>(future, task));
return null;
@@ -230,6 +229,7 @@ class ServerMessageHandler extends SimpleChannelHandler {
private final ChannelHandlerContext myChannelContext;
private final String myProjectPath;
private final BuildType myBuildType;
private final Collection<String> myArtifacts;
private final Collection<String> myPaths;
private final Set<String> myModules;
private volatile boolean myCanceled = false;
@@ -239,11 +239,13 @@ class ServerMessageHandler extends SimpleChannelHandler {
String projectId,
BuildType buildType,
Collection<String> modules,
Collection<String> artifacts,
Collection<String> paths) {
mySessionId = sessionId;
myChannelContext = channelContext;
myProjectPath = projectId;
myBuildType = buildType;
myArtifacts = artifacts;
myPaths = paths;
myModules = new HashSet<String>(modules);
}
@@ -262,7 +264,7 @@ class ServerMessageHandler extends SimpleChannelHandler {
final Ref<Boolean> hasErrors = new Ref<Boolean>(false);
final Ref<Boolean> markedFilesUptodate = new Ref<Boolean>(false);
try {
ServerState.getInstance().startBuild(myProjectPath, myBuildType, myModules, myPaths, new MessageHandler() {
ServerState.getInstance().startBuild(myProjectPath, myBuildType, myModules, myArtifacts, myPaths, new MessageHandler() {
public void processMessage(BuildMessage buildMessage) {
final JpsRemoteProto.Message.Response response;
if (buildMessage instanceof FileGeneratedEvent) {
@@ -13,6 +13,7 @@ import org.jetbrains.jps.api.BuildType;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.api.GlobalLibrary;
import org.jetbrains.jps.api.SdkLibrary;
import org.jetbrains.jps.artifacts.Artifact;
import org.jetbrains.jps.idea.IdeaProjectLoader;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.messages.BuildMessage;
@@ -119,7 +120,8 @@ class ServerState {
}
}
public void startBuild(String projectPath, BuildType buildType, Set<String> modules, Collection<String> paths, final MessageHandler msgHandler, CanceledStatus cs) throws Throwable{
public void startBuild(String projectPath, BuildType buildType, Set<String> modules, Collection<String> artifacts,
Collection<String> paths, final MessageHandler msgHandler, CanceledStatus cs) throws Throwable{
final String projectName = getProjectName(projectPath);
@@ -161,7 +163,7 @@ class ServerState {
final Project project = pd.project;
try {
final CompileScope compileScope = createCompilationScope(buildType, pd, modules, paths);
final CompileScope compileScope = createCompilationScope(buildType, pd, modules, artifacts, paths);
final IncProjectBuilder builder = new IncProjectBuilder(pd, BuilderRegistry.getInstance(), cs);
if (msgHandler != null) {
builder.addMessageHandler(msgHandler);
@@ -191,17 +193,32 @@ class ServerState {
}
}
private static CompileScope createCompilationScope(BuildType buildType, ProjectDescriptor pd, Set<String> modules, Collection<String> paths) throws Exception {
private static CompileScope createCompilationScope(BuildType buildType, ProjectDescriptor pd, Set<String> modules,
Collection<String> artifactNames, Collection<String> paths) throws Exception {
Set<Artifact> artifacts = new HashSet<Artifact>();
if (artifactNames.isEmpty() && buildType == BuildType.PROJECT_REBUILD) {
artifacts.addAll(pd.project.getArtifacts().values());
}
else {
final Map<String, Artifact> artifactMap = pd.project.getArtifacts();
for (String name : artifactNames) {
final Artifact artifact = artifactMap.get(name);
if (!StringUtil.isEmpty(artifact.getOutputPath())) {
artifacts.add(artifact);
}
}
}
final CompileScope compileScope;
if (buildType == BuildType.PROJECT_REBUILD || (modules.isEmpty() && paths.isEmpty())) {
compileScope = new AllProjectScope(pd.project, buildType != BuildType.MAKE);
compileScope = new AllProjectScope(pd.project, artifacts, buildType != BuildType.MAKE);
}
else {
final Set<Module> forcedModules;
if (!modules.isEmpty()) {
forcedModules = new HashSet<Module>();
for (Module m : pd.project.getModules().values()) {
if (modules.contains(m.getName())){
if (modules.contains(m.getName())) {
forcedModules.add(m);
}
}
@@ -236,10 +253,10 @@ class ServerState {
}
if (filesToCompile.isEmpty()) {
compileScope = new ModulesScope(pd.project, forcedModules, buildType != BuildType.MAKE);
compileScope = new ModulesScope(pd.project, forcedModules, artifacts, buildType != BuildType.MAKE);
}
else {
compileScope = new ModulesAndFilesScope(pd.project, forcedModules, filesToCompile, buildType != BuildType.MAKE);
compileScope = new ModulesAndFilesScope(pd.project, forcedModules, filesToCompile, artifacts, buildType != BuildType.MAKE);
}
}
return compileScope;
@@ -28,6 +28,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.Project;
import org.jetbrains.jps.Sdk;
import org.jetbrains.jps.api.CanceledStatus;
import org.jetbrains.jps.artifacts.Artifact;
import org.jetbrains.jps.idea.IdeaProjectLoader;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.storage.BuildDataManager;
@@ -36,6 +37,7 @@ import org.jetbrains.jps.server.ClasspathBootstrap;
import org.jetbrains.jps.server.ProjectDescriptor;
import java.io.*;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
@@ -320,7 +322,7 @@ public abstract class IncrementalTestCase extends TestCase {
new IncProjectBuilder(
projectDescriptor, BuilderRegistry.getInstance(), CanceledStatus.NULL
).build(
new AllProjectScope(project, true), false, true
new AllProjectScope(project, Collections.<Artifact>emptySet(), true), false, true
);
modify();
@@ -332,7 +334,7 @@ public abstract class IncrementalTestCase extends TestCase {
new IncProjectBuilder(
projectDescriptor, BuilderRegistry.getInstance(), CanceledStatus.NULL
).build(
new AllProjectScope(project, false), true, false
new AllProjectScope(project, Collections.<Artifact>emptySet(), false), true, false
);
FileAssert.assertEquals(new File(getBaseDir() + ".log"), new File(getWorkDir() + ".log"));
@@ -1,6 +1,8 @@
package org.jetbrains.jps;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.net.URI;
@@ -10,6 +12,30 @@ import java.util.Set;
* @author nik
*/
public class PathUtil {
//todo[nik] copied from DeploymentUtil
public static String trimForwardSlashes(@NotNull String path) {
while (path.length() != 0 && (path.charAt(0) == '/' || path.charAt(0) == File.separatorChar)) {
path = path.substring(1);
}
return path;
}
//todo[nik] copied from DeploymentUtil
public static String appendToPath(@NotNull String basePath, @NotNull String relativePath) {
final boolean endsWithSlash = StringUtil.endsWithChar(basePath, '/') || StringUtil.endsWithChar(basePath, '\\');
final boolean startsWithSlash = StringUtil.startsWithChar(relativePath, '/') || StringUtil.startsWithChar(relativePath, '\\');
String tail;
if (endsWithSlash && startsWithSlash) {
tail = trimForwardSlashes(relativePath);
}
else if (!endsWithSlash && !startsWithSlash && basePath.length() > 0 && relativePath.length() > 0) {
tail = "/" + relativePath;
}
else {
tail = relativePath;
}
return basePath + tail;
}
public static String toPath(URI uri) {
if (uri.getScheme() == null) {