mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
artifact builder for compile server
This commit is contained in:
@@ -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();
|
||||
}
|
||||
+16
@@ -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();
|
||||
}
|
||||
}
|
||||
+114
@@ -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);
|
||||
}
|
||||
}
|
||||
+189
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -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));
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
+206
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+202
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -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);
|
||||
}
|
||||
+91
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -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);
|
||||
}
|
||||
+16
@@ -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();
|
||||
}
|
||||
+30
@@ -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;
|
||||
}
|
||||
}
|
||||
+89
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -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;
|
||||
}
|
||||
+37
@@ -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();
|
||||
}
|
||||
}
|
||||
+56
@@ -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);
|
||||
}
|
||||
}
|
||||
+41
@@ -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;
|
||||
}
|
||||
}
|
||||
+30
@@ -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();
|
||||
}
|
||||
}
|
||||
+43
@@ -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();
|
||||
}
|
||||
}
|
||||
+46
@@ -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();
|
||||
}
|
||||
}
|
||||
+54
@@ -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() + ")";
|
||||
}
|
||||
}
|
||||
+76
@@ -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() : "";
|
||||
}
|
||||
}
|
||||
+71
@@ -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);
|
||||
}
|
||||
}
|
||||
+44
@@ -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;
|
||||
}
|
||||
}
|
||||
+17
@@ -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);
|
||||
}
|
||||
+10
-1
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user