updating class dependencies in a centralized way at the end of every compilation round

This commit is contained in:
Eugene Zhuravlev
2014-12-07 22:14:29 +01:00
parent ca48dd6b3e
commit 1b2f6f3240
5 changed files with 126 additions and 88 deletions
@@ -52,9 +52,60 @@ import java.util.*;
public class JavaBuilderUtil {
private static final Key<Set<File>> ALL_AFFECTED_FILES_KEY = Key.create("_all_affected_files_");
private static final Key<Set<File>> ALL_COMPILED_FILES_KEY = Key.create("_all_compiled_files_");
private static final Key<Set<File>> FILES_TO_COMPILE_KEY = Key.create("_files_to_compile_");
private static final Key<Set<File>> SUCCESSFULLY_COMPILED_FILES_KEY = Key.create("_successfully_compiled_files_");
private static final Key<Pair<Mappings, Callbacks.Backend>> MAPPINGS_DELTA_KEY = Key.create("_mappings_delta_");
public static final Key<Callbacks.ConstantAffectionResolver> CONSTANT_SEARCH_SERVICE = Key.create("_constant_search_service_");
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.Builder");
public static void registerFileToCompile(CompileContext context, File file) {
registerFilesToCompile(context, Collections.singleton(file));
}
public static void registerFilesToCompile(CompileContext context, Collection<File> files) {
getFilesContainer(context, FILES_TO_COMPILE_KEY).addAll(files);
}
public static void registerSuccessfullyCompiled(CompileContext context, File file) {
registerSuccessfullyCompiled(context, Collections.singleton(file));
}
public static void registerSuccessfullyCompiled(CompileContext context, Collection<File> files) {
getFilesContainer(context, SUCCESSFULLY_COMPILED_FILES_KEY).addAll(files);
}
@NotNull
public static Callbacks.Backend getDependenciesRegistrar(CompileContext context) {
Pair<Mappings, Callbacks.Backend> pair = MAPPINGS_DELTA_KEY.get(context);
if (pair == null) {
final Mappings delta = context.getProjectDescriptor().dataManager.getMappings().createDelta();
pair = Pair.create(delta, delta.getCallback());
MAPPINGS_DELTA_KEY.set(context, pair);
}
return pair.second;
}
public static boolean updateMappingsOnRoundCompletion(
CompileContext context, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, ModuleChunk chunk) throws IOException {
Mappings delta = null;
final Pair<Mappings, Callbacks.Backend> pair = MAPPINGS_DELTA_KEY.get(context);
if (pair != null) {
MAPPINGS_DELTA_KEY.set(context, null);
delta = pair.getFirst();
}
if (delta == null) {
return false;
}
final Set<File> compiledFiles = getFilesContainer(context, FILES_TO_COMPILE_KEY);
FILES_TO_COMPILE_KEY.set(context, null);
final Set<File> successfullyCompiled = getFilesContainer(context, SUCCESSFULLY_COMPILED_FILES_KEY);
SUCCESSFULLY_COMPILED_FILES_KEY.set(context, null);
return updateMappings(context, delta, dirtyFilesHolder, chunk, compiledFiles, successfullyCompiled);
}
/**
*
* @param context
@@ -83,8 +134,8 @@ public class JavaBuilderUtil {
if (!isForcedRecompilationAllJavaModules(context)) {
if (context.shouldDifferentiate(chunk)) {
context.processMessage(new ProgressMessage("Checking dependencies... [" + chunk.getPresentableShortName() + "]"));
final Set<File> allCompiledFiles = getAllCompiledFilesContainer(context);
final Set<File> allAffectedFiles = getAllAffectedFilesContainer(context);
final Set<File> allCompiledFiles = getFilesContainer(context, ALL_COMPILED_FILES_KEY);
final Set<File> allAffectedFiles = getFilesContainer(context, ALL_AFFECTED_FILES_KEY);
// mark as affected all files that were dirty before compilation
allAffectedFiles.addAll(filesToCompile);
@@ -232,22 +283,14 @@ public class JavaBuilderUtil {
return false;
}
private static Set<File> getAllAffectedFilesContainer(CompileContext context) {
Set<File> allAffectedFiles = ALL_AFFECTED_FILES_KEY.get(context);
if (allAffectedFiles == null) {
allAffectedFiles = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
ALL_AFFECTED_FILES_KEY.set(context, allAffectedFiles);
@NotNull
private static Set<File> getFilesContainer(CompileContext context, final Key<Set<File>> dataKey) {
Set<File> files = dataKey.get(context);
if (files == null) {
files = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
dataKey.set(context, files);
}
return allAffectedFiles;
}
private static Set<File> getAllCompiledFilesContainer(CompileContext context) {
Set<File> allCompiledFiles = ALL_COMPILED_FILES_KEY.get(context);
if (allCompiledFiles == null) {
allCompiledFiles = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
ALL_COMPILED_FILES_KEY.set(context, allCompiledFiles);
}
return allCompiledFiles;
return files;
}
private static Set<String> getRemovedPaths(ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder) {
@@ -1135,55 +1135,63 @@ public class IncProjectBuilder {
continue;
}
for (ModuleLevelBuilder builder : builders) {
processDeletedPaths(context, chunk.getTargets());
long start = System.nanoTime();
final ModuleLevelBuilder.ExitCode buildResult = builder.build(context, chunk, dirtyFilesHolder, outputConsumer);
incBuilderElapsedTime(builder, System.nanoTime() - start);
doneSomething |= (buildResult != ModuleLevelBuilder.ExitCode.NOTHING_DONE);
if (buildResult == ModuleLevelBuilder.ExitCode.ABORT) {
throw new StopBuildException("Builder " + builder.getPresentableName() + " requested build stop");
}
context.checkCanceled();
if (buildResult == ModuleLevelBuilder.ExitCode.ADDITIONAL_PASS_REQUIRED) {
if (!nextPassRequired) {
// recalculate basis
myTargetsProcessed -= (buildersPassed * modulesInChunk) / stageCount;
stageCount += myTotalModuleLevelBuilderCount;
myTargetsProcessed += (buildersPassed * modulesInChunk) / stageCount;
try {
for (ModuleLevelBuilder builder : builders) {
processDeletedPaths(context, chunk.getTargets());
long start = System.nanoTime();
final ModuleLevelBuilder.ExitCode buildResult = builder.build(context, chunk, dirtyFilesHolder, outputConsumer);
incBuilderElapsedTime(builder, System.nanoTime() - start);
doneSomething |= (buildResult != ModuleLevelBuilder.ExitCode.NOTHING_DONE);
if (buildResult == ModuleLevelBuilder.ExitCode.ABORT) {
throw new StopBuildException("Builder " + builder.getPresentableName() + " requested build stop");
}
context.checkCanceled();
if (buildResult == ModuleLevelBuilder.ExitCode.ADDITIONAL_PASS_REQUIRED) {
nextPassRequired = true;
}
else if (buildResult == ModuleLevelBuilder.ExitCode.CHUNK_REBUILD_REQUIRED) {
if (!rebuildFromScratchRequested && !JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) {
LOG.info("Builder " + builder.getPresentableName() + " requested rebuild of module chunk " + chunk.getName());
// allow rebuild from scratch only once per chunk
rebuildFromScratchRequested = true;
try {
// forcibly mark all files in the chunk dirty
context.getProjectDescriptor().fsState.clearContextRoundData(context);
FSOperations.markDirty(context, CompilationRound.NEXT, chunk, null);
// reverting to the beginning
myTargetsProcessed -= (buildersPassed * modulesInChunk) / stageCount;
stageCount = myTotalModuleLevelBuilderCount;
buildersPassed = 0;
nextPassRequired = true;
outputConsumer.clear();
break BUILDER_CATEGORY_LOOP;
}
catch (Exception e) {
throw new ProjectBuildException(e);
}
}
else {
LOG.debug("Builder " + builder.getPresentableName() + " requested second chunk rebuild");
}
}
buildersPassed++;
updateDoneFraction(context, modulesInChunk / (stageCount));
}
}
finally {
final boolean moreToCompile = JavaBuilderUtil.updateMappingsOnRoundCompletion(context, dirtyFilesHolder, chunk);
if (moreToCompile) {
nextPassRequired = true;
}
else if (buildResult == ModuleLevelBuilder.ExitCode.CHUNK_REBUILD_REQUIRED) {
if (!rebuildFromScratchRequested && !JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) {
LOG.info("Builder " + builder.getPresentableName() + " requested rebuild of module chunk " + chunk.getName());
// allow rebuild from scratch only once per chunk
rebuildFromScratchRequested = true;
try {
// forcibly mark all files in the chunk dirty
context.getProjectDescriptor().fsState.clearContextRoundData(context);
FSOperations.markDirty(context, CompilationRound.NEXT, chunk, null);
// reverting to the beginning
myTargetsProcessed -= (buildersPassed * modulesInChunk) / stageCount;
stageCount = myTotalModuleLevelBuilderCount;
buildersPassed = 0;
nextPassRequired = true;
outputConsumer.clear();
break BUILDER_CATEGORY_LOOP;
}
catch (Exception e) {
throw new ProjectBuildException(e);
}
}
else {
LOG.debug("Builder " + builder.getPresentableName() + " requested second chunk rebuild");
}
if (nextPassRequired && !rebuildFromScratchRequested) {
// recalculate basis
myTargetsProcessed -= (buildersPassed * modulesInChunk) / stageCount;
stageCount += myTotalModuleLevelBuilderCount;
myTargetsProcessed += (buildersPassed * modulesInChunk) / stageCount;
}
buildersPassed++;
updateDoneFraction(context, modulesInChunk / (stageCount));
}
}
}
@@ -39,8 +39,6 @@ import org.jetbrains.jps.builders.java.JavaBuilderExtension;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.builders.java.JavaCompilingTool;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.builders.java.dependencyView.Callbacks;
import org.jetbrains.jps.builders.java.dependencyView.Mappings;
import org.jetbrains.jps.builders.logging.ProjectBuilderLogger;
import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
import org.jetbrains.jps.cmdline.ProjectDescriptor;
@@ -252,9 +250,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
final Collection<File> platformCp = ProjectPaths.getPlatformCompilationClasspath(chunk, false/*context.isProjectRebuild()*/);
// begin compilation round
final Mappings delta = pd.dataManager.getMappings().createDelta();
final Callbacks.Backend mappingsCallback = delta.getCallback();
final OutputFilesSink outputSink = new OutputFilesSink(context, outputConsumer, mappingsCallback, chunk.getPresentableShortName());
final OutputFilesSink outputSink = new OutputFilesSink(context, outputConsumer, JavaBuilderUtil.getDependenciesRegistrar(context), chunk.getPresentableShortName());
try {
if (hasSourcesToCompile) {
final AtomicReference<String> ref = COMPILER_VERSION_INFO.get(context);
@@ -323,9 +319,8 @@ public class JavaBuilder extends ModuleLevelBuilder {
}
}
finally {
if (JavaBuilderUtil.updateMappings(context, delta, dirtyFilesHolder, chunk, files, outputSink.getSuccessfullyCompiled())) {
exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED;
}
JavaBuilderUtil.registerFilesToCompile(context, files);
JavaBuilderUtil.registerSuccessfullyCompiled(context, outputSink.getSuccessfullyCompiled());
}
return exitCode;
@@ -147,9 +147,7 @@ public class GreclipseBuilder extends ModuleLevelBuilder {
context.processMessage(new CompilerMessage(getPresentableName(), BuildMessage.Kind.ERROR, "Compilation failed"));
}
if (GroovyBuilder.updateDependencies(context, chunk, dirtyFilesHolder, toCompile, successfullyCompiled, outputConsumer, this)) {
return ExitCode.ADDITIONAL_PASS_REQUIRED;
}
GroovyBuilder.updateDependencies(context, toCompile, successfullyCompiled, outputConsumer, this);
return ExitCode.OK;
}
@@ -40,7 +40,6 @@ import org.jetbrains.jps.builders.FileProcessor;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.builders.java.dependencyView.Callbacks;
import org.jetbrains.jps.builders.java.dependencyView.Mappings;
import org.jetbrains.jps.builders.storage.SourceToOutputMapping;
import org.jetbrains.jps.cmdline.ClasspathBootstrap;
import org.jetbrains.jps.cmdline.ProjectDescriptor;
@@ -162,8 +161,8 @@ public class GroovyBuilder extends ModuleLevelBuilder {
context.processMessage(message);
}
if (!myForStubs && updateDependencies(context, chunk, dirtyFilesHolder, toCompile, compiled, outputConsumer, this)) {
return ExitCode.ADDITIONAL_PASS_REQUIRED;
if (!myForStubs) {
updateDependencies(context, toCompile, compiled, outputConsumer, this);
}
return hasFilesToCompileForNextRound(context) ? ExitCode.ADDITIONAL_PASS_REQUIRED : ExitCode.OK;
}
@@ -443,17 +442,14 @@ public class GroovyBuilder extends ModuleLevelBuilder {
return toCompile;
}
public static boolean updateDependencies(CompileContext context,
ModuleChunk chunk,
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
List<File> toCompile,
Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> successfullyCompiled,
OutputConsumer outputConsumer, Builder builder) throws IOException {
final Mappings delta = context.getProjectDescriptor().dataManager.getMappings().createDelta();
final List<File> successfullyCompiledFiles = new ArrayList<File>();
public static void updateDependencies(CompileContext context,
List<File> toCompile,
Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> successfullyCompiled,
OutputConsumer outputConsumer, Builder builder) throws IOException {
JavaBuilderUtil.registerFilesToCompile(context, toCompile);
if (!successfullyCompiled.isEmpty()) {
final Callbacks.Backend callback = delta.getCallback();
final Callbacks.Backend callback = JavaBuilderUtil.getDependenciesRegistrar(context);
for (Map.Entry<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> entry : successfullyCompiled.entrySet()) {
final ModuleBuildTarget target = entry.getKey();
@@ -479,12 +475,10 @@ public class GroovyBuilder extends ModuleLevelBuilder {
builder.getPresentableName(), BuildMessage.Kind.WARNING, message + "\n" + CompilerMessage.getTextFromThrowable(e), sourcePath)
);
}
successfullyCompiledFiles.add(srcFile);
JavaBuilderUtil.registerSuccessfullyCompiled(context, srcFile);
}
}
}
return JavaBuilderUtil.updateMappings(context, delta, dirtyFilesHolder, chunk, toCompile, successfullyCompiledFiles);
}
private static String readClassName(byte[] classBytes) throws IOException{