external build: handle module output directory changes/module renames: recompile whole module

ensure up-to-date project model is used by the build by saving the project before compilation
faster rebuild in case of forced storage cleanup
This commit is contained in:
Eugene Zhuravlev
2012-06-18 23:11:45 +02:00
parent 27eeb01272
commit 60e1732968
6 changed files with 159 additions and 24 deletions
@@ -648,7 +648,10 @@ public class CompileDriver {
new CompilerTask(myProject, compileInBackground, contentName, ApplicationManager.getApplication().isUnitTestMode());
StatusBar.Info.set("", myProject, "Compiler");
if (useOutOfProcessBuild() && BuildManager.getInstance().rescanRequired(myProject)) {
// ensure the project model seen by build process is up-to-date
myProject.save();
}
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
FileDocumentManager.getInstance().saveAllDocuments();
@@ -686,7 +689,7 @@ public class CompileDriver {
compileContext.addMessage(message);
}
final Collection<String> paths = fetchFiles(compileContext);
final List<Module> modules = paths.isEmpty()? Arrays.asList(compileContext.getCompileScope().getAffectedModules()) : Collections.<Module>emptyList();
final List<Module> modules = paths.isEmpty() && !isRebuild && !allProjectModulesAffected(compileContext)? Arrays.asList(compileContext.getCompileScope().getAffectedModules()) : Collections.<Module>emptyList();
final Set<Artifact> artifacts = ArtifactCompileScope.getArtifactsToBuild(myProject, compileContext.getCompileScope(), true);
final RequestFuture future = compileInExternalProcess(compileContext, modules, artifacts, paths, callback);
if (future != null) {
@@ -780,6 +783,12 @@ public class CompileDriver {
});
}
private static boolean allProjectModulesAffected(CompileContextImpl compileContext) {
final Set<Module> allModules = new HashSet<Module>(Arrays.asList(compileContext.getProjectCompileScope().getAffectedModules()));
allModules.removeAll(Arrays.asList(compileContext.getCompileScope().getAffectedModules()));
return allModules.isEmpty();
}
private static List<String> fetchFiles(CompileContextImpl context) {
if (context.isRebuild()) {
return Collections.emptyList();
@@ -78,7 +78,8 @@ import org.jetbrains.jps.cmdline.BuildMain;
import org.jetbrains.jps.server.ClasspathBootstrap;
import org.jetbrains.jps.server.Server;
import javax.tools.*;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -222,6 +223,14 @@ public class BuildManager implements ApplicationComponent{
}
}
public boolean rescanRequired(Project project) {
final String projectPath = getProjectPath(project);
synchronized (myProjectDataMap) {
final ProjectData data = myProjectDataMap.get(projectPath);
return data == null || data.myNeedRescan;
}
}
@Nullable
private static String getProjectPath(final Project project) {
final String path = project.getPresentableUrl();
@@ -2,6 +2,8 @@ package org.jetbrains.jps.incremental;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.EventDispatcher;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -14,6 +16,7 @@ import org.jetbrains.jps.incremental.messages.FileGeneratedEvent;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.incremental.messages.UptoDateFilesSavedEvent;
import org.jetbrains.jps.incremental.storage.BuildDataManager;
import org.jetbrains.jps.incremental.storage.ModuleOutputRootsLayout;
import org.jetbrains.jps.incremental.storage.SourceToOutputMapping;
import org.jetbrains.jps.incremental.storage.Timestamps;
import org.jetbrains.jps.server.ProjectDescriptor;
@@ -350,17 +353,20 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler
for (Module module : chunk.getModules()) {
if (isProjectRebuild()) {
markDirtyFiles(module, myTimestamps, true, isCompilingTests() ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, null);
updateOutputRootsLayout(module);
}
else {
if (isMake()) {
if (myProjectDescriptor.fsState.markInitialScanPerformed(module.getName(), isCompilingTests())) {
initModuleFSState(module);
updateOutputRootsLayout(module);
}
}
else {
// forced compilation mode
if (getScope().isRecompilationForced(module.getName())) {
markDirtyFiles(module, myTimestamps, true, isCompilingTests() ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, null);
updateOutputRootsLayout(module);
}
}
}
@@ -368,9 +374,24 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler
}
private void initModuleFSState(Module module) throws IOException {
final HashSet<File> currentFiles = new HashSet<File>();
markDirtyFiles(module, myTimestamps, false, isCompilingTests() ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, currentFiles);
boolean forceMarkDirty = false;
final File currentOutput = getProjectPaths().getModuleOutputDir(module, isCompilingTests());
if (currentOutput != null) {
Pair<String, String> outputsPair = getDataManager().getOutputRootsLayout().getState(module.getName());
if (outputsPair != null) {
final String previousPath = isCompilingTests() ? outputsPair.second : outputsPair.first;
forceMarkDirty = StringUtil.isEmpty(previousPath) || !FileUtil.filesEqual(currentOutput, new File(previousPath));
}
else {
forceMarkDirty = true;
}
}
final HashSet<File> currentFiles = new HashSet<File>();
markDirtyFiles(module, myTimestamps, forceMarkDirty, isCompilingTests() ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, currentFiles);
// handle deleted paths
myProjectDescriptor.fsState.clearDeletedPaths(module.getName(), isCompilingTests());
final SourceToOutputMapping sourceToOutputMap = getDataManager().getSourceToOutputMap(module.getName(), isCompilingTests());
for (final Iterator<String> it = sourceToOutputMap.getKeysIterator(); it.hasNext();) {
final String path = it.next();
@@ -382,6 +403,27 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler
}
}
private void updateOutputRootsLayout(Module module) throws IOException {
final File currentOutput = getProjectPaths().getModuleOutputDir(module, isCompilingTests());
if (currentOutput == null) {
return;
}
final ModuleOutputRootsLayout outputRootsLayout = getDataManager().getOutputRootsLayout();
Pair<String, String> outputsPair = outputRootsLayout.getState(module.getName());
// update data
final String productionPath;
final String testPath;
if (isCompilingTests()) {
productionPath = outputsPair != null? outputsPair.first : "";
testPath = FileUtil.toSystemIndependentName(currentOutput.getPath());
}
else {
productionPath = FileUtil.toSystemIndependentName(currentOutput.getPath());
testPath = outputsPair != null? outputsPair.second : "";
}
outputRootsLayout.update(module.getName(), Pair.create(productionPath, testPath));
}
public boolean hasRemovedSources() {
final Map<String, Collection<String>> removed = Utils.REMOVED_SOURCES_KEY.get(this);
return removed != null && !removed.isEmpty();
@@ -447,7 +489,6 @@ public class CompileContext extends UserDataHolderBase implements MessageHandler
continue;
}
myProjectDescriptor.fsState.clearRecompile(rd);
myProjectDescriptor.fsState.clearDeletedPaths(module.getName(), isCompilingTests());
traverseRecursively(rd, rd.root, excludes, tsStorage, forceMarkDirty, currentFiles);
}
}
@@ -218,6 +218,22 @@ public class IncProjectBuilder {
context.processMessage(new ProgressMessage("Running 'after' tasks"));
runTasks(context, myBuilderRegistry.getAfterTasks());
// cleanup output roots layout, commented for efficiency
//final ModuleOutputRootsLayout outputRootsLayout = context.getDataManager().getOutputRootsLayout();
//try {
// final Iterator<String> keysIterator = outputRootsLayout.getKeysIterator();
// final Map<String, Module> modules = myProjectDescriptor.project.getModules();
// while (keysIterator.hasNext()) {
// final String moduleName = keysIterator.next();
// if (modules.containsKey(moduleName)) {
// outputRootsLayout.remove(moduleName);
// }
// }
//}
//catch (IOException e) {
// throw new ProjectBuildException(e);
//}
}
finally {
for (ProjectLevelBuilder builder : myBuilderRegistry.getProjectLevelBuilders()) {
@@ -18,7 +18,7 @@ import java.util.Map;
* Date: 10/7/11
*/
public class BuildDataManager implements StorageOwner {
private static final int VERSION = 4;
private static final int VERSION = 5;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.storage.BuildDataManager");
private static final String SRC_TO_OUTPUTS_STORAGE = "src-out";
private static final String SRC_TO_FORM_STORAGE = "src-form";
@@ -30,6 +30,7 @@ public class BuildDataManager implements StorageOwner {
private final SourceToFormMapping mySrcToFormMap;
private final ArtifactsBuildData myArtifactsBuildData;
private final ModuleOutputRootsLayout myOutputRootsLayout;
private final Mappings myMappings;
private final File myDataStorageRoot;
private final File myVersionFile;
@@ -37,11 +38,16 @@ public class BuildDataManager implements StorageOwner {
public BuildDataManager(final File dataStorageRoot, final boolean useMemoryTempCaches) throws IOException {
myDataStorageRoot = dataStorageRoot;
mySrcToFormMap = new SourceToFormMapping(new File(getSourceToFormsRoot(), "data"));
myOutputRootsLayout = new ModuleOutputRootsLayout(new File(getOutputsLayoutRoot(), "data"));
myMappings = new Mappings(getMappingsRoot(), useMemoryTempCaches);
myArtifactsBuildData = new ArtifactsBuildData(new File(dataStorageRoot, "artifacts"));
myVersionFile = new File(myDataStorageRoot, "version.dat");
}
private File getOutputsLayoutRoot() {
return new File(myDataStorageRoot, "output-roots");
}
public SourceToOutputMapping getSourceToOutputMap(final String moduleName, final boolean testSources) throws IOException {
String lowerCaseModuleName = moduleName.toLowerCase(Locale.US);
final Map<String, SourceToOutputMapping> storageMap = testSources ? myTestSourceToOutputs : myProductionSourceToOutputs;
@@ -64,6 +70,10 @@ public class BuildDataManager implements StorageOwner {
return mySrcToFormMap;
}
public ModuleOutputRootsLayout getOutputRootsLayout() {
return myOutputRootsLayout;
}
public Mappings getMappings() {
return myMappings;
}
@@ -88,14 +98,19 @@ public class BuildDataManager implements StorageOwner {
wipeStorage(getSourceToFormsRoot(), mySrcToFormMap);
}
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
mappings.clean();
}
try {
wipeStorage(getOutputsLayoutRoot(), myOutputRootsLayout);
}
else {
FileUtil.delete(getMappingsRoot());
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
mappings.clean();
}
}
else {
FileUtil.delete(getMappingsRoot());
}
}
}
}
@@ -115,6 +130,7 @@ public class BuildDataManager implements StorageOwner {
}
}
mySrcToFormMap.flush(memoryCachesOnly);
myOutputRootsLayout.flush(memoryCachesOnly);
final Mappings mappings = myMappings;
if (mappings != null) {
synchronized (mappings) {
@@ -138,17 +154,22 @@ public class BuildDataManager implements StorageOwner {
closeStorage(mySrcToFormMap);
}
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
try {
mappings.close();
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw ((IOException)cause);
try {
closeStorage(myOutputRootsLayout);
}
finally {
final Mappings mappings = myMappings;
if (mappings != null) {
try {
mappings.close();
}
catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw ((IOException)cause);
}
throw e;
}
throw e;
}
}
}
@@ -0,0 +1,39 @@
package org.jetbrains.jps.incremental.storage;
import com.intellij.openapi.util.Pair;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.IOUtil;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
/**
* @author Eugene Zhuravlev
* Date: 12/29/11
*/
public class ModuleOutputRootsLayout extends AbstractStateStorage<String, Pair<String, String>>{
public ModuleOutputRootsLayout(File storePath) throws IOException {
super(storePath, new EnumeratorStringDescriptor(), new PairDataExternalizer());
}
public void appendData(String s, Pair<String, String> data) throws IOException {
update(s, data);
}
private static class PairDataExternalizer implements DataExternalizer<Pair<String, String>> {
public void save(DataOutput out, Pair<String, String> value) throws IOException {
IOUtil.writeString(value.getFirst(), out);
IOUtil.writeString(value.getSecond(), out);
}
public Pair<String, String> read(DataInput in) throws IOException {
final String first = IOUtil.readString(in);
final String second = IOUtil.readString(in);
return new Pair<String, String>(first, second);
}
}
}