- refactor:handle cache corruptions outside IncProjectBuilder

- when forcing rebuild because of cache corruption, keep the original compilation scope
This commit is contained in:
Eugene Zhuravlev
2012-04-22 22:14:19 +02:00
parent f18ecfd87b
commit a3c603ebf5
6 changed files with 130 additions and 96 deletions
@@ -145,6 +145,7 @@ final class BuildSession implements Runnable, CanceledStatus {
}
private void runBuild(String projectPath, BuildType buildType, Set<String> modules, Collection<String> artifacts, Map<String, String> builderParams, Collection<String> paths, final MessageHandler msgHandler, CanceledStatus cs) throws Throwable{
boolean forceCleanCaches = false;
ProjectDescriptor pd;
final Project project = loadProject(projectPath);
final BuildFSState fsState = new BuildFSState(false);
@@ -154,49 +155,70 @@ final class BuildSession implements Runnable, CanceledStatus {
try {
timestamps = new ProjectTimestamps(dataStorageRoot);
dataManager = new BuildDataManager(dataStorageRoot, true);
if (dataManager.versionDiffers()) {
forceCleanCaches = true;
msgHandler.processMessage(new CompilerMessage("build", BuildMessage.Kind.INFO, "Dependency data format has changed, project rebuild required"));
}
}
catch (Exception e) {
// second try
e.printStackTrace(System.err);
LOG.info(e);
if (timestamps != null) {
timestamps.close();
}
if (dataManager != null) {
dataManager.close();
}
buildType = BuildType.PROJECT_REBUILD; // force project rebuild
forceCleanCaches = true;
FileUtil.delete(dataStorageRoot);
timestamps = new ProjectTimestamps(dataStorageRoot);
dataManager = new BuildDataManager(dataStorageRoot, true);
// second attempt succeded
msgHandler.processMessage(new CompilerMessage("compile-server", BuildMessage.Kind.INFO, "Project rebuild forced: " + e.getMessage()));
msgHandler.processMessage(new CompilerMessage("build", BuildMessage.Kind.INFO, "Project rebuild forced: " + e.getMessage()));
}
pd = new ProjectDescriptor(project, fsState, timestamps, dataManager, BuildLoggingManager.DEFAULT);
try {
final CompileScope compileScope = createCompilationScope(buildType, pd, modules, artifacts, paths);
final IncProjectBuilder builder = new IncProjectBuilder(pd, BuilderRegistry.getInstance(), builderParams, cs);
if (msgHandler != null) {
for (int attempt = 0; attempt < 2; attempt++) {
if (forceCleanCaches && modules.isEmpty() && paths.isEmpty()) {
// if compilation scope is the whole project and cache rebuild is forced, use PROJECT_REBUILD for faster compilation
buildType = BuildType.PROJECT_REBUILD;
}
final CompileScope compileScope = createCompilationScope(buildType, pd, modules, artifacts, paths);
final IncProjectBuilder builder = new IncProjectBuilder(pd, BuilderRegistry.getInstance(), builderParams, cs);
builder.addMessageHandler(msgHandler);
}
switch (buildType) {
case PROJECT_REBUILD:
builder.build(compileScope, false, true);
break;
try {
switch (buildType) {
case PROJECT_REBUILD:
builder.build(compileScope, false, true, forceCleanCaches);
break;
case FORCED_COMPILATION:
builder.build(compileScope, false, false);
break;
case FORCED_COMPILATION:
builder.build(compileScope, false, false, forceCleanCaches);
break;
case MAKE:
builder.build(compileScope, true, false);
break;
case MAKE:
builder.build(compileScope, true, false, forceCleanCaches);
break;
case CLEAN:
//todo[nik]
// new ProjectBuilder(new GantBinding(), project).clean();
break;
case CLEAN:
//todo[nik]
// new ProjectBuilder(new GantBinding(), project).clean();
break;
}
break; // break attempts loop
}
catch (RebuildRequestedException e) {
// todo: do not try second attempt here; just notify the calling process about "rebuild requested" and end the session
if (attempt == 0) {
LOG.info(e);
forceCleanCaches = true;
}
else {
throw e;
}
}
}
}
finally {
@@ -79,7 +79,7 @@ public class IncProjectBuilder {
myMessageHandlers.add(handler);
}
public void build(CompileScope scope, final boolean isMake, final boolean isProjectRebuild) {
public void build(CompileScope scope, final boolean isMake, final boolean isProjectRebuild, boolean forceCleanCaches) throws RebuildRequestedException{
final LowMemoryWatcher memWatcher = LowMemoryWatcher.register(new Forceable() {
@Override
public boolean isDirty() {
@@ -94,47 +94,30 @@ public class IncProjectBuilder {
});
CompileContext context = null;
try {
try {
if (myProjectDescriptor.dataManager.versionDiffers()) {
myMessageDispatcher.processMessage(new CompilerMessage(
COMPILE_SERVER_NAME, BuildMessage.Kind.INFO, "Dependency data format has changed, project rebuild required"
));
context = createContextForForcedRebuild(scope, isMake, isProjectRebuild);
}
else {
context = createContext(scope, isMake, isProjectRebuild);
}
runBuild(context);
}
catch (ProjectBuildException e) {
final Throwable cause = e.getCause();
if (cause instanceof PersistentEnumerator.CorruptedException || cause instanceof MappingFailedException || cause instanceof IOException) {
// force rebuild
myMessageDispatcher.processMessage(new CompilerMessage(
COMPILE_SERVER_NAME, BuildMessage.Kind.INFO,
"Internal caches are corrupted or have outdated format, forcing project rebuild: " +
e.getMessage())
);
flushContext(context);
context = createContextForForcedRebuild(scope, isMake, isProjectRebuild);
runBuild(context);
}
else {
throw e;
}
}
context = createContext(scope, isMake, isProjectRebuild);
runBuild(context, forceCleanCaches);
myProjectDescriptor.dataManager.saveVersion();
}
catch (ProjectBuildException e) {
final Throwable cause = e.getCause();
if (cause == null) {
final String msg = e.getMessage();
if (!StringUtil.isEmpty(msg)) {
myMessageDispatcher.processMessage(new ProgressMessage(msg));
}
if (cause instanceof PersistentEnumerator.CorruptedException || cause instanceof MappingFailedException || cause instanceof IOException) {
myMessageDispatcher.processMessage(new CompilerMessage(
COMPILE_SERVER_NAME, BuildMessage.Kind.INFO,
"Internal caches are corrupted or have outdated format, forcing project rebuild: " +
e.getMessage())
);
throw new RebuildRequestedException(cause);
}
else {
myMessageDispatcher.processMessage(new CompilerMessage(COMPILE_SERVER_NAME, cause));
if (cause == null) {
final String msg = e.getMessage();
if (!StringUtil.isEmpty(msg)) {
myMessageDispatcher.processMessage(new ProgressMessage(msg));
}
}
else {
myMessageDispatcher.processMessage(new CompilerMessage(COMPILE_SERVER_NAME, cause));
}
}
}
finally {
@@ -152,19 +135,6 @@ public class IncProjectBuilder {
}
}
private CompileContext createContextForForcedRebuild(CompileScope scope, boolean isMake, boolean isProjectRebuild) throws ProjectBuildException {
final CompileContext context;
if (isMake || isProjectRebuild) {
context = createContext(new AllProjectScope(scope.getProject(), scope.getArtifacts(), true), false, true);
}
else {
//in case of forced compilation keep the scope, but remove all caches
context = createContext(scope, false, false);
cleanOutputRoots(context);
}
return context;
}
private static void flushContext(CompileContext context) {
if (context != null) {
context.getTimestampStorage().force();
@@ -206,12 +176,12 @@ public class IncProjectBuilder {
return myModulesProcessed / myTotalModulesWork;
}
private void runBuild(CompileContext context) throws ProjectBuildException {
private void runBuild(CompileContext context, boolean forceCleanCaches) throws ProjectBuildException {
context.setDone(0.0f);
LOG.info("Building project '" + context.getProject().getProjectName() + "'; isRebuild:" +context.isProjectRebuild() + "; isMake:" + context.isMake());
if (context.isProjectRebuild()) {
if (context.isProjectRebuild() || forceCleanCaches) {
cleanOutputRoots(context);
}
@@ -0,0 +1,17 @@
package org.jetbrains.jps.incremental;
/**
* @author Eugene Zhuravlev
* Date: 9/20/11
*/
public class RebuildRequestedException extends ProjectBuildException{
public RebuildRequestedException(Throwable cause) {
super(cause);
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
@@ -130,7 +130,7 @@ class ServerState {
public void startBuild(String projectPath, BuildType buildType, Set<String> modules, Collection<String> artifacts,
Map<String, String> builderParams, Collection<String> paths, final MessageHandler msgHandler, CanceledStatus cs) throws Throwable{
boolean forceCleanCaches = false;
ProjectDescriptor pd;
synchronized (myConfigurationLock) {
pd = myProjects.get(projectPath);
@@ -143,17 +143,21 @@ class ServerState {
try {
timestamps = new ProjectTimestamps(dataStorageRoot);
dataManager = new BuildDataManager(dataStorageRoot, myKeepTempCachesInMemory);
if (dataManager.versionDiffers()) {
forceCleanCaches = true;
msgHandler.processMessage(new CompilerMessage("compile-server", BuildMessage.Kind.INFO, "Dependency data format has changed, project rebuild required"));
}
}
catch (Exception e) {
// second try
e.printStackTrace(System.err);
LOG.info(e);
if (timestamps != null) {
timestamps.close();
}
if (dataManager != null) {
dataManager.close();
}
buildType = BuildType.PROJECT_REBUILD; // force project rebuild
forceCleanCaches = true;
FileUtil.delete(dataStorageRoot);
timestamps = new ProjectTimestamps(dataStorageRoot);
dataManager = new BuildDataManager(dataStorageRoot, myKeepTempCachesInMemory);
@@ -168,28 +172,44 @@ class ServerState {
}
try {
final CompileScope compileScope = createCompilationScope(buildType, pd, modules, artifacts, paths);
final IncProjectBuilder builder = new IncProjectBuilder(pd, BuilderRegistry.getInstance(), builderParams, cs);
if (msgHandler != null) {
for (int attempt = 0; attempt < 2; attempt++) {
if (forceCleanCaches && modules.isEmpty() && paths.isEmpty()) {
// if compilation scope is the whole project and cache rebuild is forced, use PROJECT_REBUILD for faster compilation
buildType = BuildType.PROJECT_REBUILD;
}
final CompileScope compileScope = createCompilationScope(buildType, pd, modules, artifacts, paths);
final IncProjectBuilder builder = new IncProjectBuilder(pd, BuilderRegistry.getInstance(), builderParams, cs);
builder.addMessageHandler(msgHandler);
}
switch (buildType) {
case PROJECT_REBUILD:
builder.build(compileScope, false, true);
break;
try {
switch (buildType) {
case PROJECT_REBUILD:
builder.build(compileScope, false, true, forceCleanCaches);
break;
case FORCED_COMPILATION:
builder.build(compileScope, false, false);
break;
case FORCED_COMPILATION:
builder.build(compileScope, false, false, forceCleanCaches);
break;
case MAKE:
builder.build(compileScope, true, false);
break;
case MAKE:
builder.build(compileScope, true, false, forceCleanCaches);
break;
case CLEAN:
//todo[nik]
// new ProjectBuilder(new GantBinding(), project).clean();
break;
case CLEAN:
//todo[nik]
// new ProjectBuilder(new GantBinding(), project).clean();
break;
}
break; // break attempts loop
}
catch (RebuildRequestedException e) {
if (attempt == 0) {
LOG.info(e);
forceCleanCaches = true;
}
else {
throw e;
}
}
}
}
finally {
@@ -210,7 +210,7 @@ public abstract class IncrementalTestCase extends TestCase {
new IncProjectBuilder(
projectDescriptor, BuilderRegistry.getInstance(), Collections.<String, String>emptyMap(), CanceledStatus.NULL
).build(
new AllProjectScope(project, Collections.<Artifact>emptySet(), true), false, true
new AllProjectScope(project, Collections.<Artifact>emptySet(), true), false, true, false
);
modify();
@@ -222,7 +222,7 @@ public abstract class IncrementalTestCase extends TestCase {
new IncProjectBuilder(
projectDescriptor, BuilderRegistry.getInstance(), Collections.<String, String>emptyMap(), CanceledStatus.NULL
).build(
new AllProjectScope(project, Collections.<Artifact>emptySet(), false), true, false
new AllProjectScope(project, Collections.<Artifact>emptySet(), false), true, false, false
);
final String expected = StringUtil.convertLineSeparators(FileUtil.loadFile(new File(getBaseDir() + ".log")));
@@ -195,7 +195,12 @@ public abstract class ArtifactBuilderTestCase extends UsefulTestCase {
}
}
});
builder.build(new AllProjectScope(myDescriptor.project, new HashSet<Artifact>(Arrays.asList(artifacts)), force), !force, false);
try {
builder.build(new AllProjectScope(myDescriptor.project, new HashSet<Artifact>(Arrays.asList(artifacts)), force), !force, false, false);
}
catch (RebuildRequestedException e) {
fail(e.getMessage());
}
if (shouldFail) {
assertFalse("Build not failed as expected", errorMessages.isEmpty());
}