diff --git a/compiler/compiler-impl.iml b/compiler/compiler-impl.iml index 4223c594a53d..72515d9328d5 100644 --- a/compiler/compiler-impl.iml +++ b/compiler/compiler-impl.iml @@ -24,6 +24,15 @@ + + + + + + + + + diff --git a/compiler/impl/com/intellij/compiler/CompilerConfigurationImpl.java b/compiler/impl/com/intellij/compiler/CompilerConfigurationImpl.java index 17fa63f63de5..ec871d96948e 100644 --- a/compiler/impl/com/intellij/compiler/CompilerConfigurationImpl.java +++ b/compiler/impl/com/intellij/compiler/CompilerConfigurationImpl.java @@ -10,13 +10,15 @@ package com.intellij.compiler; import com.intellij.CommonBundle; import com.intellij.compiler.impl.javaCompiler.BackendCompiler; +import com.intellij.compiler.impl.javaCompiler.api.CompilerAPICompiler; import com.intellij.compiler.impl.javaCompiler.eclipse.EclipseCompiler; +import com.intellij.compiler.impl.javaCompiler.eclipse.EclipseEmbeddedCompiler; import com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler; -import com.intellij.compiler.impl.javaCompiler.javac.JavacEmbeddedCompiler; import com.intellij.compiler.impl.javaCompiler.javac.JavacSettings; import com.intellij.compiler.impl.javaCompiler.jikes.JikesCompiler; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.compiler.CompilerBundle; import com.intellij.openapi.compiler.options.ExcludedEntriesConfiguration; import com.intellij.openapi.components.*; @@ -65,7 +67,6 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements private final Collection myRegisteredCompilers = new ArrayList(); private BackendCompiler JAVAC_EXTERNAL_BACKEND; - private BackendCompiler JAVAC_EMBEDDED_BACKEND; private final Perl5Matcher myPatternMatcher = new Perl5Matcher(); { @@ -178,8 +179,6 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements private void createCompilers() { JAVAC_EXTERNAL_BACKEND = new JavacCompiler(myProject); myRegisteredCompilers.add(JAVAC_EXTERNAL_BACKEND); - JAVAC_EMBEDDED_BACKEND = new JavacEmbeddedCompiler(myProject); - //myRegisteredCompilers.add(JAVAC_EMBEDDED_BACKEND); if (!ApplicationManager.getApplication().isUnitTestMode()) { final BackendCompiler JIKES_BACKEND = new JikesCompiler(myProject); @@ -189,13 +188,26 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements final EclipseCompiler eclipse = new EclipseCompiler(myProject); myRegisteredCompilers.add(eclipse); } - //try { - // final EclipseEmbeddedCompiler eclipseEmbedded = new EclipseEmbeddedCompiler(myProject); - // myRegisteredCompilers.add(eclipseEmbedded); - //} - //catch (NoClassDefFoundError e) { - // // eclipse jar must be not in the classpath - //} + + if (ApplicationManagerEx.getApplicationEx().isInternal()) { + try { + final EclipseEmbeddedCompiler eclipseEmbedded = new EclipseEmbeddedCompiler(myProject); + myRegisteredCompilers.add(eclipseEmbedded); + } + catch (NoClassDefFoundError e) { + // eclipse jar must be not in the classpath + } + } + + if (ApplicationManagerEx.getApplicationEx().isInternal()) { + try { + CompilerAPICompiler compiler = new CompilerAPICompiler(myProject); + myRegisteredCompilers.add(compiler); + } + catch (NoClassDefFoundError e) { + // wrong JDK + } + } } myDefaultJavaCompiler = JAVAC_EXTERNAL_BACKEND; @@ -394,11 +406,7 @@ public class CompilerConfigurationImpl extends CompilerConfiguration implements if (JAVAC_EXTERNAL_BACKEND == null) { createCompilers(); } - if (myDefaultJavaCompiler != JAVAC_EXTERNAL_BACKEND) return myDefaultJavaCompiler; - boolean runEmbedded = ApplicationManager.getApplication().isUnitTestMode() - ? !JavacSettings.getInstance(myProject).isTestsUseExternalCompiler() - : Boolean.parseBoolean(System.getProperty(PROPERTY_IDEA_USE_EMBEDDED_JAVAC)); - return runEmbedded ? JAVAC_EMBEDDED_BACKEND : JAVAC_EXTERNAL_BACKEND; + return myDefaultJavaCompiler; } public void setDefaultCompiler(BackendCompiler defaultCompiler) { diff --git a/compiler/impl/com/intellij/compiler/OutputParser.java b/compiler/impl/com/intellij/compiler/OutputParser.java index 4608e90fecaf..6977a5f4f2bc 100644 --- a/compiler/impl/com/intellij/compiler/OutputParser.java +++ b/compiler/impl/com/intellij/compiler/OutputParser.java @@ -2,6 +2,7 @@ package com.intellij.compiler; import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.compiler.impl.javaCompiler.FileObject; import org.jetbrains.annotations.NonNls; import java.util.ArrayList; @@ -15,7 +16,7 @@ public abstract class OutputParser { @NonNls String getCurrentLine(); void setProgressText(String text); void fileProcessed(@NonNls String path); - void fileGenerated(@NonNls String path); + void fileGenerated(@NonNls FileObject path); void message(CompilerMessageCategory category, String message, @NonNls String url, int lineNum, int columnNum); } diff --git a/compiler/impl/com/intellij/compiler/impl/CompileDriver.java b/compiler/impl/com/intellij/compiler/impl/CompileDriver.java index 50e54d8c1045..62b80a410061 100644 --- a/compiler/impl/com/intellij/compiler/impl/CompileDriver.java +++ b/compiler/impl/com/intellij/compiler/impl/CompileDriver.java @@ -58,10 +58,7 @@ import com.intellij.psi.PsiCompiledElement; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; -import com.intellij.util.Chunk; -import com.intellij.util.LocalTimeCounter; -import com.intellij.util.ProfilingUtil; -import com.intellij.util.StringBuilderSpinAllocator; +import com.intellij.util.*; import com.intellij.util.containers.HashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; @@ -85,8 +82,16 @@ public class CompileDriver { @NonNls private static final boolean GENERATE_CLASSPATH_INDEX = "true".equals(System.getProperty("generate.classpath.index")); - private final FileProcessingCompilerAdapterFactory myProcessingCompilerAdapterFactory; - private final FileProcessingCompilerAdapterFactory myPackagingCompilerAdapterFactory; + private static final FileProcessingCompilerAdapterFactory FILE_PROCESSING_COMPILER_ADAPTER_FACTORY = new FileProcessingCompilerAdapterFactory() { + public FileProcessingCompilerAdapter create(CompileContext context, FileProcessingCompiler compiler) { + return new FileProcessingCompilerAdapter(context, compiler); + } + }; + private static final FileProcessingCompilerAdapterFactory FILE_PACKAGING_COMPILER_ADAPTER_FACTORY = new FileProcessingCompilerAdapterFactory() { + public FileProcessingCompilerAdapter create(CompileContext context, FileProcessingCompiler compiler) { + return new PackagingCompilerAdapter(context, (PackagingCompiler)compiler); + } + }; public CompileDriver(Project project) { myProject = project; @@ -109,16 +114,6 @@ public class CompileDriver { } } - myProcessingCompilerAdapterFactory = new FileProcessingCompilerAdapterFactory() { - public FileProcessingCompilerAdapter create(CompileContext context, FileProcessingCompiler compiler) { - return new FileProcessingCompilerAdapter(context, compiler); - } - }; - myPackagingCompilerAdapterFactory = new FileProcessingCompilerAdapterFactory() { - public FileProcessingCompilerAdapter create(CompileContext context, FileProcessingCompiler compiler) { - return new PackagingCompilerAdapter(context, (PackagingCompiler)compiler); - } - }; } public void rebuild(CompileStatusNotification callback) { @@ -150,10 +145,11 @@ public class CompileDriver { return false; } - for (Pair pair : myGenerationCompilerModuleToOutputDirMap.keySet()) { - final Pair outputs = myGenerationCompilerModuleToOutputDirMap.get(pair); - compileContext.assignModule(outputs.getFirst(), pair.getSecond(), false); - compileContext.assignModule(outputs.getSecond(), pair.getSecond(), true); + for (Map.Entry, Pair> entry : myGenerationCompilerModuleToOutputDirMap.entrySet()) { + final Pair outputs = entry.getValue(); + Module module = entry.getKey().getSecond(); + compileContext.assignModule(outputs.getFirst(), module, false); + compileContext.assignModule(outputs.getSecond(), module, true); } final Ref status = new Ref(); @@ -188,7 +184,7 @@ public class CompileDriver { final int CACHE_FORMAT_VERSION; final boolean COMPILATION_IN_PROGRESS; - public CompileStatus(int cacheVersion, boolean isCompilationInProgress) { + private CompileStatus(int cacheVersion, boolean isCompilationInProgress) { CACHE_FORMAT_VERSION = cacheVersion; COMPILATION_IN_PROGRESS = isCompilationInProgress; } @@ -260,10 +256,11 @@ public class CompileDriver { private CompileScope addAdditionalRoots(CompileScope originalScope) { CompileScope scope = originalScope; final Set affected = new HashSet(Arrays.asList(originalScope.getAffectedModules())); - for (final Pair pair : myGenerationCompilerModuleToOutputDirMap.keySet()) { - if (affected.contains(pair.getSecond())) { - final Pair outputs = myGenerationCompilerModuleToOutputDirMap.get(pair); - scope = new CompositeScope(scope, new FileSetCompileScope(new VirtualFile[]{outputs.getFirst(), outputs.getSecond()}, new Module[]{pair.getSecond()})); + for (Map.Entry, Pair> entry : myGenerationCompilerModuleToOutputDirMap.entrySet()) { + Module module = entry.getKey().getSecond(); + if (affected.contains(module)) { + final Pair outputs = entry.getValue(); + scope = new CompositeScope(scope, new FileSetCompileScope(new VirtualFile[]{outputs.getFirst(), outputs.getSecond()}, new Module[]{module})); } } @@ -303,10 +300,11 @@ public class CompileDriver { final CompileContextImpl compileContext = new CompileContextImpl(myProject, compileTask, scope, dependencyCache, !isRebuild && !forceCompile, isRebuild); compileContext.putUserData(COMPILATION_START_TIMESTAMP, LocalTimeCounter.currentTime()); - for (Pair pair : myGenerationCompilerModuleToOutputDirMap.keySet()) { - final Pair outputs = myGenerationCompilerModuleToOutputDirMap.get(pair); - compileContext.assignModule(outputs.getFirst(), pair.getSecond(), false); - compileContext.assignModule(outputs.getSecond(), pair.getSecond(), true); + for (Map.Entry, Pair> entry : myGenerationCompilerModuleToOutputDirMap.entrySet()) { + final Pair outputs = entry.getValue(); + Module module = entry.getKey().getSecond(); + compileContext.assignModule(outputs.getFirst(), module, false); + compileContext.assignModule(outputs.getSecond(), module, true); } compileTask.start(new Runnable() { @@ -355,7 +353,6 @@ public class CompileDriver { ExitStatus status = ExitStatus.ERRORS; boolean wereExceptions = false; try { - compileContext.getProgressIndicator().pushState(); if (checkCachesVersion) { checkCachesVersion(compileContext); if (compileContext.isRebuildRequested()) { @@ -379,7 +376,6 @@ public class CompileDriver { } finally { dropDependencyCache(compileContext); - compileContext.getProgressIndicator().popState(); final ExitStatus _status = status; if (compileContext.isRebuildRequested()) { ApplicationManager.getApplication().invokeLater(new Runnable() { @@ -464,7 +460,7 @@ public class CompileDriver { private static class ExitException extends Exception { private final ExitStatus myStatus; - public ExitException(ExitStatus status) { + private ExitException(ExitStatus status) { myStatus = status; } @@ -473,7 +469,7 @@ public class CompileDriver { } } - private ExitStatus doCompile(CompileContextEx context, + private ExitStatus doCompile(final CompileContextEx context, boolean isRebuild, final boolean forceCompile, final boolean trackDependencies, @@ -491,17 +487,11 @@ public class CompileDriver { } if (!onlyCheckStatus) { - try { - context.getProgressIndicator().pushState(); - if (!executeCompileTasks(context, true)) { - if (LOG.isDebugEnabled()) { - LOG.debug("Compilation cancelled"); - } - return ExitStatus.CANCELLED; + if (!executeCompileTasks(context, true)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Compilation cancelled"); } - } - finally { - context.getProgressIndicator().popState(); + return ExitStatus.CANCELLED; } } @@ -512,42 +502,8 @@ public class CompileDriver { return ExitStatus.ERRORS; } - // need this to make sure the VFS is built - boolean needRecalcOutputDirs = false; - final List outputsToRefresh = new ArrayList(); - for (VirtualFile output : context.getAllOutputDirectories()) { - if (output.isValid()) { - walkChildren(output, context); - } - else { - needRecalcOutputDirs = true; - final File file = new File(output.getPath()); - if (!file.exists()) { - final boolean created = file.mkdirs(); - if (!created) { - context.addMessage(CompilerMessageCategory.ERROR, "Failed to create output directory " + file.getPath(), null, 0, 0); - return ExitStatus.ERRORS; - } - } - output = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); - if (output == null) { - context.addMessage(CompilerMessageCategory.ERROR, "Failed to locate output directory " + file.getPath(), null, 0, 0); - return ExitStatus.ERRORS; - } - } - outputsToRefresh.add(output); - } - for (Pair pair : myGenerationCompilerModuleToOutputDirMap.keySet()) { - final Pair generated = myGenerationCompilerModuleToOutputDirMap.get(pair); - walkChildren(generated.getFirst(), context); - outputsToRefresh.add(generated.getFirst()); - walkChildren(generated.getSecond(), context); - outputsToRefresh.add(generated.getSecond()); - } - RefreshQueue.getInstance().refresh(false, true, null, outputsToRefresh.toArray(new VirtualFile[outputsToRefresh.size()])); - if (needRecalcOutputDirs) { - context.recalculateOutputDirs(); - } + final ExitStatus status = refreshFiles(context); + if (status != null) return status; boolean didSomething = false; @@ -557,23 +513,24 @@ public class CompileDriver { didSomething |= generateSources(compilerManager, context, forceCompile, onlyCheckStatus); didSomething |= invokeFileProcessingCompilers(compilerManager, context, SourceInstrumentingCompiler.class, - myProcessingCompilerAdapterFactory, forceCompile, true, onlyCheckStatus); + FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, forceCompile, true, onlyCheckStatus); didSomething |= translate(context, compilerManager, forceCompile, isRebuild, trackDependencies, outputDirectories, onlyCheckStatus); didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassInstrumentingCompiler.class, - myProcessingCompilerAdapterFactory, isRebuild, false, onlyCheckStatus); + FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); // explicitly passing forceCompile = false because in scopes that is narrower than ProjectScope it is impossible // to understand whether the class to be processed is in scope or not. Otherwise compiler may process its items even if // there were changes in completely independent files. didSomething |= invokeFileProcessingCompilers(compilerManager, context, ClassPostProcessingCompiler.class, - myProcessingCompilerAdapterFactory, isRebuild, false, onlyCheckStatus); + FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); - didSomething |= invokeFileProcessingCompilers(compilerManager, context, PackagingCompiler.class, myPackagingCompilerAdapterFactory, + didSomething |= invokeFileProcessingCompilers(compilerManager, context, PackagingCompiler.class, + FILE_PACKAGING_COMPILER_ADAPTER_FACTORY, isRebuild, false, onlyCheckStatus); - didSomething |= invokeFileProcessingCompilers(compilerManager, context, Validator.class, myProcessingCompilerAdapterFactory, + didSomething |= invokeFileProcessingCompilers(compilerManager, context, Validator.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY, forceCompile, true, onlyCheckStatus); } catch (ExitException e) { @@ -590,14 +547,15 @@ public class CompileDriver { final VirtualFile[] allOutputDirs = context.getAllOutputDirectories(); if (didSomething && GENERATE_CLASSPATH_INDEX) { - context.getProgressIndicator().pushState(); - context.getProgressIndicator().setText("Generating classpath index..."); - int count = 0; - for (VirtualFile file : allOutputDirs) { - context.getProgressIndicator().setFraction(((double)++count) / allOutputDirs.length); - createClasspathIndex(file); - } - context.getProgressIndicator().popState(); + runInContext(context, "Generating classpath index...", new ThrowableRunnable(){ + public void run() { + int count = 0; + for (VirtualFile file : allOutputDirs) { + context.getProgressIndicator().setFraction((double)++count / allOutputDirs.length); + createClasspathIndex(file); + } + } + }); } if (!context.getProgressIndicator().isCanceled() && context.getMessageCount(CompilerMessageCategory.ERROR) == 0) { @@ -610,14 +568,8 @@ public class CompileDriver { } if (!onlyCheckStatus) { - try { - context.getProgressIndicator().pushState(); - if (!executeCompileTasks(context, false)) { - return ExitStatus.CANCELLED; - } - } - finally { - context.getProgressIndicator().popState(); + if (!executeCompileTasks(context, false)) { + return ExitStatus.CANCELLED; } } @@ -637,6 +589,53 @@ public class CompileDriver { } } + private ExitStatus refreshFiles(final CompileContextEx context) { + final ExitStatus[] status = {null}; + runInContext(context, "Refreshing files...",new ThrowableRunnable(){ + public void run() { + // need this to make sure the VFS is built + boolean needRecalcOutputDirs = false; + final List outputsToRefresh = new ArrayList(); + for (VirtualFile output : context.getAllOutputDirectories()) { + if (output.isValid()) { + walkChildren(output, context); + } + else { + needRecalcOutputDirs = true; + final File file = new File(output.getPath()); + if (!file.exists()) { + final boolean created = file.mkdirs(); + if (!created) { + context.addMessage(CompilerMessageCategory.ERROR, "Failed to create output directory " + file.getPath(), null, 0, 0); + status[0] = ExitStatus.ERRORS; + return; + } + } + output = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + if (output == null) { + context.addMessage(CompilerMessageCategory.ERROR, "Failed to locate output directory " + file.getPath(), null, 0, 0); + status[0] = ExitStatus.ERRORS; + return; + } + } + outputsToRefresh.add(output); + } + for (Map.Entry, Pair> entry : myGenerationCompilerModuleToOutputDirMap.entrySet()) { + final Pair generated = entry.getValue(); + walkChildren(generated.getFirst(), context); + outputsToRefresh.add(generated.getFirst()); + walkChildren(generated.getSecond(), context); + outputsToRefresh.add(generated.getSecond()); + } + RefreshQueue.getInstance().refresh(false, true, null, outputsToRefresh.toArray(new VirtualFile[outputsToRefresh.size()])); + if (needRecalcOutputDirs) { + context.recalculateOutputDirs(); + } + } + }); + return status[0]; + } + private static void logErrorMessages(final CompileContext context) { final CompilerMessage[] errors = context.getMessages(CompilerMessageCategory.ERROR); if (errors.length > 0) { @@ -700,14 +699,11 @@ public class CompileDriver { } private static void dropDependencyCache(final CompileContextEx context) { - context.getProgressIndicator().pushState(); - try { - context.getProgressIndicator().setText(CompilerBundle.message("progress.saving.caches")); - context.getDependencyCache().dispose(); - } - finally { - context.getProgressIndicator().popState(); - } + runInContext(context, CompilerBundle.message("progress.saving.caches"), new ThrowableRunnable(){ + public void run() { + context.getDependencyCache().dispose(); + } + }); } private boolean generateSources(final CompilerManager compilerManager, @@ -867,70 +863,78 @@ public class CompileDriver { return map; } - private void deleteAll(final CompileContextEx context, Set outputDirectories) { + private static void runInContext(CompileContext context, String title, ThrowableRunnable action) throws T { context.getProgressIndicator().pushState(); + context.getProgressIndicator().setText(title); try { - final boolean isTestMode = ApplicationManager.getApplication().isUnitTestMode(); - final VirtualFile[] allSources = context.getProjectCompileScope().getFiles(null, true); - context.getProgressIndicator().setText(CompilerBundle.message("progress.clearing.output")); - if (myShouldClearOutputDirectory) { - clearOutputDirectories(outputDirectories); - } - else { // refresh is still required - try { - for (final Compiler compiler : CompilerManager.getInstance(myProject).getCompilers(Compiler.class)) { - try { - if (compiler instanceof GeneratingCompiler) { - final StateCache cache = getGeneratingCompilerCache((GeneratingCompiler)compiler); - final Iterator urlIterator = cache.getUrlsIterator(); - while (urlIterator.hasNext()) { - context.getProgressIndicator().checkCanceled(); - deleteFile(new File(VirtualFileManager.extractPath(urlIterator.next()))); - } - } - else if (compiler instanceof TranslatingCompiler) { - final ArrayList> toDelete = new ArrayList>(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - TranslatingCompilerFilesMonitor.getInstance().collectFiles( - context, - (TranslatingCompiler)compiler, Arrays.asList(allSources).iterator(), - true /*pass true to make sure that every source in scope file is processed*/, - false /*important! should pass false to enable collection of files to delete*/, - new ArrayList(), - toDelete - ); - } - }); - for (Trinity trinity : toDelete) { - context.getProgressIndicator().checkCanceled(); - final File file = trinity.getFirst(); - final boolean deleted = deleteFile(file); - if (isTestMode && deleted) { - CompilerManagerImpl.addDeletedPath(FileUtil.toSystemIndependentName(file.getPath())); - } - } - } - } - catch (IOException e) { - LOG.info(e); - } - } - pruneEmptyDirectories(context.getProgressIndicator(), outputDirectories); // to avoid too much files deleted events - } - finally { - CompilerUtil.refreshIODirectories(outputDirectories); - } - } - dropScopesCaches(); - - clearCompilerSystemDirectory(context); + action.run(); } finally { context.getProgressIndicator().popState(); } } + private void deleteAll(final CompileContextEx context, final Set outputDirectories) { + runInContext(context, CompilerBundle.message("progress.clearing.output"), new ThrowableRunnable(){ + public void run() { + final boolean isTestMode = ApplicationManager.getApplication().isUnitTestMode(); + final VirtualFile[] allSources = context.getProjectCompileScope().getFiles(null, true); + if (myShouldClearOutputDirectory) { + clearOutputDirectories(outputDirectories); + } + else { // refresh is still required + try { + for (final Compiler compiler : CompilerManager.getInstance(myProject).getCompilers(Compiler.class)) { + try { + if (compiler instanceof GeneratingCompiler) { + final StateCache cache = getGeneratingCompilerCache((GeneratingCompiler)compiler); + final Iterator urlIterator = cache.getUrlsIterator(); + while (urlIterator.hasNext()) { + context.getProgressIndicator().checkCanceled(); + deleteFile(new File(VirtualFileManager.extractPath(urlIterator.next()))); + } + } + else if (compiler instanceof TranslatingCompiler) { + final ArrayList> toDelete = new ArrayList>(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + TranslatingCompilerFilesMonitor.getInstance().collectFiles( + context, + (TranslatingCompiler)compiler, Arrays.asList(allSources).iterator(), + true /*pass true to make sure that every source in scope file is processed*/, + false /*important! should pass false to enable collection of files to delete*/, + new ArrayList(), + toDelete + ); + } + }); + for (Trinity trinity : toDelete) { + context.getProgressIndicator().checkCanceled(); + final File file = trinity.getFirst(); + final boolean deleted = deleteFile(file); + if (isTestMode && deleted) { + CompilerManagerImpl.addDeletedPath(FileUtil.toSystemIndependentName(file.getPath())); + } + } + } + } + catch (IOException e) { + LOG.info(e); + } + } + pruneEmptyDirectories(context.getProgressIndicator(), outputDirectories); // to avoid too much files deleted events + } + finally { + CompilerUtil.refreshIODirectories(outputDirectories); + } + } + dropScopesCaches(); + + clearCompilerSystemDirectory(context); + } + }); + } + private void dropScopesCaches() { // hack to be sure the classpath will include the output directories ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -946,23 +950,26 @@ public class CompileDriver { } } - private static boolean doPrune(ProgressIndicator progress, final File directory, final Set outPutDirectories) { + private static boolean doPrune(ProgressIndicator progress, final File directory, final Set outputDirectories) { progress.checkCanceled(); final File[] files = directory.listFiles(); boolean isEmpty = true; - if (files != null) { + if (files == null) { + isEmpty = false; + } + else { for (File file : files) { - if (file.isDirectory() && !outPutDirectories.contains(file)) { - if (doPrune(progress, file, outPutDirectories)) { + if (outputDirectories.contains(file)) { + isEmpty = false; + } + else { + if (doPrune(progress, file, outputDirectories)) { deleteFile(file); } else { isEmpty = false; } } - else { - isEmpty = false; - } } } @@ -1074,13 +1081,13 @@ public class CompileDriver { final List toGenerate = new ArrayList(); final List filesToRefresh = new ArrayList(); final List generatedFiles = new ArrayList(); - final List affectedModules = new ArrayList(); + final Set affectedModules = new THashSet(); try { final StateCache cache = getGeneratingCompilerCache(compiler); final Set pathsToRemove = new HashSet(cache.getUrls()); final Map itemToOutputPathMap = new HashMap(); - final IOException[] ex = new IOException[] {null}; + final IOException[] ex = {null}; ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { for (final GeneratingCompiler.GenerationItem item : allItems) { @@ -1091,7 +1098,7 @@ public class CompileDriver { try { final ValidityState savedState = cache.getState(outputPath); - + if (forceGenerate || savedState == null || !savedState.equalsTo(item.getValidityState())) { final String outputPathUrl = VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, outputPath); if (context.getCompileScope().belongs(outputPathUrl)) { @@ -1131,20 +1138,21 @@ public class CompileDriver { } if (!pathsToRemove.isEmpty()) { - context.getProgressIndicator().pushState(); - context.getProgressIndicator().setText(CompilerBundle.message("progress.synchronizing.output.directory")); - for (final String path : pathsToRemove) { - final File file = new File(path); - final boolean deleted = deleteFile(file); - if (deleted) { - cache.remove(path); - filesToRefresh.add(file); + runInContext(context, CompilerBundle.message("progress.synchronizing.output.directory"), new ThrowableRunnable(){ + public void run() throws IOException { + for (final String path : pathsToRemove) { + final File file = new File(path); + final boolean deleted = deleteFile(file); + if (deleted) { + cache.remove(path); + filesToRefresh.add(file); + } + } } - } - context.getProgressIndicator().popState(); + }); } - Map> moduleToItemMap = + final Map> moduleToItemMap = buildModuleToGenerationItemMap(toGenerate.toArray(new GeneratingCompiler.GenerationItem[toGenerate.size()])); List modules = new ArrayList(moduleToItemMap.size()); for (final Module module : moduleToItemMap.keySet()) { @@ -1153,38 +1161,36 @@ public class CompileDriver { ModuleCompilerUtil.sortModules(myProject, modules); for (final Module module : modules) { - context.getProgressIndicator().pushState(); - try { - final Set items = moduleToItemMap.get(module); - if (items != null && !items.isEmpty()) { - final GeneratingCompiler.GenerationItem[][] productionAndTestItems = splitGenerationItems(items); - boolean moduleAffected = false; - for (GeneratingCompiler.GenerationItem[] _items : productionAndTestItems) { - if (_items.length > 0) { - final VirtualFile outputDir = getGenerationOutputDir(compiler, module, _items[0].isTestSource()); - final GeneratingCompiler.GenerationItem[] successfullyGenerated = compiler.generate(context, _items, outputDir); - context.getProgressIndicator().setText(CompilerBundle.message("progress.updating.caches")); - if (successfullyGenerated.length > 0) { - moduleAffected = true; - } - for (final GeneratingCompiler.GenerationItem item : successfullyGenerated) { - final String fullOutputPath = itemToOutputPathMap.get(item); - cache.update(fullOutputPath, item.getValidityState()); - final File file = new File(fullOutputPath); - filesToRefresh.add(file); - generatedFiles.add(file); + runInContext(context, "Generating output from "+compiler.getDescription(),new ThrowableRunnable(){ + public void run() throws IOException { + final Set items = moduleToItemMap.get(module); + if (items != null && !items.isEmpty()) { + final GeneratingCompiler.GenerationItem[][] productionAndTestItems = splitGenerationItems(items); + for (GeneratingCompiler.GenerationItem[] _items : productionAndTestItems) { + if (_items.length > 0) { + final VirtualFile outputDir = getGenerationOutputDir(compiler, module, _items[0].isTestSource()); + final GeneratingCompiler.GenerationItem[] successfullyGenerated = compiler.generate(context, _items, outputDir); + + context.getProgressIndicator().setText(CompilerBundle.message("progress.updating.caches")); + runInContext(context, CompilerBundle.message("progress.updating.caches"), new ThrowableRunnable() { + public void run() throws IOException { + if (successfullyGenerated.length > 0) { + affectedModules.add(module); + } + for (final GeneratingCompiler.GenerationItem item : successfullyGenerated) { + final String fullOutputPath = itemToOutputPathMap.get(item); + cache.update(fullOutputPath, item.getValidityState()); + final File file = new File(fullOutputPath); + filesToRefresh.add(file); + generatedFiles.add(file); + } + } + }); } } } - - if (moduleAffected) { - affectedModules.add(module); - } } - } - finally { - context.getProgressIndicator().popState(); - } + }); } } catch (IOException e) { @@ -1193,7 +1199,6 @@ public class CompileDriver { throw new ExitException(ExitStatus.ERRORS); } finally { - context.getProgressIndicator().pushState(); CompilerUtil.refreshIOFiles(filesToRefresh); if (!generatedFiles.isEmpty()) { List vFiles = ApplicationManager.getApplication().runReadAction(new Computable>() { @@ -1244,116 +1249,118 @@ public class CompileDriver { final boolean trackDependencies, final Set outputDirectories, final boolean onlyCheckStatus) throws ExitException { - - final Set toCompile = new HashSet(); final List> toDelete = new ArrayList>(); - context.getProgressIndicator().pushState(); - final boolean[] wereFilesDeleted = new boolean[]{false}; - try { - final TranslatingCompilerFilesMonitor monitor = TranslatingCompilerFilesMonitor.getInstance(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - - TranslatingCompilerFilesMonitor.getInstance().collectFiles( - context, compiler, Arrays.asList(snapshot).iterator(), forceCompile, isRebuild, toCompile, toDelete - ); - if (trackDependencies && !toCompile.isEmpty()) { // should add dependent files - final FileTypeManager fileTypeManager = FileTypeManager.getInstance(); - final PsiManager psiManager = PsiManager.getInstance(myProject); - final VirtualFile[] filesToCompile = toCompile.toArray(new VirtualFile[toCompile.size()]); - for (final VirtualFile file : filesToCompile) { - if (fileTypeManager.getFileTypeByFile(file) == StdFileTypes.JAVA) { - final PsiFile psiFile = psiManager.findFile(file); - if (psiFile != null) { - addDependentFiles(psiFile, toCompile, compiler, context); + final boolean[] wereFilesDeleted = {false}; + runInContext(context, "Compiling using "+compiler.getDescription(), new ThrowableRunnable(){ + public void run() throws ExitException { + try { + final TranslatingCompilerFilesMonitor monitor = TranslatingCompilerFilesMonitor.getInstance(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + TranslatingCompilerFilesMonitor.getInstance().collectFiles( + context, compiler, Arrays.asList(snapshot).iterator(), forceCompile, isRebuild, toCompile, toDelete + ); + if (trackDependencies && !toCompile.isEmpty()) { // should add dependent files + final FileTypeManager fileTypeManager = FileTypeManager.getInstance(); + final PsiManager psiManager = PsiManager.getInstance(myProject); + final VirtualFile[] filesToCompile = toCompile.toArray(new VirtualFile[toCompile.size()]); + for (final VirtualFile file : filesToCompile) { + if (fileTypeManager.getFileTypeByFile(file) == StdFileTypes.JAVA) { + final PsiFile psiFile = psiManager.findFile(file); + if (psiFile != null) { + addDependentFiles(psiFile, toCompile, compiler, context); + } + } } } } + }); + + if (onlyCheckStatus) { + if (toDelete.isEmpty() && toCompile.isEmpty()) { + return; + } + if (LOG.isDebugEnabled()) { + if (!toDelete.isEmpty()) { + LOG.debug("Found items to delete, compiler " + compiler.getDescription()); + } + if (!toCompile.isEmpty()) { + LOG.debug("Found items to compile, compiler " + compiler.getDescription()); + } + } + throw new ExitException(ExitStatus.CANCELLED); } - } - }); - if (onlyCheckStatus) { - if (toDelete.isEmpty() && toCompile.isEmpty()) { - return false; - } - if (LOG.isDebugEnabled()) { if (!toDelete.isEmpty()) { - LOG.debug("Found items to delete, compiler " + compiler.getDescription()); + try { + wereFilesDeleted[0] = syncOutputDir(context, toDelete, outputDirectories); + } + catch (CacheCorruptedException e) { + LOG.info(e); + context.requestRebuildNextTime(e.getMessage()); + } } - if (!toCompile.isEmpty()) { - LOG.debug("Found items to compile, compiler " + compiler.getDescription()); - } - } - throw new ExitException(ExitStatus.CANCELLED); - } - if (!toDelete.isEmpty()) { - try { - wereFilesDeleted[0] = syncOutputDir(context, toDelete, outputDirectories); + if ((wereFilesDeleted[0] || !toCompile.isEmpty()) && context.getMessageCount(CompilerMessageCategory.ERROR) == 0) { + final TranslatingCompiler.ExitStatus exitStatus = compiler.compile(context, toCompile.toArray(new VirtualFile[toCompile.size()])); + monitor.update(context, exitStatus.getSuccessfullyCompiled(), exitStatus.getFilesToRecompile()); + } } - catch (CacheCorruptedException e) { + catch (IOException e) { LOG.info(e); context.requestRebuildNextTime(e.getMessage()); + throw new ExitException(ExitStatus.ERRORS); } } - - if ((wereFilesDeleted[0] || !toCompile.isEmpty()) && context.getMessageCount(CompilerMessageCategory.ERROR) == 0) { - final TranslatingCompiler.ExitStatus exitStatus = compiler.compile(context, toCompile.toArray(new VirtualFile[toCompile.size()])); - monitor.update(context, exitStatus.getSuccessfullyCompiled(), exitStatus.getFilesToRecompile()); - } - } - catch (IOException e) { - LOG.info(e); - context.requestRebuildNextTime(e.getMessage()); - throw new ExitException(ExitStatus.ERRORS); - } - finally { - context.getProgressIndicator().popState(); - } + }); return !toCompile.isEmpty() || wereFilesDeleted[0]; } private static boolean syncOutputDir(final CompileContextEx context, final Collection> toDelete, final Set outputDirectories) throws CacheCorruptedException { - int total = toDelete.size(); + final int total = toDelete.size(); final DependencyCache dependencyCache = context.getDependencyCache(); final boolean isTestMode = ApplicationManager.getApplication().isUnitTestMode(); - context.getProgressIndicator().pushState(); final List filesToRefresh = new ArrayList(); try { - context.getProgressIndicator().setText(CompilerBundle.message("progress.synchronizing.output.directory")); - int current = 0; - boolean wereFilesDeleted = false; - for (final Trinity trinity : toDelete) { - context.getProgressIndicator().setFraction(((double)(++current)) / total); - context.getProgressIndicator().checkCanceled(); - final File outputPath = trinity.getFirst(); - filesToRefresh.add(outputPath); - if (deleteFile(outputPath)) { - wereFilesDeleted = true; - final String className = trinity.getSecond(); - if (className != null) { - final int id = dependencyCache.getSymbolTable().getId(className); - dependencyCache.addTraverseRoot(id); - final boolean sourcePresent = trinity.getThird().booleanValue(); - if (!sourcePresent) { - dependencyCache.markSourceRemoved(id); + final boolean[] wereFilesDeleted = {false}; + runInContext(context, CompilerBundle.message("progress.synchronizing.output.directory"), new ThrowableRunnable(){ + public void run() throws CacheCorruptedException { + int current = 0; + for (final Trinity trinity : toDelete) { + context.getProgressIndicator().setFraction((double)++current / total); + context.getProgressIndicator().checkCanceled(); + final File outputPath = trinity.getFirst(); + filesToRefresh.add(outputPath); + if (deleteFile(outputPath)) { + wereFilesDeleted[0] = true; + final String className = trinity.getSecond(); + if (className != null) { + final int id = dependencyCache.getSymbolTable().getId(className); + dependencyCache.addTraverseRoot(id); + final boolean sourcePresent = trinity.getThird().booleanValue(); + if (!sourcePresent) { + dependencyCache.markSourceRemoved(id); + } + } + if (isTestMode) { + CompilerManagerImpl.addDeletedPath(outputPath.getPath()); + } } } - if (isTestMode) { - CompilerManagerImpl.addDeletedPath(outputPath.getPath()); - } + runInContext(context, "Pruning empty directories...", new ThrowableRunnable() { + public void run() throws RuntimeException { + pruneEmptyDirectories(context.getProgressIndicator(), outputDirectories); + } + }); } - } - pruneEmptyDirectories(context.getProgressIndicator(), outputDirectories); - return wereFilesDeleted; + }); + return wereFilesDeleted[0]; } finally { CompilerUtil.refreshIOFiles(filesToRefresh); - context.getProgressIndicator().popState(); } } @@ -1409,14 +1416,14 @@ public class CompileDriver { final CompileScope scope = context.getCompileScope(); final List toProcess = new ArrayList(); final Set allUrls = new HashSet(); - final IOException[] ex = new IOException[] {null}; + final IOException[] ex = {null}; ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { try { for (FileProcessingCompiler.ProcessingItem item : items) { final VirtualFile file = item.getFile(); if (file == null) { - LOG.assertTrue(false, "FileProcessingCompiler.ProcessingItem.getFile() must not return null: compiler " + adapter.getCompiler().getDescription()); + LOG.error("FileProcessingCompiler.ProcessingItem.getFile() must not return null: compiler " + adapter.getCompiler().getDescription()); } final String url = file.getUrl(); allUrls.add(url); @@ -1446,26 +1453,27 @@ public class CompileDriver { final Collection urls = cache.getUrls(); final List urlsToRemove = new ArrayList(); if (!urls.isEmpty()) { - context.getProgressIndicator().pushState(); - context.getProgressIndicator().setText(CompilerBundle.message("progress.processing.outdated.files")); - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - for (final String url : urls) { - if (!allUrls.contains(url)) { - if (!checkScope || scope.belongs(url)) { - urlsToRemove.add(url); + runInContext(context, CompilerBundle.message("progress.processing.outdated.files"), new ThrowableRunnable(){ + public void run() throws IOException { + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + for (final String url : urls) { + if (!allUrls.contains(url)) { + if (!checkScope || scope.belongs(url)) { + urlsToRemove.add(url); + } + } } } + }); + if (!onlyCheckStatus && !urlsToRemove.isEmpty()) { + for (final String url : urlsToRemove) { + adapter.processOutdatedItem(context, url, cache.getExtState(url)); + cache.remove(url); + } } } }); - if (!onlyCheckStatus && !urlsToRemove.isEmpty()) { - for (final String url : urlsToRemove) { - adapter.processOutdatedItem(context, url, cache.getExtState(url)); - cache.remove(url); - } - } - context.getProgressIndicator().popState(); } if (onlyCheckStatus) { @@ -1493,33 +1501,33 @@ public class CompileDriver { return false; } - context.getProgressIndicator().pushState(); final FileProcessingCompiler.ProcessingItem[] processed = adapter.process(toProcess.toArray(new FileProcessingCompiler.ProcessingItem[toProcess.size()])); - context.getProgressIndicator().popState(); if (processed.length > 0) { - context.getProgressIndicator().pushState(); - context.getProgressIndicator().setText(CompilerBundle.message("progress.updating.caches")); - List vFiles = new ArrayList(processed.length); - for (FileProcessingCompiler.ProcessingItem aProcessed : processed) { - final VirtualFile file = aProcessed.getFile(); - vFiles.add(file); - if (LOG.isDebugEnabled()) { - LOG.debug("File processed by " + adapter.getCompiler().getDescription()); - LOG.debug("\tFile processed " + file.getPresentableUrl() + "; ts=" + file.getTimeStamp()); + runInContext(context, CompilerBundle.message("progress.updating.caches"), new ThrowableRunnable(){ + public void run() { + List vFiles = new ArrayList(processed.length); + for (FileProcessingCompiler.ProcessingItem aProcessed : processed) { + final VirtualFile file = aProcessed.getFile(); + vFiles.add(file); + if (LOG.isDebugEnabled()) { + LOG.debug("File processed by " + adapter.getCompiler().getDescription()); + LOG.debug("\tFile processed " + file.getPresentableUrl() + "; ts=" + file.getTimeStamp()); + } + } + LocalFileSystem.getInstance().refreshFiles(vFiles); + if (LOG.isDebugEnabled()) { + LOG.debug("Files after VFS refresh:"); + for (VirtualFile file : vFiles) { + LOG.debug("\t" + file.getPresentableUrl() + "; ts=" + file.getTimeStamp()); + } + } + for (FileProcessingCompiler.ProcessingItem item : processed) { + cacheUpdater.addFileForUpdate(item, cache); + } } - } - LocalFileSystem.getInstance().refreshFiles(vFiles); - if (LOG.isDebugEnabled()) { - LOG.debug("Files after VFS refresh:"); - for (VirtualFile file : vFiles) { - LOG.debug("\t" + file.getPresentableUrl() + "; ts=" + file.getTimeStamp()); - } - } - for (FileProcessingCompiler.ProcessingItem item : processed) { - cacheUpdater.addFileForUpdate(item, cache); - } + }); } return true; } @@ -1556,21 +1564,25 @@ public class CompileDriver { }, null); } - private boolean executeCompileTasks(CompileContext context, boolean beforeTasks) { + private boolean executeCompileTasks(final CompileContext context, final boolean beforeTasks) { final CompilerManager manager = CompilerManager.getInstance(myProject); final ProgressIndicator progressIndicator = context.getProgressIndicator(); try { - CompileTask[] tasks = beforeTasks ? manager.getBeforeTasks() : manager.getAfterTasks(); - if (tasks.length > 0) { - progressIndicator.setText(beforeTasks - ? CompilerBundle.message("progress.executing.precompile.tasks") - : CompilerBundle.message("progress.executing.postcompile.tasks")); - for (CompileTask task : tasks) { - if (!task.execute(context)) { - return false; + final boolean[] result = {true}; + runInContext(context, beforeTasks + ? CompilerBundle.message("progress.executing.precompile.tasks") + : CompilerBundle.message("progress.executing.postcompile.tasks"),new ThrowableRunnable(){ + public void run() { + CompileTask[] tasks = beforeTasks ? manager.getBeforeTasks() : manager.getAfterTasks(); + for (CompileTask task : tasks) { + if (!task.execute(context)) { + result[0] = false; + return; + } } } - } + }); + return result[0]; } finally { WindowManager.getInstance().getStatusBar(myProject).setInfo(""); @@ -1582,7 +1594,6 @@ public class CompileDriver { }); } } - return true; } // todo: add validation for module chunks: all modules that form a chunk must have the same JDK @@ -1877,7 +1888,7 @@ public class CompileDriver { } public void doUpdate() throws IOException{ - final IOException[] ex = new IOException[] {null}; + final IOException[] ex = {null}; ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { try { diff --git a/compiler/impl/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java b/compiler/impl/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java index c02f07acf67d..4627ee5efce0 100644 --- a/compiler/impl/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java +++ b/compiler/impl/com/intellij/compiler/impl/TranslatingCompilerFilesMonitor.java @@ -172,7 +172,7 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { final Project project = context.getProject(); final int projectId = getProjectId(project); final LocalFileSystem lfs = LocalFileSystem.getInstance(); - final IOException[] _ex = new IOException[] {null}; + final IOException[] _ex = {null}; // need read action here to ensure that no modifications were made to VFS while updating file attributes ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -1032,7 +1032,7 @@ public class TranslatingCompilerFilesMonitor implements ApplicationComponent { private final String mySrcUrl; private final LocalFileSystem myFileSystem; - public ScheduleOutputsForDeletionProc(final String srcUrl) { + private ScheduleOutputsForDeletionProc(final String srcUrl) { mySrcUrl = srcUrl; myFileSystem = LocalFileSystem.getInstance(); } diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompiler.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompiler.java index cde55a4f0d32..022c495b58aa 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompiler.java @@ -16,17 +16,16 @@ public interface BackendCompiler { @NotNull @NonNls String getId(); // used for externalization @NotNull String getPresentableName(); @NotNull Configurable createConfigurable(); - @NotNull - Set getCompilableFileTypes(); - @Nullable OutputParser createErrorParser(final String outputDir); - @Nullable OutputParser createOutputParser(final String outputDir); + @NotNull Set getCompilableFileTypes(); + @Nullable OutputParser createErrorParser(@NotNull String outputDir); + @Nullable OutputParser createOutputParser(@NotNull String outputDir); boolean checkCompiler(final CompileScope scope); @NotNull Process launchProcess( - final ModuleChunk chunk, - final String outputDir, - final CompileContext compileContext) throws IOException; + @NotNull ModuleChunk chunk, + @NotNull String outputDir, + @NotNull CompileContext compileContext) throws IOException; void compileFinished(); diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java index 5a6209ac99a9..c76a6bc0dc9b 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/BackendCompilerWrapper.java @@ -35,17 +35,17 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; import com.intellij.util.Chunk; import com.intellij.util.StringBuilderSpinAllocator; import com.intellij.util.cls.ClsFormatException; import gnu.trove.TIntHashSet; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import java.io.File; -import java.io.FileOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; import java.util.concurrent.ArrayBlockingQueue; @@ -64,35 +64,36 @@ public class BackendCompilerWrapper { private final List myOutputItems; private final CompileContextEx myCompileContext; - private final VirtualFile[] myFilesToCompile; + private final List myFilesToCompile; private final Project myProject; private final Set myFilesToRecompile; private final Map myModuleToTempDirMap = new HashMap(); private final ProjectFileIndex myProjectFileIndex; @NonNls private static final String PACKAGE_ANNOTATION_FILE_NAME = "package-info.java"; + private static final FileObject myStopThreadToken = new FileObject(null,null); - public BackendCompilerWrapper(final Project project, - VirtualFile[] filesToCompile, - CompileContextEx compileContext, - BackendCompiler compiler) { + public BackendCompilerWrapper(@NotNull final Project project, + @NotNull List filesToCompile, + @NotNull CompileContextEx compileContext, + @NotNull BackendCompiler compiler) { myProject = project; myCompiler = compiler; myCompileContext = compileContext; myFilesToCompile = filesToCompile; - myFilesToRecompile = new HashSet(Arrays.asList(filesToCompile)); + myFilesToRecompile = new HashSet(filesToCompile); myProjectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); - mySuccesfullyCompiledJavaFiles = new HashSet(filesToCompile.length); - myOutputItems = new ArrayList(filesToCompile.length); - myFileNameToSourceMap = new HashMap>(filesToCompile.length); - myFilesToRefresh = new ArrayList(filesToCompile.length); + mySuccesfullyCompiledJavaFiles = new HashSet(filesToCompile.size()); + myOutputItems = new ArrayList(filesToCompile.size()); + myFileNameToSourceMap = new HashMap>(filesToCompile.size()); + myFilesToRefresh = new ArrayList(filesToCompile.size()); } - public TranslatingCompiler.OutputItem[] compile() throws CompilerException, CacheCorruptedException { + public List compile() throws CompilerException, CacheCorruptedException { Application application = ApplicationManager.getApplication(); final Set allDependent = new HashSet(); COMPILE: try { - if (myFilesToCompile.length > 0) { + if (!myFilesToCompile.isEmpty()) { if (application.isUnitTestMode()) { saveTestData(); } @@ -101,20 +102,18 @@ public class BackendCompilerWrapper { compileModules(moduleToFilesMap); } - - VirtualFile[] dependentFiles; + Collection dependentFiles; do { dependentFiles = findDependentFiles(); - if (dependentFiles.length > 0) { - final List deps = Arrays.asList(dependentFiles); - myFilesToRecompile.addAll(deps); - allDependent.addAll(deps); + if (!dependentFiles.isEmpty()) { + myFilesToRecompile.addAll(dependentFiles); + allDependent.addAll(dependentFiles); if (myCompileContext.getProgressIndicator().isCanceled() || myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) { break COMPILE; } - final VirtualFile[] filesInScope = getFilesInScope(dependentFiles); - if (filesInScope.length == 0) { + final List filesInScope = getFilesInScope(dependentFiles); + if (filesInScope.isEmpty()) { break; } final Map> moduleToFilesMap = buildModuleToFilesMap(myCompileContext, filesInScope); @@ -122,7 +121,7 @@ public class BackendCompilerWrapper { compileModules(moduleToFilesMap); } } - while (dependentFiles.length > 0 && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0); + while (!dependentFiles.isEmpty() && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0); } catch (IOException e) { throw new CompilerException(CompilerBundle.message("error.compiler.process.not.started", e.getMessage()), e); @@ -151,7 +150,7 @@ public class BackendCompilerWrapper { if (myCompileContext.getProgressIndicator().isCanceled()) { myFilesToRecompile.clear(); // when cancelled pretend nothing was compiled and next compile will compile everything from the scratch - return TranslatingCompiler.EMPTY_OUTPUT_ITEM_ARRAY; + return Collections.emptyList(); } // do not update caches if cancelled because there is a chance that they will be incomplete @@ -163,7 +162,7 @@ public class BackendCompilerWrapper { myFilesToRecompile.addAll(allDependent); } processPackageInfoFiles(); - return myOutputItems.toArray(new TranslatingCompiler.OutputItem[myOutputItems.size()]); + return myOutputItems; } // package-info.java hack @@ -192,8 +191,8 @@ public class BackendCompilerWrapper { }); } - private VirtualFile[] getFilesInScope(final VirtualFile[] dependentFiles) { - final List filesInScope = new ArrayList(dependentFiles.length); + private List getFilesInScope(final Collection dependentFiles) { + final List filesInScope = new ArrayList(dependentFiles.size()); ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { for (VirtualFile dependentFile : dependentFiles) { @@ -203,7 +202,7 @@ public class BackendCompilerWrapper { } } }); - return filesInScope.toArray(new VirtualFile[filesInScope.size()]); + return filesInScope; } private void compileModules(final Map> moduleToFilesMap) throws IOException { @@ -328,7 +327,7 @@ public class BackendCompilerWrapper { private final TIntHashSet myProcessedNames = new TIntHashSet(); private final Set myProcessedFiles = new HashSet(); - private VirtualFile[] findDependentFiles() throws CacheCorruptedException { + private Collection findDependentFiles() throws CacheCorruptedException { myCompileContext.getProgressIndicator().setText(CompilerBundle.message("progress.checking.dependencies")); final DependencyCache dependencyCache = myCompileContext.getDependencyCache(); @@ -388,7 +387,7 @@ public class BackendCompilerWrapper { } myCompileContext.getProgressIndicator().setText(CompilerBundle.message("progress.found.dependent.files", dependentFiles.size())); - return dependentFiles.toArray(new VirtualFile[dependentFiles.size()]); + return dependentFiles; } private final Object lock = new Object(); @@ -424,14 +423,14 @@ public class BackendCompilerWrapper { } } - protected void processCompiledClass(final String classFileToProcess) throws CacheCorruptedException { + protected void processCompiledClass(final FileObject classFileToProcess) throws CacheCorruptedException { synchronized (lock) { myClassParsingThread.addPath(classFileToProcess); } } } - private void doCompile(final ModuleChunk chunk, String outputDir, int sourcesFilter) throws IOException { + private void doCompile(@NotNull final ModuleChunk chunk, @NotNull String outputDir, int sourcesFilter) throws IOException { myCompileContext.getProgressIndicator().checkCanceled(); chunk.setSourcesFilter(sourcesFilter); @@ -866,7 +865,7 @@ public class BackendCompilerWrapper { myCompileContext.getProgressIndicator().setText2(msg); } - private static Map> buildModuleToFilesMap(final CompileContext context, final VirtualFile[] files) { + private static Map> buildModuleToFilesMap(final CompileContext context, final List files) { final Map> map = new HashMap>(); ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { @@ -890,9 +889,8 @@ public class BackendCompilerWrapper { } private class ClassParsingThread implements Runnable { - private final BlockingQueue myPaths = new ArrayBlockingQueue(50000); + private final BlockingQueue myPaths = new ArrayBlockingQueue(50000); private CacheCorruptedException myError = null; - private final String myStopThreadToken = new String(); private final boolean myAddNotNullAssertions; private final boolean myIsJdk16; @@ -903,10 +901,11 @@ public class BackendCompilerWrapper { public void run() { try { - //noinspection StringEquality - String path; - while ((path = getNextPath()) != myStopThreadToken) { - processPath(path.replace('/', File.separatorChar)); + while (true) { + FileObject path = myPaths.take(); + + if (path == myStopThreadToken) break; + processPath(path); } } catch (InterruptedException e) { @@ -917,30 +916,23 @@ public class BackendCompilerWrapper { } } - public void addPath(String path) throws CacheCorruptedException { + public void addPath(FileObject path) throws CacheCorruptedException { if (myError != null) { throw myError; } myPaths.offer(path); } - private String getNextPath() throws InterruptedException { - return myPaths.take(); - } - public void stopParsing() { myPaths.offer(myStopThreadToken); } - private void processPath(final String path) throws CacheCorruptedException { + private void processPath(FileObject fileObject) throws CacheCorruptedException { + File file = fileObject.getFile(); + byte[] fileContent = fileObject.getContent(); + String path = file.getPath(); try { - final File file = new File(path); // the file is assumed to exist! - byte[] fileContent = ArrayUtil.EMPTY_BYTE_ARRAY; - try{ - fileContent = FileUtil.loadFileBytes(file); - } - catch(IOException ignored){ - } + // the file is assumed to exist! final DependencyCache dependencyCache = myCompileContext.getDependencyCache(); final int newClassQName = dependencyCache.reparseClassFile(file, fileContent); @@ -949,7 +941,8 @@ public class BackendCompilerWrapper { final String qName = dependencyCache.resolve(newClassQName); String relativePathToSource = "/" + MakeUtil.createRelativePathToSource(qName, sourceFileName); putName(sourceFileName, newClassQName, relativePathToSource, path); - + + boolean fileContentChanged = false; if (myAddNotNullAssertions && hasNotNullAnnotations(newClassesCache, dependencyCache.getSymbolTable(), newClassQName)) { try { ClassReader reader = new ClassReader(fileContent, 0, fileContent.length); @@ -958,19 +951,18 @@ public class BackendCompilerWrapper { final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer); reader.accept(instrumenter, 0); if (instrumenter.isModification()) { - final FileOutputStream output = new FileOutputStream(file); - try { - output.write(writer.toByteArray()); - } - finally { - output.close(); - } + fileContent = writer.toByteArray(); + fileContentChanged = true; } } catch (Exception ignored) { LOG.info(ignored); } } + + if (fileContentChanged || !fileObject.isSaved()) { + writeFile(file, fileContent); + } } catch (ClsFormatException e) { String message; @@ -983,11 +975,24 @@ public class BackendCompilerWrapper { } myCompileContext.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1); } + catch (IOException e) { + myCompileContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1); + } finally { myClassesCount += 1; updateStatistics(); } } + + private void writeFile(File file, byte[] fileContent) throws IOException { + try { + FileUtil.writeToFile(file, fileContent); + } + catch (FileNotFoundException e) { + FileUtil.createParentDirs(file); + FileUtil.writeToFile(file, fileContent); + } + } } private static boolean hasNotNullAnnotations(final Cache cache, final SymbolTable symbolTable, final int className) throws CacheCorruptedException { @@ -1025,11 +1030,12 @@ public class BackendCompilerWrapper { private final String myPath; private final int myKind; - private OutputDir(String path, int kind) { + private OutputDir(@NotNull String path, int kind) { myPath = path; myKind = kind; } + @NotNull public String getPath() { return myPath; } diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java index 71d08533026c..7557fa39694d 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/CompilerParsingThread.java @@ -24,10 +24,9 @@ public abstract class CompilerParsingThread implements Runnable, OutputParser.Ca private boolean mySkipLF = false; private Throwable myError = null; private final boolean myIsUnitTestMode; - private String myClassFileToProcess = null; + private FileObject myClassFileToProcess = null; private String myLastReadLine = null; - private boolean myProcessExited = false; - + private volatile boolean myProcessExited = false; public CompilerParsingThread(Process process, OutputParser outputParser, final boolean readErrorStream, boolean trimLines) { myProcess = process; @@ -44,7 +43,7 @@ public abstract class CompilerParsingThread implements Runnable, OutputParser.Ca if (!myIsUnitTestMode && myProcess == null) { break; } - if (isCanceled()) { + if (myProcessExited || isCanceled()) { break; } if (!myOutputParser.processMessageLine(this)) { @@ -57,6 +56,7 @@ public abstract class CompilerParsingThread implements Runnable, OutputParser.Ca } } catch (Throwable e) { + e.printStackTrace(); myError = e; LOG.info(e); } @@ -92,8 +92,8 @@ public abstract class CompilerParsingThread implements Runnable, OutputParser.Ca return myLastReadLine; } - public final void fileGenerated(String path) { - String previousPath = myClassFileToProcess; + public final void fileGenerated(FileObject path) { + FileObject previousPath = myClassFileToProcess; myClassFileToProcess = path; if (previousPath != null) { try { @@ -108,8 +108,7 @@ public abstract class CompilerParsingThread implements Runnable, OutputParser.Ca protected abstract boolean isCanceled(); - protected abstract void processCompiledClass(final String classFileToProcess) throws CacheCorruptedException; - + protected abstract void processCompiledClass(final FileObject classFileToProcess) throws CacheCorruptedException; private String readLine(final Reader reader) { StringBuilder buffer; @@ -176,11 +175,11 @@ public abstract class CompilerParsingThread implements Runnable, OutputParser.Ca } } - private synchronized boolean isProcessTerminated() { + private boolean isProcessTerminated() { return myProcessExited; } - public synchronized void setProcessTerminated(final boolean procesExited) { + public void setProcessTerminated(final boolean procesExited) { myProcessExited = procesExited; } } diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/ExternalCompiler.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/ExternalCompiler.java index f08abfd9f602..562861cbdb67 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/ExternalCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/ExternalCompiler.java @@ -31,7 +31,7 @@ public abstract class ExternalCompiler implements BackendCompiler { } @NotNull - public Process launchProcess(final ModuleChunk chunk, final String outputDir, final CompileContext compileContext) throws IOException { + public Process launchProcess(@NotNull final ModuleChunk chunk, @NotNull final String outputDir, @NotNull final CompileContext compileContext) throws IOException { final String[] commands = createStartupCommand(chunk, compileContext, outputDir); if (LOG.isDebugEnabled()) { diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/FileObject.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/FileObject.java new file mode 100644 index 000000000000..919c724d1640 --- /dev/null +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/FileObject.java @@ -0,0 +1,46 @@ +package com.intellij.compiler.impl.javaCompiler; + +import com.intellij.util.ArrayUtil; +import com.intellij.openapi.util.io.FileUtil; + +import java.io.File; +import java.io.IOException; + +/** + * @author cdr + */ +public class FileObject { + private final File myFile; + private final byte[] myContent; + private final boolean mySaved; + + public FileObject(File file, byte[] content) { + myFile = file; + myContent = content; + mySaved = false; + } + + public FileObject(File file) { + myFile = file; + byte[] fileContent = ArrayUtil.EMPTY_BYTE_ARRAY; + try{ + fileContent = FileUtil.loadFileBytes(file); + } + catch(IOException ignored){ + } + myContent = fileContent; + mySaved = true; + } + + public File getFile() { + return myFile; + } + + public byte[] getContent() { + return myContent; + } + + public boolean isSaved() { + return mySaved; + } +} diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/JavaCompiler.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/JavaCompiler.java index 782b1554aa37..13fc95e0cd8b 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/JavaCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/JavaCompiler.java @@ -18,6 +18,10 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + public class JavaCompiler implements TranslatingCompiler { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.JavaCompiler"); private final Project myProject; @@ -38,19 +42,19 @@ public class JavaCompiler implements TranslatingCompiler { public ExitStatus compile(CompileContext context, VirtualFile[] files) { final BackendCompiler backEndCompiler = getBackEndCompiler(); - final BackendCompilerWrapper wrapper = new BackendCompilerWrapper(myProject, files, (CompileContextEx)context, backEndCompiler); - OutputItem[] outputItems; + final BackendCompilerWrapper wrapper = new BackendCompilerWrapper(myProject, Arrays.asList(files), (CompileContextEx)context, backEndCompiler); + List outputItems; try { outputItems = wrapper.compile(); } catch (CompilerException e) { - outputItems = EMPTY_OUTPUT_ITEM_ARRAY; + outputItems = Collections.emptyList(); context.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1); } catch (CacheCorruptedException e) { LOG.info(e); context.requestRebuildNextTime(e.getMessage()); - outputItems = EMPTY_OUTPUT_ITEM_ARRAY; + outputItems = Collections.emptyList(); } return new ExitStatusImpl(outputItems, wrapper.getFilesToRecompile()); @@ -66,17 +70,16 @@ public class JavaCompiler implements TranslatingCompiler { } private static class ExitStatusImpl implements ExitStatus { - - private final OutputItem[] myOuitputItems; + private final List myOutputItems; private final VirtualFile[] myMyFilesToRecompile; - public ExitStatusImpl(OutputItem[] ouitputItems, VirtualFile[] myFilesToRecompile) { - myOuitputItems = ouitputItems; + private ExitStatusImpl(List outputItems, VirtualFile[] myFilesToRecompile) { + myOutputItems = outputItems; myMyFilesToRecompile = myFilesToRecompile; } public OutputItem[] getSuccessfullyCompiled() { - return myOuitputItems; + return myOutputItems.toArray(new OutputItem[myOutputItems.size()]); } public VirtualFile[] getFilesToRecompile() { diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompAPIDriver.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompAPIDriver.java new file mode 100644 index 000000000000..4a2b644ca426 --- /dev/null +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompAPIDriver.java @@ -0,0 +1,110 @@ +package com.intellij.compiler.impl.javaCompiler.api; + +import com.intellij.compiler.OutputParser; +import com.sun.source.util.JavacTask; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import org.jetbrains.annotations.NotNull; + +import javax.tools.*; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.File; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * @author cdr + */ +@SuppressWarnings({"Since15"}) +public class CompAPIDriver { + private final BlockingQueue myCompilationResults = new LinkedBlockingQueue(); + private static final CompilationEvent GUARD = new CompilationEvent() { + @Override + protected void process(OutputParser.Callback callback) { + } + }; + + public void compile(List commandLine, List paths, final String outputDir) { + JavaCompiler compiler = new com.sun.tools.javac.api.JavacTool(); //use current classloader + StandardJavaFileManager manager = new MyFileManager(this, outputDir); + + Iterable input = manager.getJavaFileObjectsFromFiles(paths); + + DiagnosticListener listener = new DiagnosticListener() { + public void report(Diagnostic diagnostic) { + CompilationEvent event = CompilationEvent.diagnostic(diagnostic); + myCompilationResults.offer(event); + } + }; + try { + PrintWriter silent = new PrintWriter(new OutputStream(){ + public void write(int b) {} + }); + + JavaCompiler.CompilationTask task = compiler.getTask(silent, manager, listener, commandLine, null, input); + ((JavacTask)task).setTaskListener(new TaskListener() { + public void started(TaskEvent taskEvent) { + JavaFileObject sourceFile = taskEvent.getSourceFile(); + CompilationEvent event; + switch (taskEvent.getKind()) { + case ANALYZE: + event = CompilationEvent.progress("Analyzing ",sourceFile); + break; + case PARSE: + event = CompilationEvent.progress("Parsing ", sourceFile); + break; + default: + event = null; + } + if (event != null) { + myCompilationResults.offer(event); + } + } + public void finished(TaskEvent taskEvent) { + CompilationEvent event; + switch (taskEvent.getKind()) { + case ENTER: + event = CompilationEvent.fileProcessed(); + break; + default: + event = null; + } + if (event != null) { + myCompilationResults.offer(event); + } + } + }); + task.call(); + } + finally { + myCompilationResults.offer(GUARD); + } + } + + public boolean processAll(@NotNull OutputParser.Callback callback) { + try { + while (true) { + CompilationEvent event = myCompilationResults.take(); + if (event == GUARD) break; + event.process(callback); + } + } + catch (InterruptedException ignored) { + System.out.println("ignored " + ignored); + } + return false; + } + + public void finish() { + myCompilationResults.clear(); + } + + + public void offer(CompilationEvent compilationEvent) { + myCompilationResults.offer(compilationEvent); + } +} + + diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompilationEvent.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompilationEvent.java new file mode 100644 index 000000000000..4835d32929f8 --- /dev/null +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompilationEvent.java @@ -0,0 +1,70 @@ +package com.intellij.compiler.impl.javaCompiler.api; + +import com.intellij.compiler.OutputParser; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.compiler.CompilerMessageCategory; + +import javax.tools.JavaFileObject; +import javax.tools.Diagnostic; +import java.net.URI; +import java.io.File; + +/** + * @author cdr + */ +@SuppressWarnings({"Since15"}) +abstract class CompilationEvent { + protected abstract void process(OutputParser.Callback callback); + static CompilationEvent progress(final String title, final JavaFileObject fileObject) { + return new CompilationEvent() { + @Override + protected void process(OutputParser.Callback callback) { + showProgressFor(title, fileObject.toUri(), callback); + } + }; + } + + private static void showProgressFor(String title, URI uri, OutputParser.Callback callback) { + callback.setProgressText(title + StringUtil.last(uri.toString(), 70, true)); + } + + static CompilationEvent generateClass(final URI uri, final byte[] bytes) { + return new CompilationEvent() { + @Override + protected void process(OutputParser.Callback callback) { + showProgressFor("Writing ", uri, callback); + File file = new File(uri); + callback.fileGenerated(new com.intellij.compiler.impl.javaCompiler.FileObject(file,bytes)); + } + }; + } + static CompilationEvent diagnostic(final Diagnostic diagnostic) { + return new CompilationEvent() { + @Override + protected void process(OutputParser.Callback callback) { + JavaFileObject fileObject = diagnostic.getSource(); + String message = diagnostic.getMessage(null); + String url = fileObject == null ? null : VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(new File(fileObject.toUri()).getPath())); + + CompilerMessageCategory category = diagnostic.getKind() == Diagnostic.Kind.ERROR + ? CompilerMessageCategory.ERROR + : diagnostic.getKind() == Diagnostic.Kind.WARNING || + diagnostic.getKind() == Diagnostic.Kind.MANDATORY_WARNING + ? CompilerMessageCategory.WARNING + : CompilerMessageCategory.INFORMATION; + callback.message(category, message, url, (int)diagnostic.getLineNumber(), (int)diagnostic.getColumnNumber()); + } + }; + } + + public static CompilationEvent fileProcessed() { + return new CompilationEvent() { + @Override + protected void process(OutputParser.Callback callback) { + callback.fileProcessed(null); + } + }; + } +} diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompilerAPICompiler.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompilerAPICompiler.java new file mode 100644 index 000000000000..26f815531939 --- /dev/null +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/CompilerAPICompiler.java @@ -0,0 +1,182 @@ +package com.intellij.compiler.impl.javaCompiler.api; + +import com.intellij.compiler.OutputParser; +import com.intellij.compiler.impl.CompilerUtil; +import com.intellij.compiler.impl.javaCompiler.BackendCompiler; +import com.intellij.compiler.impl.javaCompiler.DependencyProcessor; +import com.intellij.compiler.impl.javaCompiler.ModuleChunk; +import com.intellij.compiler.impl.javaCompiler.javac.JavacCompiler; +import com.intellij.compiler.impl.javaCompiler.javac.JavacConfigurable; +import com.intellij.compiler.impl.javaCompiler.javac.JavacSettings; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileScope; +import com.intellij.openapi.compiler.CompilerMessageCategory; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.*; + + +public class CompilerAPICompiler implements BackendCompiler { + private final Project myProject; + private int myExitCode; + private static final Set COMPILABLE_TYPES = Collections.singleton(StdFileTypes.JAVA); + private final CompAPIDriver myCompAPIDriver = new CompAPIDriver(); + + public CompilerAPICompiler(Project project) { + myProject = project; + } + + public DependencyProcessor getDependencyProcessor() { + return null; + } + + public boolean checkCompiler(final CompileScope scope) { + final Module[] modules = scope.getAffectedModules(); + final Set checkedJdks = new HashSet(); + for (final Module module : modules) { + final Sdk jdk = ModuleRootManager.getInstance(module).getSdk(); + if (jdk == null) { + continue; + } + checkedJdks.add(jdk); + } + Sdk projectJdk = ProjectRootManager.getInstance(myProject).getProjectJdk(); + if (projectJdk != null) checkedJdks.add(projectJdk); + + for (Sdk sdk : checkedJdks) { + if (!CompilerUtil.isOfVersion(sdk.getVersionString(), "1.6")) { + Messages.showErrorDialog(myProject, "Compiler API supports JDK of version 6 only: "+sdk.getVersionString(), "Incompatible JDK"); + return false; + } + } + return true; + } + + @NotNull + @NonNls + // used for externalization + public String getId() { + return "compAPI"; + } + + @NotNull + public String getPresentableName() { + return "Javac in-process (Java6 only)"; + } + + @NotNull + public Configurable createConfigurable() { + return new JavacConfigurable(JavacSettings.getInstance(myProject)); + } + + @NotNull + public Set getCompilableFileTypes() { + return COMPILABLE_TYPES; + } + + @Nullable + public OutputParser createErrorParser(@NotNull final String outputDir) { + return new OutputParser() { + public boolean processMessageLine(Callback callback) { + return myCompAPIDriver.processAll(callback); + } + }; + } + + @Nullable + public OutputParser createOutputParser(@NotNull final String outputDir) { + return null; + } + + public void compileFinished() { + myCompAPIDriver.finish(); + } + + @NotNull + public Process launchProcess(@NotNull final ModuleChunk chunk, @NotNull final String outputDir, @NotNull final CompileContext compileContext) throws IOException { + final IOException[] ex = {null}; + @NonNls final List commandLine = ApplicationManager.getApplication().runReadAction(new Computable>() { + public List compute() { + try { + List commandLine = new ArrayList(); + JavacSettings javacSettings = JavacSettings.getInstance(myProject); + final List additionalOptions = + JavacCompiler.addAdditionalSettings(commandLine, javacSettings, false, false, false, false, false); + + JavacCompiler.addCommandLineOptions(chunk, commandLine, outputDir, chunk.getJdk(), false,false, null, false, false); + commandLine.addAll(additionalOptions); + return commandLine; + } + catch (IOException e) { + ex[0] = e; + } + return null; + } + }); + if (ex[0] != null) { + throw ex[0]; + } + + return new Process() { + public OutputStream getOutputStream() { + throw new UnsupportedOperationException(); + } + + public InputStream getInputStream() { + return null; + } + + public InputStream getErrorStream() { + return null; + } + + public void destroy() { + } + + public int waitFor() { + try { + //commandLine.remove("-verbose"); + compile(commandLine, chunk, outputDir); + myExitCode = 0; + return myExitCode; + } + catch (Exception e) { + compileContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1); + myExitCode = -1; + return -1; + } + } + + public int exitValue() { + return myExitCode; + } + }; + } + + private void compile(List commandLine, ModuleChunk chunk, String outputDir) { + VirtualFile[] filesToCompile = chunk.getFilesToCompile(); + List paths = new ArrayList(filesToCompile.length); + for (VirtualFile file : filesToCompile) { + paths.add(new File(file.getPresentableUrl())); + } + myCompAPIDriver.compile(commandLine, paths, outputDir); + } +} \ No newline at end of file diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/MyFileManager.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/MyFileManager.java new file mode 100644 index 000000000000..49f221d220e8 --- /dev/null +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/api/MyFileManager.java @@ -0,0 +1,126 @@ +package com.intellij.compiler.impl.javaCompiler.api; + +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.DefaultFileManager; +import gnu.trove.THashMap; + +import javax.tools.FileObject; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** +* @author cdr +*/ +class MyFileManager extends DefaultFileManager { + private final String myOutputDir; + private final CompAPIDriver myCompAPIDriver; + private final Map uri2Output = new THashMap(); + private final Map uri2Source = new THashMap(); + + MyFileManager(CompAPIDriver compAPIDriver, String outputDir) { + super(new Context(), false, null); + myCompAPIDriver = compAPIDriver; + myOutputDir = outputDir; + } + + @Override + public Iterable getJavaFileObjectsFromFiles(Iterable files) { + //return super.getJavaFileObjectsFromFiles(files); + int size = ((Collection)files).size(); + List result = new ArrayList(size); + + for (File file : files) { + result.add(getSource(file)); + } + + return result; + } + + private JavaFileObject getSource(File file) { + URI uri = file.toURI(); + JavaFileObject fileObject = uri2Source.get(uri); + if (fileObject == null) { + fileObject = new JavaFile(file); + uri2Source.put(uri, fileObject); + } + return fileObject; + } + private JavaFileObject getOutput(URI uri) { + JavaFileObject fileObject = uri2Output.get(uri); + if (fileObject == null) { + fileObject = new Output(uri) { + protected void offerClassFile(URI uri, byte[] bytes) { + myCompAPIDriver.offer(CompilationEvent.generateClass(uri, bytes)); + } + }; + uri2Output.put(uri, fileObject); + } + return fileObject; + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject fileObject) { + return getOutput(toURI(myOutputDir,name)); + } + + private static class JavaFile extends SimpleJavaFileObject { + private final File myFile; + + protected JavaFile(File file) { + super(file.toURI(), Kind.SOURCE); + myFile = file; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(myFile); + if (virtualFile == null) return null; + return LoadTextUtil.loadText(virtualFile); + } + + @Override + public String toString() { + return toUri().toString(); + } + } + + private abstract static class Output extends SimpleJavaFileObject { + private Output(URI uri) { + super(uri, Kind.CLASS); + } + + @Override + public ByteArrayOutputStream openOutputStream() { + return new ByteArrayOutputStream() { + @Override + public void close() throws IOException { + super.close(); + offerClassFile(toUri(), toByteArray()); + } + }; + } + + protected abstract void offerClassFile(URI uri, byte[] bytes); + } + + private static URI toURI(String outputDir, String name) { + try { + return new URI("file:///" + outputDir.replace('\\','/') + "/" + name.replace('.', '/') + JavaFileObject.Kind.CLASS.extension); + } + catch (URISyntaxException exception) { + throw new Error(exception); + } + } +} diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerDriver.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerDriver.java index 528bddd55388..e914943852b9 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerDriver.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerDriver.java @@ -19,6 +19,7 @@ package com.intellij.compiler.impl.javaCompiler.eclipse; import com.intellij.compiler.OutputParser; +import com.intellij.compiler.impl.javaCompiler.FileObject; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.CompilerBundle; import com.intellij.openapi.compiler.CompilerMessageCategory; @@ -55,7 +56,7 @@ import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -public class EclipseCompilerDriver implements IEclipseCompilerDriver { +public class EclipseCompilerDriver { private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.javaCompiler.eclipse.EclipseCompilerDriver"); private String[] sourceFilePaths; @@ -183,17 +184,11 @@ public class EclipseCompilerDriver implements IEclipseCompilerDriver { String filePath = String.valueOf(classFile.fileName()); String relativePath = FileUtil.toSystemDependentName(filePath + ".class"); String path = FileUtil.toSystemDependentName(outputDir) + File.separatorChar + relativePath; - - try { - File out = new File(path); - FileUtil.createParentDirs(out); - FileUtil.writeToFile(out, classFile.getBytes()); - } - catch (IOException e) { - LOG.error(e); - } - callback.fileGenerated(path); + byte[] bytes = classFile.getBytes(); + File out = new File(path); + + callback.fileGenerated(new FileObject(out,bytes)); } IProblem[] problems = result.getProblems(); if (problems != null) { diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerOutputParser.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerOutputParser.java index 30ddc51c23d0..a191207cc3eb 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerOutputParser.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseCompilerOutputParser.java @@ -1,11 +1,13 @@ package com.intellij.compiler.impl.javaCompiler.eclipse; import com.intellij.compiler.OutputParser; +import com.intellij.compiler.impl.javaCompiler.FileObject; import com.intellij.openapi.compiler.CompilerBundle; import com.intellij.openapi.compiler.CompilerMessageCategory; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NonNls; +import java.io.File; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -61,7 +63,7 @@ public class EclipseCompilerOutputParser extends OutputParser { String path = matcher.group(1); String absPath = FileUtil.toSystemDependentName(myOutputDir + '/' + path); //callback.setProgressText(CompilerBundle.message("eclipse.compiler.writing", absPath)); - callback.fileGenerated(absPath); + callback.fileGenerated(new FileObject(new File(absPath))); return true; } if (COMPILED_PATTERN.matcher(line).matches() || GENERATED_PATTERN.matcher(line).matches()) { diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompiler.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompiler.java index 57fb4efa2ba7..68a69234f783 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/eclipse/EclipseEmbeddedCompiler.java @@ -30,13 +30,13 @@ public class EclipseEmbeddedCompiler implements BackendCompiler { private final Project myProject; private final EclipseCompiler myEclipseExternalCompiler; private int myExitCode; - private IEclipseCompilerDriver myEclipseCompilerDriver; + private final EclipseCompilerDriver myEclipseCompilerDriver; private static final Set COMPILABLE_TYPES = Collections.singleton(StdFileTypes.JAVA); public EclipseEmbeddedCompiler(Project project) { myProject = project; myEclipseExternalCompiler = new EclipseCompiler(project); - createCompileDriver(); + myEclipseCompilerDriver = new EclipseCompilerDriver(); } public DependencyProcessor getDependencyProcessor() { @@ -49,8 +49,8 @@ public class EclipseEmbeddedCompiler implements BackendCompiler { @NotNull @NonNls - public String getId() // used for externalization - { + public String getId() { + // used for externalization return "EclipseEmbedded"; } @@ -70,7 +70,7 @@ public class EclipseEmbeddedCompiler implements BackendCompiler { } @Nullable - public OutputParser createErrorParser(final String outputDir) { + public OutputParser createErrorParser(@NotNull final String outputDir) { return new OutputParser() { public boolean processMessageLine(Callback callback) { return myEclipseCompilerDriver.processMessageLine(callback, outputDir, myProject); @@ -79,7 +79,7 @@ public class EclipseEmbeddedCompiler implements BackendCompiler { } @Nullable - public OutputParser createOutputParser(final String outputDir) { + public OutputParser createOutputParser(@NotNull final String outputDir) { return null; } @@ -88,9 +88,9 @@ public class EclipseEmbeddedCompiler implements BackendCompiler { @NotNull - public Process launchProcess(final ModuleChunk chunk, final String outputDir, final CompileContext compileContext) throws IOException { + public Process launchProcess(@NotNull final ModuleChunk chunk, @NotNull final String outputDir, @NotNull final CompileContext compileContext) throws IOException { @NonNls final ArrayList commandLine = new ArrayList(); - final IOException[] ex = new IOException[]{null}; + final IOException[] ex = {null}; ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { try { @@ -141,8 +141,4 @@ public class EclipseEmbeddedCompiler implements BackendCompiler { } }; } - - private void createCompileDriver() { - myEclipseCompilerDriver = new EclipseCompilerDriver(); - } } diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/FilePathActionJavac.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/FilePathActionJavac.java index 7e9ac583d0a6..bf5971702b46 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/FilePathActionJavac.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/FilePathActionJavac.java @@ -16,12 +16,14 @@ package com.intellij.compiler.impl.javaCompiler.javac; import com.intellij.compiler.OutputParser; +import com.intellij.compiler.impl.javaCompiler.FileObject; import com.intellij.openapi.compiler.CompilerBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.StdFileTypes; +import java.io.File; import java.util.regex.Matcher; /** @@ -48,8 +50,7 @@ public class FilePathActionJavac extends JavacParserAction { callback.setProgressText(CompilerBundle.message("progress.parsing.file", name)); } else if (StdFileTypes.CLASS.equals(fileType)) { - callback.fileGenerated(filePath); + callback.fileGenerated(new FileObject(new File(filePath))); } } - } diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java index 52b26f90f5c7..e5c305140eb8 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/javac/JavacCompiler.java @@ -21,6 +21,7 @@ import com.intellij.openapi.projectRoots.ex.JavaSdkUtil; import com.intellij.openapi.projectRoots.impl.MockJdkWrapper; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; @@ -112,61 +113,54 @@ public class JavacCompiler extends ExternalCompiler { return new JavacConfigurable(JavacSettings.getInstance(myProject)); } - public OutputParser createErrorParser(final String outputDir) { + public OutputParser createErrorParser(@NotNull final String outputDir) { return new JavacOutputParser(myProject); } - public OutputParser createOutputParser(final String outputDir) { + public OutputParser createOutputParser(@NotNull final String outputDir) { return null; } + private static class MyException extends RuntimeException { + private MyException(Throwable cause) { + super(cause); + } + } + @NotNull public String[] createStartupCommand(final ModuleChunk chunk, final CompileContext context, final String outputPath) throws IOException, IllegalArgumentException { - final ArrayList commandLine = new ArrayList(); - - final Exception[] ex = new Exception[]{null}; - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - try { - createStartupCommand(chunk, commandLine, outputPath); + try { + return ApplicationManager.getApplication().runReadAction(new Computable() { + public String[] compute() { + try { + final List commandLine = new ArrayList(); + createStartupCommand(chunk, commandLine, outputPath, JavacSettings.getInstance(myProject)); + return ArrayUtil.toStringArray(commandLine); + } + catch (IOException e) { + throw new MyException(e); + } } - catch (IllegalArgumentException e) { - ex[0] = e; - } - catch (IOException e) { - ex[0] = e; - } - } - }); - if (ex[0] != null) { - if (ex[0] instanceof IOException) { - throw (IOException)ex[0]; - } - else if (ex[0] instanceof IllegalArgumentException) { - throw (IllegalArgumentException)ex[0]; - } - else { - LOG.error(ex[0]); - } + }); + } + catch (MyException e) { + Throwable cause = e.getCause(); + if (cause instanceof IOException) { + throw (IOException)cause; + } + throw e; } - return ArrayUtil.toStringArray(commandLine); } - private void createStartupCommand(final ModuleChunk chunk, @NonNls final List commandLine, final String outputPath) throws IOException { + private void createStartupCommand(final ModuleChunk chunk, @NonNls final List commandLine, final String outputPath, + JavacSettings javacSettings) throws IOException { final Sdk jdk = getJdkForStartupCommand(chunk); final String versionString = jdk.getVersionString(); if (versionString == null || "".equals(versionString) || !(jdk.getSdkType() instanceof JavaSdkType)) { throw new IllegalArgumentException(CompilerBundle.message("javac.error.unknown.jdk.version", jdk.getName())); } - - final JavaSdkType sdkType = (JavaSdkType)jdk.getSdkType(); - final String toolsJarPath = sdkType.getToolsPath(jdk); - if (toolsJarPath == null) { - throw new IllegalArgumentException(CompilerBundle.message("javac.error.tools.jar.missing", jdk.getName())); - } - final boolean isVersion1_0 = CompilerUtil.isOfVersion(versionString, "1.0"); final boolean isVersion1_1 = CompilerUtil.isOfVersion(versionString, "1.1"); final boolean isVersion1_2 = CompilerUtil.isOfVersion(versionString, "1.2"); @@ -175,11 +169,18 @@ public class JavacCompiler extends ExternalCompiler { final boolean isVersion1_5 = CompilerUtil.isOfVersion(versionString, "1.5") || CompilerUtil.isOfVersion(versionString, "5.0"); final boolean isVersion1_5_or_higher = isVersion1_5 || !(isVersion1_0 || isVersion1_1 || isVersion1_2 || isVersion1_3 || isVersion1_4); - final JavacSettings javacSettings = JavacSettings.getInstance(myProject); + + JavaSdkType sdkType = (JavaSdkType)jdk.getSdkType(); + + final String toolsJarPath = sdkType.getToolsPath(jdk); + if (toolsJarPath == null) { + throw new IllegalArgumentException(CompilerBundle.message("javac.error.tools.jar.missing", jdk.getName())); + } final String vmExePath = sdkType.getVMExecutablePath(jdk); commandLine.add(vmExePath); + if (isVersion1_1 || isVersion1_0) { commandLine.add("-mx" + javacSettings.MAXIMUM_HEAP_SIZE + "m"); } @@ -187,27 +188,8 @@ public class JavacCompiler extends ExternalCompiler { commandLine.add("-Xmx" + javacSettings.MAXIMUM_HEAP_SIZE + "m"); } - final List additionalOptions = new ArrayList(); - StringTokenizer tokenizer = new StringTokenizer(javacSettings.getOptionsString(), " "); - while (tokenizer.hasMoreTokens()) { - @NonNls String token = tokenizer.nextToken(); - if (isVersion1_0) { - if ("-deprecation".equals(token)) { - continue; // not supported for this version - } - } - if (isVersion1_0 || isVersion1_1 || isVersion1_2 || isVersion1_3 || isVersion1_4) { - if ("-Xlint".equals(token)) { - continue; // not supported in these versions - } - } - if (token.startsWith("-J-")) { - commandLine.add(token.substring("-J".length())); - } - else { - additionalOptions.add(token); - } - } + final List additionalOptions = + addAdditionalSettings(commandLine, javacSettings, isVersion1_0, isVersion1_1, isVersion1_2, isVersion1_3, isVersion1_4); CompilerUtil.addLocaleOptions(commandLine, false); @@ -229,40 +211,9 @@ public class JavacCompiler extends ExternalCompiler { commandLine.add(JAVAC_MAIN_CLASS); } - LanguageLevel languageLevel = chunk.getLanguageLevel(); - CompilerUtil.addSourceCommandLineSwitch(jdk, languageLevel, commandLine); + addCommandLineOptions(chunk, commandLine, outputPath, jdk, isVersion1_0, isVersion1_1, myTempFiles, true, true); - commandLine.add("-verbose"); - - final String cp = chunk.getCompilationClasspath(); - final String bootCp = chunk.getCompilationBootClasspath(); - - final String classPath; - if (isVersion1_0 || isVersion1_1) { - classPath = bootCp + File.pathSeparator + cp; - } - else { - classPath = cp; - commandLine.add("-bootclasspath"); - addClassPathValue(jdk, false, commandLine, bootCp, "javac_bootcp"); - } - - commandLine.add("-classpath"); - addClassPathValue(jdk, isVersion1_0, commandLine, classPath, "javac_cp"); - - if (!isVersion1_1 && !isVersion1_0) { - commandLine.add("-sourcepath"); - // this way we tell the compiler that the sourcepath is "empty". However, javac thinks that sourcepath is 'new File("")' - // this may cause problems if we have java code in IDEA working directory - commandLine.add("\"\""); - } - - commandLine.add("-d"); - commandLine.add(outputPath.replace('/', File.separatorChar)); - - for (String option : additionalOptions) { - commandLine.add(option); - } + commandLine.addAll(additionalOptions); final VirtualFile[] files = chunk.getFilesToCompile(); @@ -298,11 +249,80 @@ public class JavacCompiler extends ExternalCompiler { } } - private void addClassPathValue(final Sdk jdk, - final boolean isVersion1_0, - final List commandLine, - final String cpString, - @NonNls final String tempFileName) throws IOException { + public static List addAdditionalSettings(List commandLine, JavacSettings javacSettings, + boolean version1_0, + boolean version1_1, + boolean version1_2, + boolean version1_3, + boolean version1_4) { + final List additionalOptions = new ArrayList(); + StringTokenizer tokenizer = new StringTokenizer(javacSettings.getOptionsString(), " "); + while (tokenizer.hasMoreTokens()) { + @NonNls String token = tokenizer.nextToken(); + if (version1_0) { + if ("-deprecation".equals(token)) { + continue; // not supported for this version + } + } + if (version1_0 || version1_1 || version1_2 || version1_3 || version1_4) { + if ("-Xlint".equals(token)) { + continue; // not supported in these versions + } + } + if (token.startsWith("-J-")) { + commandLine.add(token.substring("-J".length())); + } + else { + additionalOptions.add(token); + } + } + return additionalOptions; + } + + public static void addCommandLineOptions(ModuleChunk chunk, @NonNls List commandLine, String outputPath, Sdk jdk, + boolean version1_0, + boolean version1_1, + List tempFiles, boolean addSourcePath, boolean useTempFile) throws IOException { + + LanguageLevel languageLevel = chunk.getLanguageLevel(); + CompilerUtil.addSourceCommandLineSwitch(jdk, languageLevel, commandLine); + + commandLine.add("-verbose"); + + final String cp = chunk.getCompilationClasspath(); + final String bootCp = chunk.getCompilationBootClasspath(); + + final String classPath; + if (version1_0 || version1_1) { + classPath = bootCp + File.pathSeparator + cp; + } + else { + classPath = cp; + commandLine.add("-bootclasspath"); + addClassPathValue(jdk, false, commandLine, bootCp, "javac_bootcp", tempFiles, useTempFile); + } + + commandLine.add("-classpath"); + addClassPathValue(jdk, version1_0, commandLine, classPath, "javac_cp", tempFiles, useTempFile); + + if (!version1_1 && !version1_0 && addSourcePath) { + commandLine.add("-sourcepath"); + // this way we tell the compiler that the sourcepath is "empty". However, javac thinks that sourcepath is 'new File("")' + // this may cause problems if we have java code in IDEA working directory + commandLine.add("\"\""); + } + + commandLine.add("-d"); + commandLine.add(outputPath.replace('/', File.separatorChar)); + } + + private static void addClassPathValue(final Sdk jdk, final boolean isVersion1_0, final List commandLine, final String cpString, @NonNls final String tempFileName, + List tempFiles, + boolean useTempFile) throws IOException { + if (!useTempFile) { + commandLine.add(cpString); + return; + } // must include output path to classpath, otherwise javac will compile all dependent files no matter were they compiled before or not if (isVersion1_0) { commandLine.add(((JavaSdkType)jdk.getSdkType()).getToolsPath(jdk) + File.pathSeparator + cpString); @@ -310,7 +330,7 @@ public class JavacCompiler extends ExternalCompiler { else { File cpFile = FileUtil.createTempFile(tempFileName, ".tmp"); cpFile.deleteOnExit(); - myTempFiles.add(cpFile); + tempFiles.add(cpFile); final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(cpFile))); try { CompilerIOUtil.writeString(cpString, out); @@ -337,5 +357,6 @@ public class JavacCompiler extends ExternalCompiler { public void compileFinished() { FileUtil.asyncDelete(myTempFiles); + myTempFiles.clear(); } } diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/JikesCompiler.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/JikesCompiler.java index 59db836113e2..d7cf1d4a6e1a 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/JikesCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/JikesCompiler.java @@ -99,12 +99,12 @@ public class JikesCompiler extends ExternalCompiler { return new JikesConfigurable(JikesSettings.getInstance(myProject)); } - public OutputParser createErrorParser(final String outputDir) { + public OutputParser createErrorParser(@NotNull final String outputDir) { return new JikesOutputParser(myProject); } @Nullable - public OutputParser createOutputParser(final String outputDir) { + public OutputParser createOutputParser(@NotNull final String outputDir) { return null; } diff --git a/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/ParserActionJikes.java b/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/ParserActionJikes.java index 8832fa66f926..4c1a5b24246f 100644 --- a/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/ParserActionJikes.java +++ b/compiler/impl/com/intellij/compiler/impl/javaCompiler/jikes/ParserActionJikes.java @@ -22,6 +22,7 @@ import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.text.StringUtil; import com.intellij.compiler.ParserAction; import com.intellij.compiler.OutputParser; +import com.intellij.compiler.impl.javaCompiler.FileObject; import org.jetbrains.annotations.NonNls; import java.io.File; @@ -81,7 +82,7 @@ public class ParserActionJikes extends ParserAction { callback.setProgressText(CompilerBundle.message("progress.parsing.file", name)); } else if (StdFileTypes.CLASS.equals(fileType)) { - callback.fileGenerated(filePath); + callback.fileGenerated(new FileObject(new File(filePath))); } } } diff --git a/compiler/impl/com/intellij/compiler/impl/resourceCompiler/ResourceCompiler.java b/compiler/impl/com/intellij/compiler/impl/resourceCompiler/ResourceCompiler.java index 2a8851b55005..28976eeea8b9 100644 --- a/compiler/impl/com/intellij/compiler/impl/resourceCompiler/ResourceCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/resourceCompiler/ResourceCompiler.java @@ -57,7 +57,7 @@ public class ResourceCompiler implements TranslatingCompiler { return !StdFileTypes.JAVA.equals(FILE_TYPE_MANAGER.getFileTypeByFile(file)) && myConfiguration.isResourceFile(file.getName()); } - public TranslatingCompiler.ExitStatus compile(final CompileContext context, final VirtualFile[] files) { + public ExitStatus compile(final CompileContext context, final VirtualFile[] files) { context.getProgressIndicator().pushState(); context.getProgressIndicator().setText(CompilerBundle.message("progress.copying.resources")); @@ -116,6 +116,7 @@ public class ResourceCompiler implements TranslatingCompiler { break; } context.getProgressIndicator().setFraction(i * 1.0 / copyCommands.size()); + context.getProgressIndicator().setText2("Copying "+command.getToPath()+"..."); try { final MyOutputItem outputItem = command.copy(filesToRefresh); processed.add(outputItem); @@ -128,7 +129,7 @@ public class ResourceCompiler implements TranslatingCompiler { ); } } - if (filesToRefresh.size() > 0) { + if (!filesToRefresh.isEmpty()) { CompilerUtil.refreshIOFiles(filesToRefresh); } @@ -145,7 +146,7 @@ public class ResourceCompiler implements TranslatingCompiler { private final String myToPath; private final VirtualFile mySourceFile; - public CopyCommand(String outputPath, String fromPath, String toPath, VirtualFile sourceFile) { + private CopyCommand(String outputPath, String fromPath, String toPath, VirtualFile sourceFile) { myOutputPath = outputPath; myFromPath = fromPath; myToPath = toPath; @@ -181,7 +182,7 @@ public class ResourceCompiler implements TranslatingCompiler { private final String myTargetPath; private final VirtualFile myFile; - public MyOutputItem(String outputPath, String targetPath, VirtualFile file) { + private MyOutputItem(String outputPath, String targetPath, VirtualFile file) { myOutputPath = outputPath; myTargetPath = targetPath; myFile = file; @@ -202,7 +203,7 @@ public class ResourceCompiler implements TranslatingCompiler { private static class MyExitStatus implements ExitStatus { private final OutputItem[] myItemsArray; - public MyExitStatus(OutputItem[] itemsArray) { + private MyExitStatus(OutputItem[] itemsArray) { myItemsArray = itemsArray; } diff --git a/compiler/impl/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java b/compiler/impl/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java index ef76e8ab0b53..7a431baa3094 100644 --- a/compiler/impl/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java +++ b/compiler/impl/com/intellij/compiler/impl/rmiCompiler/RmicCompiler.java @@ -5,6 +5,7 @@ import com.intellij.compiler.RmicSettings; import com.intellij.compiler.impl.CompilerUtil; import com.intellij.compiler.impl.javaCompiler.CompilerParsingThread; import com.intellij.compiler.impl.javaCompiler.CompilerParsingThreadImpl; +import com.intellij.compiler.impl.javaCompiler.FileObject; import com.intellij.compiler.make.Cache; import com.intellij.compiler.make.CacheCorruptedException; import com.intellij.compiler.make.DependencyCache; @@ -230,8 +231,9 @@ public class RmicCompiler implements ClassPostProcessingCompiler{ final Process process = Runtime.getRuntime().exec(cmdLine); final Set successfullyCompiledItems = new HashSet(); final CompilerParsingThread parsingThread = new CompilerParsingThreadImpl(process, context, outputParser, false, true) { - protected void processCompiledClass(String classFileToProcess) { - final RmicProcessingItem item = pathToItemMap.get(classFileToProcess.replace(File.separatorChar, '/')); + protected void processCompiledClass(FileObject classFileToProcess) { + String key = classFileToProcess.getFile().getPath().replace(File.separatorChar, '/'); + final RmicProcessingItem item = pathToItemMap.get(key); if (item != null) { successfullyCompiledItems.add(item); } @@ -251,9 +253,9 @@ public class RmicCompiler implements ClassPostProcessingCompiler{ try { parsingThreadFuture.get(); } - catch (InterruptedException e) { + catch (InterruptedException ignored) { } - catch (ExecutionException e) { + catch (ExecutionException ignored) { } return successfullyCompiledItems.toArray(new RmicProcessingItem[successfullyCompiledItems.size()]); } @@ -407,7 +409,7 @@ public class RmicCompiler implements ClassPostProcessingCompiler{ private final long mySkelTimestamp; private final long myTieTimestamp; - public RemoteClassValidityState(long remoteClassTimestamp, long stubTimestamp, long skelTimestamp, long tieTimestamp) { + private RemoteClassValidityState(long remoteClassTimestamp, long stubTimestamp, long skelTimestamp, long tieTimestamp) { myRemoteClassTimestamp = remoteClassTimestamp; myStubTimestamp = stubTimestamp; mySkelTimestamp = skelTimestamp; @@ -443,7 +445,7 @@ public class RmicCompiler implements ClassPostProcessingCompiler{ private final File myTie; private boolean myIsRemoteObject = false; - public RmicProcessingItem(Module module, final VirtualFile outputClassFile, File outputDir, String qName) { + private RmicProcessingItem(Module module, final VirtualFile outputClassFile, File outputDir, String qName) { myModule = module; myOutputClassFile = outputClassFile; myOutputDir = outputDir; diff --git a/lib/dev/compilerapi.jar b/lib/dev/compilerapi.jar new file mode 100644 index 000000000000..b5bf1f3d8ed7 Binary files /dev/null and b/lib/dev/compilerapi.jar differ diff --git a/plugins/ant/src/com/intellij/lang/ant/config/execution/OutputParser.java b/plugins/ant/src/com/intellij/lang/ant/config/execution/OutputParser.java index 9ff9652bf248..24cb2c3bc267 100644 --- a/plugins/ant/src/com/intellij/lang/ant/config/execution/OutputParser.java +++ b/plugins/ant/src/com/intellij/lang/ant/config/execution/OutputParser.java @@ -2,6 +2,7 @@ package com.intellij.lang.ant.config.execution; import com.intellij.compiler.impl.javaCompiler.javac.JavacOutputParser; import com.intellij.compiler.impl.javaCompiler.jikes.JikesOutputParser; +import com.intellij.compiler.impl.javaCompiler.FileObject; import com.intellij.execution.process.OSProcessHandler; import com.intellij.lang.ant.AntBundle; import com.intellij.openapi.application.ApplicationManager; @@ -263,7 +264,7 @@ public class OutputParser{ public void fileProcessed(String path) { } - public void fileGenerated(String path) { + public void fileGenerated(FileObject path) { } }; try { diff --git a/resources_eng/src/messages/CompilerBundle.properties b/resources_eng/src/messages/CompilerBundle.properties index bcb35b94022c..abef44e30153 100644 --- a/resources_eng/src/messages/CompilerBundle.properties +++ b/resources_eng/src/messages/CompilerBundle.properties @@ -140,7 +140,7 @@ eclipse.options.group.title=Eclipse Options eclipse.compiler.parsing=Parsing {0} eclipse.compiler.reading=Reading classes eclipse.compiler.analyzing=Analyzing {0} -compiler.eclipse.embedded.name=Eclipse embedded (faster, but requires more/leaks memory) +compiler.eclipse.embedded.name=Eclipse in-process generate.ant.build.title=Generate Ant Build generate.ant.build.progress.message=Generating ant build... diff --git a/ui-designer/impl/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java b/ui-designer/impl/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java index 579eff891813..3650fea2db34 100644 --- a/ui-designer/impl/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java +++ b/ui-designer/impl/com/intellij/uiDesigner/make/Form2ByteCodeCompiler.java @@ -153,7 +153,7 @@ public final class Form2ByteCodeCompiler implements ClassInstrumentingCompiler { } }); - return items.toArray(new FileProcessingCompiler.ProcessingItem[items.size()]); + return items.toArray(new ProcessingItem[items.size()]); } private static boolean belongsToCompileScope(final CompileContext context, final VirtualFile formFile, final String classToBind) { @@ -184,7 +184,7 @@ public final class Form2ByteCodeCompiler implements ClassInstrumentingCompiler { return module2formFiles; } - private HashMap> sortByModules(final FileProcessingCompiler.ProcessingItem[] items) { + private HashMap> sortByModules(final ProcessingItem[] items) { final HashMap> module2formFiles = new HashMap>(); for (ProcessingItem item1 : items) { final MyInstrumentationItem item = (MyInstrumentationItem)item1; @@ -282,7 +282,7 @@ public final class Form2ByteCodeCompiler implements ClassInstrumentingCompiler { final ArrayList list = module2itemsList.get(module); for (final MyInstrumentationItem item : list) { - context.getProgressIndicator().setFraction((double)(++formsProcessed) / ((double)items.length)); + context.getProgressIndicator().setFraction((double)++formsProcessed / (double)items.length); final VirtualFile formFile = item.getFormFile();