From 10d52604cce313ac0c50bda16a5f2e8cea10e77a Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 7 Feb 2012 21:26:57 +0100 Subject: [PATCH] TranslatingCompilerFilesMonitor: suspend activity for projects where "compile server" mode is enabled --- .../intellij/compiler/impl/CompileDriver.java | 9 +- .../impl/TranslatingCompilerFilesMonitor.java | 177 ++++++++++++++++-- .../options/CompilerUIConfigurable.java | 5 +- .../openapi/compiler/CompilerPaths.java | 4 + 4 files changed, 177 insertions(+), 18 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index 5751811c903b..857d42b0632b 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -144,8 +144,6 @@ public class CompileDriver { } }; - private OutputPathFinder myOutputFinder; // need this for updating zip archives (experimental feature) - private Set myAllOutputDirectories; private static final long ONE_MINUTE_MS = 60L /*sec*/ * 1000L /*millisec*/; @@ -665,6 +663,7 @@ public class CompileDriver { finish - start ); CompilerCacheManager.getInstance(myProject).flushCaches(); + FileUtil.delete(CompilerPaths.getRebuildMarkerFile(myProject)); } } }; @@ -755,8 +754,6 @@ public class CompileDriver { } myAllOutputDirectories = getAllOutputDirectories(compileContext); - // need this for updating zip archives experiment, uncomment if the feature is turned on - //myOutputFinder = new OutputPathFinder(myAllOutputDirectories); status = doCompile(compileContext, isRebuild, forceCompile, false); } catch (Throwable ex) { @@ -827,6 +824,10 @@ public class CompileDriver { } private void checkCachesVersion(final CompileContextImpl compileContext, final long currentVFSTimestamp) { + if (CompilerPaths.getRebuildMarkerFile(compileContext.getProject()).exists()) { + compileContext.requestRebuildNextTime("Compiler caches are out of date, project rebuild is required"); + return; + } final CompileStatus compileStatus = readStatus(); if (compileStatus == null) { compileContext.requestRebuildNextTime(CompilerBundle.message("error.compiler.caches.corrupted")); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java index 7f12839af0b4..f6c37ea4b2b9 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java @@ -19,6 +19,7 @@ import com.intellij.ProjectTopics; import com.intellij.compiler.CompileServerManager; import com.intellij.compiler.CompilerConfiguration; import com.intellij.compiler.CompilerIOUtil; +import com.intellij.compiler.CompilerWorkspaceConfiguration; import com.intellij.compiler.make.MakeUtil; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; @@ -82,12 +83,16 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { public static boolean ourDebugMode = false; @NonNls private static final String PATHS_TO_DELETE_FILENAME = "paths_to_delete.dat"; + private static final String SUSPENDED_PROJECTS_FILENAME = "suspended.dat"; private static final String OUTPUT_ROOTS_FILENAME = "output_roots.dat"; private static final FileAttribute ourSourceFileAttribute = new FileAttribute("_make_source_file_info_", 3); private static final FileAttribute ourOutputFileAttribute = new FileAttribute("_make_output_file_info_", 3); private static final Key> SOURCE_FILES_CACHE = Key.create("_source_url_to_vfile_cache_"); private final Object myDataLock = new Object(); + + private final TIntHashSet mySuspendedProjects = new TIntHashSet(); // projectId for allprojects that should not be monitored + private final TIntObjectHashMap mySourcesToRecompile = new TIntObjectHashMap(); // ProjectId->set of source file paths private PersistentHashMap>> myOutputRootsStorage; // ProjectId->map[moduleId->Pair(outputDirId, testOutputDirId)] private final TIntObjectHashMap> myOutputsToDelete = new TIntObjectHashMap>(); // Map: projectId -> Map{output path -> [sourceUrl; classname]} @@ -126,7 +131,7 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { } }; private final ProjectManager myProjectManager; - private final TIntIntHashMap myInitInProgress = new TIntIntHashMap(); // projectId fior successfully initialized projects + private final TIntIntHashMap myInitInProgress = new TIntIntHashMap(); // projectId for successfully initialized projects private final Object myAsyncScanLock = new Object(); public TranslatingCompilerFilesMonitor(VirtualFileManager vfsManager, ProjectManager projectManager, Application application) { @@ -140,6 +145,46 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { return ApplicationManager.getApplication().getComponent(TranslatingCompilerFilesMonitor.class); } + public void suspendProject(Project project) { + final int projectId = getProjectId(project); + + synchronized (myDataLock) { + if (!mySuspendedProjects.add(projectId)) { + return; + } + FileUtil.createIfDoesntExist(CompilerPaths.getRebuildMarkerFile(project)); + // cleanup internal structures to free memory + mySourcesToRecompile.remove(projectId); + myOutputsToDelete.remove(projectId); + myGeneratedDataPaths.remove(project); + myProjectOutputRoots.remove(projectId); + } + + try { + myOutputRootsStorage.remove(projectId); + } + catch (IOException e) { + LOG.info(e); + } + + } + + public void watchProject(Project project) { + synchronized (myDataLock) { + mySuspendedProjects.remove(getProjectId(project)); + } + } + + public boolean isSuspended(Project project) { + return isSuspended(getProjectId(project)); + } + + public boolean isSuspended(int projectId) { + synchronized (myDataLock) { + return mySuspendedProjects.contains(projectId); + } + } + @Nullable public static VirtualFile getSourceFileByOutput(VirtualFile outputFile) { final OutputFileInfo outputFileInfo = loadOutputInfo(outputFile); @@ -420,6 +465,36 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { } public void initComponent() { + loadSuspendedProjects(); + loadPathsToDelete(); + ensureOutputStorageInitialized(); + } + + private void loadSuspendedProjects() { + final File file = new File(CompilerPaths.getCompilerSystemDirectory(), SUSPENDED_PROJECTS_FILENAME); + try { + final DataInputStream is = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); + try { + final int projectsCount = is.readInt(); + synchronized (myDataLock) { + for (int idx = 0; idx < projectsCount; idx++) { + mySuspendedProjects.add(is.readInt()); + } + } + } + finally { + is.close(); + } + } + catch (FileNotFoundException ignored) { + } + catch (IOException e) { + LOG.info(e); + FileUtil.delete(file); + } + } + + private void loadPathsToDelete() { final File file = new File(CompilerPaths.getCompilerSystemDirectory(), PATHS_TO_DELETE_FILENAME); try { final DataInputStream is = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); @@ -430,20 +505,29 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { final int projectId = is.readInt(); final int size = is.readInt(); if (size > 0) { - final Map map = new HashMap(); - myOutputsToDelete.put(projectId, map); + final Map map; + if (mySuspendedProjects.contains(projectId)) { + map = null; + } + else { + map = new HashMap(); + myOutputsToDelete.put(projectId, map); + } for (int i = 0; i < size; i++) { - final String outputPath = FileUtil.toSystemIndependentName(CompilerIOUtil.readString(is)); + final String _outputPath = CompilerIOUtil.readString(is); final String srcUrl = CompilerIOUtil.readString(is); final String className = CompilerIOUtil.readString(is); + if (map == null) { + continue; + } if (LOG.isDebugEnabled() || ourDebugMode) { - final String message = "INIT path to delete: " + outputPath; + final String message = "INIT path to delete: " + FileUtil.toSystemIndependentName(_outputPath); LOG.debug(message); if (ourDebugMode) { System.out.println(message); } } - map.put(outputPath, new SourceUrlClassNamePair(srcUrl, className)); + map.put(FileUtil.toSystemIndependentName(_outputPath), new SourceUrlClassNamePair(srcUrl, className)); } } } @@ -462,8 +546,6 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { } FileUtil.delete(file); } - - ensureOutputStorageInitialized(); } private void ensureOutputStorageInitialized() { @@ -528,12 +610,46 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { } public void disposeComponent() { + saveSuspendedProjects(); + savePathsToDelete(); + } + + private void saveSuspendedProjects() { + final File file = new File(CompilerPaths.getCompilerSystemDirectory(), SUSPENDED_PROJECTS_FILENAME); + try { + FileUtil.createParentDirs(file); + final DataOutputStream os = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); + try { + synchronized (myDataLock) { + os.writeInt(mySuspendedProjects.size()); + for (int projectId : mySuspendedProjects.toArray()) { + os.writeInt(projectId); + } + } + } + finally { + os.close(); + } + } + catch (IOException e) { + LOG.error(e); + } + } + + private void savePathsToDelete() { final File file = new File(CompilerPaths.getCompilerSystemDirectory(), PATHS_TO_DELETE_FILENAME); try { FileUtil.createParentDirs(file); final DataOutputStream os = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); try { synchronized (myDataLock) { + mySuspendedProjects.forEach(new TIntProcedure() { + @Override + public boolean execute(int projectId) { + myOutputsToDelete.remove(projectId); + return true; + } + }); final int[] keys = myOutputsToDelete.keys(); os.writeInt(keys.length); for (int projectId : keys) { @@ -839,10 +955,12 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { } } - public void clearPaths(final int projectId){ + public boolean clearPaths(final int projectId){ if (myProjectToOutputPathMap != null) { - myProjectToOutputPathMap.remove(projectId); + final Serializable removed = myProjectToOutputPathMap.remove(projectId); + return removed != null; } + return false; } long getTimestamp(final int projectId) { @@ -899,7 +1017,7 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { } public static List getCompiledClassNames(VirtualFile srcFile, Project project) { - SourceFileInfo info = loadSourceInfo(srcFile); + final SourceFileInfo info = loadSourceInfo(srcFile); if (info == null) { return Collections.emptyList(); } @@ -1083,6 +1201,9 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { public void scanSourcesForCompilableFiles(final Project project) { final int projectId = getProjectId(project); + if (isSuspended(projectId)) { + return; + } startAsyncScan(projectId); StartupManager.getInstance(project).runWhenProjectIsInitialized(new Runnable() { public void run() { @@ -1186,11 +1307,21 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { final ProjectRef projRef = new ProjectRef(project); final int projectId = getProjectId(project); + if (CompilerWorkspaceConfiguration.getInstance(project).useCompileServer()) { + suspendProject(project); + } + else { + watchProject(project); + } + conn.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() { private VirtualFile[] myRootsBefore; private Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project); public void beforeRootsChange(final ModuleRootEvent event) { + if (isSuspended(projectId)) { + return; + } try { myRootsBefore = ProjectRootManager.getInstance(projRef.get()).getContentSourceRoots(); } @@ -1200,6 +1331,9 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { } public void rootsChanged(final ModuleRootEvent event) { + if (isSuspended(projectId)) { + return; + } try { final VirtualFile[] rootsBefore = myRootsBefore; myRootsBefore = null; @@ -1323,6 +1457,9 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { if (srcInfo != null) { final boolean srcWillBeDeleted = VfsUtil.isAncestor(eventFile, srcFile, false); for (int projectId : srcInfo.getProjectIds().toArray()) { + if (isSuspended(projectId)) { + continue; + } if (srcInfo.isAssociated(projectId, filePath)) { if (srcWillBeDeleted) { if (LOG.isDebugEnabled() || ourDebugMode) { @@ -1351,6 +1488,9 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { deletionProc.setRootBeingDeleted(eventFile); final int sourceFileId = Math.abs(getFileId(file)); for (int projectId : projects.toArray()) { + if (isSuspended(projectId)) { + continue; + } // mark associated outputs for deletion srcInfo.processOutputPaths(projectId, deletionProc); if (LOG.isDebugEnabled() || ourDebugMode) { @@ -1390,7 +1530,15 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { final SourceFileInfo srcInfo = file.isValid()? loadSourceInfo(file) : null; if (srcInfo != null) { for (int projectId : srcInfo.getProjectIds().toArray()) { - addSourceForRecompilation(projectId, file, srcInfo); + if (isSuspended(projectId)) { + if (srcInfo.clearPaths(projectId)) { + srcInfo.updateTimestamp(projectId, -1L); + saveSourceInfo(file, srcInfo); + } + } + else { + addSourceForRecompilation(projectId, file, srcInfo); + } } } else { @@ -1415,8 +1563,11 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { if (!project.isInitialized()) { continue; // the content of this project will be scanned during its post-startup activities } - final ProjectRootManager rootManager = ProjectRootManager.getInstance(project); final int projectId = getProjectId(project); + if (isSuspended(projectId)) { + continue; + } + final ProjectRootManager rootManager = ProjectRootManager.getInstance(project); if (rootManager.getFileIndex().isInSourceContent(file)) { final TranslatingCompiler[] translators = CompilerManager.getInstance(project).getCompilers(TranslatingCompiler.class); processRecursively(file, false, new FileProcessor() { diff --git a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java index 783925920601..11a36baa4e36 100644 --- a/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java +++ b/java/compiler/impl/src/com/intellij/compiler/options/CompilerUIConfigurable.java @@ -110,11 +110,14 @@ public class CompilerUIConfigurable implements SearchableConfigurable, Configura applyResourcePatterns(extensionString, (CompilerConfigurationImpl)CompilerConfiguration.getInstance(myProject)); // this will schedule for compilation all files that might become compilable after resource patterns' changing - TranslatingCompilerFilesMonitor.getInstance().scanSourcesForCompilableFiles(myProject); + final TranslatingCompilerFilesMonitor monitor = TranslatingCompilerFilesMonitor.getInstance(); if (!workspaceConfiguration.USE_COMPILE_SERVER) { CompileServerManager.getInstance().shutdownServer(); + monitor.watchProject(myProject); + monitor.scanSourcesForCompilableFiles(myProject); } else { + monitor.suspendProject(myProject); SwingUtilities.invokeLater(new Runnable() { public void run() { CompileServerManager.getInstance().sendReloadRequest(myProject); diff --git a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java index 798e2b1d9fa2..7f21c224225f 100644 --- a/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java +++ b/java/compiler/openapi/src/com/intellij/openapi/compiler/CompilerPaths.java @@ -82,6 +82,10 @@ public class CompilerPaths { return new File(getCompilerSystemDirectory(project), ".caches"); } + public static File getRebuildMarkerFile(Project project) { + return new File(getCompilerSystemDirectory(project), "rebuild_required"); + } + /** * @param project * @return a directory under IDEA "system" directory where all files related to compiler subsystem are stored (such as compiler caches or generated files)