From f94d101b78f869c5489817d669544922af5adc41 Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 24 Jun 2026 00:41:38 +0300 Subject: [PATCH 1/2] Refactoring compilation engine. --- .../java/compiler/impl/OutputParser.java | 107 +- .../AnnotationProcessingCompiler.java | 307 +-- .../javaCompiler/BackendCompilerWrapper.java | 2000 ++++++++--------- .../javaCompiler/CompilerParsingThread.java | 397 ++-- .../impl/javaCompiler/JavaCompiler.java | 183 +- .../javac/FilePathActionJavac.java | 93 +- .../javaCompiler/javac/JavacOutputParser.java | 477 ++-- .../JavaBytecodeProcessorCompiler.java | 294 +-- .../impl/javaCompiler/JavaToolMonitor.java | 281 +-- .../AnnotationBasedInstrumentingCompiler.java | 293 +-- .../externalSystem/JavacOutputParser.java | 59 +- 11 files changed, 2144 insertions(+), 2347 deletions(-) diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/OutputParser.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/OutputParser.java index 922b217e45..f75078c9b7 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/OutputParser.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/OutputParser.java @@ -16,56 +16,81 @@ package com.intellij.java.compiler.impl; import com.intellij.java.compiler.impl.javaCompiler.FileObject; +import consulo.compiler.CompileContext; import consulo.compiler.CompilerMessageCategory; +import consulo.localize.LocalizeValue; import consulo.util.lang.StringUtil; import java.util.ArrayList; import java.util.List; public abstract class OutputParser { - protected final List myParserActions = new ArrayList(10); - - public interface Callback { - String getNextLine(); - String getCurrentLine(); - void pushBack(String line); - void setProgressText(String text); - void fileProcessed(String path); - void fileGenerated(FileObject path); - void message(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum); - } - - public boolean processMessageLine(Callback callback) { - final String line = callback.getNextLine(); - if(line == null) { - return false; - } - // common statistics messages (javac & jikes) - for (ParserAction action : myParserActions) { - if (action.execute(line, callback)) { - return true; - } - } - if (StringUtil.startsWithChar(line, '[') && StringUtil.endsWithChar(line, ']')) { - // at this point any meaningful output surrounded with '[' and ']' characters is processed, so - // suppress messages like "[total 4657ms]" or "[search path for source files: []]" - return true; - } - return false; - } + protected final List myParserActions = new ArrayList<>(10); + + public interface Callback { + String getNextLine(); + + String getCurrentLine(); + + void pushBack(String line); + + void setProgressText(LocalizeValue text); + + void fileProcessed(String path); + + void fileGenerated(FileObject path); - protected static void addMessage(Callback callback, CompilerMessageCategory type, String message) { - if(message == null || message.trim().length() == 0) { - return; + default CompileContext.MessageBuilder newInfo(LocalizeValue message) { + return newMessage(CompilerMessageCategory.INFORMATION, message); + } + + default CompileContext.MessageBuilder newInfo(String message) { + return newInfo(LocalizeValue.of(message)); + } + + default CompileContext.MessageBuilder newWarning(LocalizeValue message) { + return newMessage(CompilerMessageCategory.WARNING, message); + } + + default CompileContext.MessageBuilder newWarning(String message) { + return newWarning(LocalizeValue.of(message)); + } + + default CompileContext.MessageBuilder newError(LocalizeValue message) { + return newMessage(CompilerMessageCategory.ERROR, message); + } + + default CompileContext.MessageBuilder newError(String message) { + return newError(LocalizeValue.of(message)); + } + + CompileContext.MessageBuilder newMessage(CompilerMessageCategory category, LocalizeValue message); + + default CompileContext.MessageBuilder newMessage(CompilerMessageCategory category, String message) { + return newMessage(category, LocalizeValue.of(message)); + } } - addMessage(callback, type, message, null, -1, -1); - } - protected static void addMessage(Callback callback, CompilerMessageCategory type, String text, String url, int line, int column){ - callback.message(type, text, url, line, column); - } + public boolean processMessageLine(Callback callback) { + String line = callback.getNextLine(); + if (line == null) { + return false; + } + // common statistics messages (javac & jikes) + for (ParserAction action : myParserActions) { + if (action.execute(line, callback)) { + return true; + } + } + if (StringUtil.startsWithChar(line, '[') && StringUtil.endsWithChar(line, ']')) { + // at this point any meaningful output surrounded with '[' and ']' characters is processed, so + // suppress messages like "[total 4657ms]" or "[search path for source files: []]" + return true; + } + return false; + } - public boolean isTrimLines() { - return true; - } + public boolean isTrimLines() { + return true; + } } \ No newline at end of file diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java index 85950daa69..3c06833c8e 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/AnnotationProcessingCompiler.java @@ -34,6 +34,7 @@ import consulo.compiler.util.ModuleCompilerUtil; import consulo.ide.setting.ShowSettingsUtil; import consulo.java.compiler.impl.javaCompiler.JavaAdditionalOutputDirectoriesProvider; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.module.Module; import consulo.platform.base.localize.CommonLocalize; @@ -53,170 +54,172 @@ @ExtensionImpl(id = "java-annotation-processor", order = "before java-compiler") public class AnnotationProcessingCompiler implements TranslatingCompiler { - private static final Logger LOGGER = Logger.getInstance(AnnotationProcessingCompiler.class); - private final Project myProject; - private final JavaCompilerConfiguration myCompilerConfiguration; - - @Inject - public AnnotationProcessingCompiler(Project project) { - myProject = project; - myCompilerConfiguration = JavaCompilerConfiguration.getInstance(project); - } - - @Override - public String getDescription() { - return CompilerLocalize.annotationProcessingCompilerDescription().get(); - } - - @Override - public boolean isCompilableFile(VirtualFile file, CompileContext context) { - return myCompilerConfiguration.isAnnotationProcessorsEnabled() && file.getFileType() == JavaFileType.INSTANCE - && !isExcludedFromAnnotationProcessing(file, context); - } - - @Override - public void compile(final CompileContext context, final Chunk moduleChunk, final VirtualFile[] files, OutputSink sink) { - if (!myCompilerConfiguration.isAnnotationProcessorsEnabled()) { - return; + private static final Logger LOGGER = Logger.getInstance(AnnotationProcessingCompiler.class); + private final Project myProject; + private final JavaCompilerConfiguration myCompilerConfiguration; + + @Inject + public AnnotationProcessingCompiler(Project project) { + myProject = project; + myCompilerConfiguration = JavaCompilerConfiguration.getInstance(project); } - final LocalFileSystem lfs = LocalFileSystem.getInstance(); - final CompileContextEx _context = new CompileContextExDelegate((CompileContextEx)context) { - @Override - public VirtualFile getModuleOutputDirectory(Module module) { - final String path = JavaAdditionalOutputDirectoriesProvider.getAnnotationProcessorsGenerationPath(module); - return path != null ? lfs.findFileByPath(path) : null; - } - - @Override - public VirtualFile getModuleOutputDirectoryForTests(Module module) { - return getModuleOutputDirectory(module); - } - }; - final JavacCompiler javacCompiler = getBackEndCompiler(); - final boolean processorMode = javacCompiler.setAnnotationProcessorMode(true); - final BackendCompilerWrapper wrapper = - new BackendCompilerWrapper(this, moduleChunk, myProject, Arrays.asList(files), _context, javacCompiler, sink); - wrapper.setForceCompileTestsSeparately(true); - try { - wrapper.compile(new HashMap<>()); - } - catch (CompilerException e) { - _context.addMessage(CompilerMessageCategory.ERROR, ExceptionUtil.getThrowableText(e), null, -1, -1); + + @Override + public String getDescription() { + return CompilerLocalize.annotationProcessingCompilerDescription().get(); } - catch (CacheCorruptedException e) { - LOGGER.info(e); - _context.requestRebuildNextTime(e.getMessage()); + + @Override + @RequiredReadAction + public boolean isCompilableFile(VirtualFile file, CompileContext context) { + return myCompilerConfiguration.isAnnotationProcessorsEnabled() + && file.getFileType() == JavaFileType.INSTANCE + && !isExcludedFromAnnotationProcessing(file, context); } - finally { - javacCompiler.setAnnotationProcessorMode(processorMode); - final Set dirsToRefresh = new HashSet<>(); - Application.get().runReadAction(() -> { - for (Module module : moduleChunk.getNodes()) { - final VirtualFile out = _context.getModuleOutputDirectory(module); - if (out != null) { - dirsToRefresh.add(out); + + @Override + public void compile(final CompileContext context, Chunk moduleChunk, VirtualFile[] files, OutputSink sink) { + if (!myCompilerConfiguration.isAnnotationProcessorsEnabled()) { + return; + } + final LocalFileSystem lfs = LocalFileSystem.getInstance(); + CompileContextEx _context = new CompileContextExDelegate((CompileContextEx) context) { + @Override + public VirtualFile getModuleOutputDirectory(Module module) { + String path = JavaAdditionalOutputDirectoriesProvider.getAnnotationProcessorsGenerationPath(module); + return path != null ? lfs.findFileByPath(path) : null; + } + + @Override + public VirtualFile getModuleOutputDirectoryForTests(Module module) { + return getModuleOutputDirectory(module); + } + }; + JavacCompiler javacCompiler = getBackEndCompiler(); + boolean processorMode = javacCompiler.setAnnotationProcessorMode(true); + BackendCompilerWrapper wrapper = + new BackendCompilerWrapper(this, moduleChunk, myProject, Arrays.asList(files), _context, javacCompiler, sink); + wrapper.setForceCompileTestsSeparately(true); + try { + wrapper.compile(new HashMap<>()); + } + catch (CompilerException e) { + _context.newError(LocalizeValue.of(ExceptionUtil.getThrowableText(e))).add(); + } + catch (CacheCorruptedException e) { + LOGGER.info(e); + _context.requestRebuildNextTime(LocalizeValue.ofNullable(e.getMessage())); + } + finally { + javacCompiler.setAnnotationProcessorMode(processorMode); + Set dirsToRefresh = new HashSet<>(); + Application.get().runReadAction(() -> { + for (Module module : moduleChunk.getNodes()) { + VirtualFile out = _context.getModuleOutputDirectory(module); + if (out != null) { + dirsToRefresh.add(out); + } + } + }); + for (VirtualFile root : dirsToRefresh) { + root.refresh(false, true); + } } - } - }); - for (VirtualFile root : dirsToRefresh) { - root.refresh(false, true); - } } - } - - @Override - public FileType[] getInputFileTypes() { - return new FileType[]{JavaFileType.INSTANCE}; - } - - @Override - public FileType[] getOutputFileTypes() { - return new FileType[]{ - JavaFileType.INSTANCE, - JavaClassFileType.INSTANCE - }; - } - - private boolean isExcludedFromAnnotationProcessing(VirtualFile file, CompileContext context) { - if (!myCompilerConfiguration.isAnnotationProcessorsEnabled()) { - return true; + + @Override + public FileType[] getInputFileTypes() { + return new FileType[]{JavaFileType.INSTANCE}; + } + + @Override + public FileType[] getOutputFileTypes() { + return new FileType[]{ + JavaFileType.INSTANCE, + JavaClassFileType.INSTANCE + }; } - final Module module = context.getModuleByFile(file); - if (module != null) { - if (!myCompilerConfiguration.getAnnotationProcessingConfiguration(module).isEnabled()) { - return true; - } - final String path = JavaAdditionalOutputDirectoriesProvider.getAnnotationProcessorsGenerationPath(module); - final VirtualFile generationDir = path != null ? LocalFileSystem.getInstance().findFileByPath(path) : null; - if (generationDir != null && VirtualFileUtil.isAncestor(generationDir, file, false)) { - return true; - } + + @RequiredReadAction + private boolean isExcludedFromAnnotationProcessing(VirtualFile file, CompileContext context) { + if (!myCompilerConfiguration.isAnnotationProcessorsEnabled()) { + return true; + } + Module module = context.getModuleByFile(file); + if (module != null) { + if (!myCompilerConfiguration.getAnnotationProcessingConfiguration(module).isEnabled()) { + return true; + } + String path = JavaAdditionalOutputDirectoriesProvider.getAnnotationProcessorsGenerationPath(module); + VirtualFile generationDir = path != null ? LocalFileSystem.getInstance().findFileByPath(path) : null; + if (generationDir != null && VirtualFileUtil.isAncestor(generationDir, file, false)) { + return true; + } + } + return CompilerManager.getInstance(myProject).isExcludedFromCompilation(file); } - return CompilerManager.getInstance(myProject).isExcludedFromCompilation(file); - } - - @Override - @RequiredUIAccess - @RequiredReadAction - public boolean validateConfiguration(CompileScope scope) { - final List> chunks = ModuleCompilerUtil.getSortedModuleChunks(myProject, Arrays.asList(scope.getAffectedModules())); - for (final Chunk chunk : chunks) { - final Set chunkModules = chunk.getNodes(); - if (chunkModules.size() <= 1) { - continue; // no need to check one-module chunks - } - for (Module chunkModule : chunkModules) { - if (myCompilerConfiguration.getAnnotationProcessingConfiguration(chunkModule).isEnabled()) { - showCyclesNotSupportedForAnnotationProcessors(chunkModules.toArray(new Module[chunkModules.size()])); - return false; + + @Override + @RequiredUIAccess + public boolean validateConfiguration(CompileScope scope) { + List> chunks = ModuleCompilerUtil.getSortedModuleChunks(myProject, Arrays.asList(scope.getAffectedModules())); + for (Chunk chunk : chunks) { + Set chunkModules = chunk.getNodes(); + if (chunkModules.size() <= 1) { + continue; // no need to check one-module chunks + } + for (Module chunkModule : chunkModules) { + if (myCompilerConfiguration.getAnnotationProcessingConfiguration(chunkModule).isEnabled()) { + showCyclesNotSupportedForAnnotationProcessors(chunkModules.toArray(new Module[chunkModules.size()])); + return false; + } + } + } + + JavacCompiler compiler = getBackEndCompiler(); + boolean previousValue = compiler.setAnnotationProcessorMode(true); + try { + return compiler.checkCompiler(scope); + } + finally { + compiler.setAnnotationProcessorMode(previousValue); } - } } - final JavacCompiler compiler = getBackEndCompiler(); - final boolean previousValue = compiler.setAnnotationProcessorMode(true); - try { - return compiler.checkCompiler(scope); + @RequiredUIAccess + private void showCyclesNotSupportedForAnnotationProcessors(Module[] modulesInChunk) { + LOGGER.assertTrue(modulesInChunk.length > 0); + String moduleNameToSelect = modulesInChunk[0].getName(); + String moduleNames = getModulesString(modulesInChunk); + Messages.showMessageDialog( + myProject, + CompilerLocalize.errorAnnotationProcessingNotSupportedForModuleCycles(moduleNames).get(), + CommonLocalize.titleError().get(), + UIUtil.getErrorIcon() + ); + showConfigurationDialog(moduleNameToSelect, null); } - finally { - compiler.setAnnotationProcessorMode(previousValue); + + @RequiredUIAccess + private void showConfigurationDialog(String moduleNameToSelect, String tabNameToSelect) { + ShowSettingsUtil.getInstance().showProjectStructureDialog( + myProject, + projectStructureSelector -> projectStructureSelector.select(moduleNameToSelect, tabNameToSelect, true) + ); } - } - - @RequiredUIAccess - private void showCyclesNotSupportedForAnnotationProcessors(Module[] modulesInChunk) { - LOGGER.assertTrue(modulesInChunk.length > 0); - String moduleNameToSelect = modulesInChunk[0].getName(); - final String moduleNames = getModulesString(modulesInChunk); - Messages.showMessageDialog( - myProject, - CompilerLocalize.errorAnnotationProcessingNotSupportedForModuleCycles(moduleNames).get(), - CommonLocalize.titleError().get(), - UIUtil.getErrorIcon() - ); - showConfigurationDialog(moduleNameToSelect, null); - } - - @RequiredUIAccess - private void showConfigurationDialog(String moduleNameToSelect, String tabNameToSelect) { - ShowSettingsUtil.getInstance().showProjectStructureDialog( - myProject, - projectStructureSelector -> projectStructureSelector.select(moduleNameToSelect, tabNameToSelect, true) - ); - } - - private static String getModulesString(Module[] modulesInChunk) { - final StringBuilder moduleNames = new StringBuilder(); - for (Module module : modulesInChunk) { - if (moduleNames.length() > 0) { - moduleNames.append("\n"); - } - moduleNames.append("\"").append(module.getName()).append("\""); + + private static String getModulesString(Module[] modulesInChunk) { + StringBuilder moduleNames = new StringBuilder(); + for (Module module : modulesInChunk) { + if (moduleNames.length() > 0) { + moduleNames.append("\n"); + } + moduleNames.append("\"").append(module.getName()).append("\""); + } + return moduleNames.toString(); } - return moduleNames.toString(); - } - private JavacCompiler getBackEndCompiler() { - return (JavacCompiler)myCompilerConfiguration.findCompiler(JavaCompilerConfiguration.DEFAULT_COMPILER); - } + private JavacCompiler getBackEndCompiler() { + return (JavacCompiler) myCompilerConfiguration.findCompiler(JavaCompilerConfiguration.DEFAULT_COMPILER); + } } diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/BackendCompilerWrapper.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/BackendCompilerWrapper.java index 7ffa68d181..e7477bde00 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/BackendCompilerWrapper.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/BackendCompilerWrapper.java @@ -23,11 +23,11 @@ import com.intellij.java.language.psi.JavaPsiFacade; import com.intellij.java.language.psi.PsiClass; import com.intellij.java.language.util.cls.ClsFormatException; -import consulo.application.AccessRule; +import consulo.annotation.access.RequiredWriteAction; import consulo.application.Application; +import consulo.application.ReadAction; import consulo.application.util.AsyncFileService; import consulo.application.util.concurrent.AppExecutorUtil; -import consulo.application.util.function.Computable; import consulo.compiler.*; import consulo.compiler.localize.CompilerLocalize; import consulo.compiler.util.CompilerUtil; @@ -48,6 +48,7 @@ import consulo.process.ProcessHandler; import consulo.process.cmd.GeneralCommandLine; import consulo.project.Project; +import consulo.ui.annotation.RequiredUIAccess; import consulo.util.collection.Chunk; import consulo.util.dataholder.Key; import consulo.util.io.FilePermissionCopier; @@ -55,7 +56,7 @@ import consulo.util.lang.Comparing; import consulo.util.lang.Couple; import consulo.util.lang.StringUtil; -import consulo.util.lang.ref.Ref; +import consulo.util.lang.ref.SimpleReference; import consulo.virtualFileSystem.LocalFileSystem; import consulo.virtualFileSystem.VirtualFile; import consulo.virtualFileSystem.fileType.FileType; @@ -69,1099 +70,922 @@ import java.nio.file.Files; import java.util.*; import java.util.concurrent.*; +import java.util.function.Supplier; /** * @author Eugene Zhuravlev * @since Jan 24, 2003 */ -public class BackendCompilerWrapper -{ - public static final Key CLASS_PARSING_HANDLER_KEY = Key.create(ClassParsingHandler.class.getName()); - - private static final Logger LOG = Logger.getInstance(BackendCompilerWrapper.class); - - private final BackendCompiler myCompiler; - - private final CompileContextEx myCompileContext; - private final List myFilesToCompile; - private final TranslatingCompiler.OutputSink mySink; - private final TranslatingCompiler myTranslatingCompiler; - private final Chunk myChunk; - private final Project myProject; - private final Map myModuleToTempDirMap = new HashMap<>(); - private final ProjectFileIndex myProjectFileIndex; - private static final String PACKAGE_ANNOTATION_FILE_NAME = "package-info.java"; - private static final FileObject ourStopThreadToken = new FileObject(new File(""), new byte[0]); - public final Map> myFileNameToSourceMap = new HashMap<>(); - private final Set myProcessedPackageInfos = new HashSet<>(); - private final CompileStatistics myStatistics; - private volatile String myModuleName = null; - private boolean myForceCompileTestsSeparately = false; - - public BackendCompilerWrapper( - TranslatingCompiler translatingCompiler, - Chunk chunk, - final Project project, - List filesToCompile, - CompileContextEx compileContext, - BackendCompiler compiler, - TranslatingCompiler.OutputSink sink - ) - { - myTranslatingCompiler = translatingCompiler; - myChunk = chunk; - myProject = project; - myCompiler = compiler; - myCompileContext = compileContext; - myFilesToCompile = filesToCompile; - mySink = sink; - myProjectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); - CompileStatistics stat = compileContext.getUserData(CompileStatistics.KEY); - if (stat == null) - { - stat = new CompileStatistics(); - compileContext.putUserData(CompileStatistics.KEY, stat); - } - myStatistics = stat; - } - - public void compile(Map parsingInfo) throws CompilerException, CacheCorruptedException - { - Application application = myProject.getApplication(); - try - { - if (!myFilesToCompile.isEmpty()) - { - if (application.isUnitTestMode()) - { - saveTestData(); - } - compileModules(buildModuleToFilesMap(myFilesToCompile), parsingInfo); - } - } - catch (SecurityException e) - { - throw new CompilerException(CompilerLocalize.errorCompilerProcessNotStarted(e.getMessage()).get(), e); - } - catch (IllegalArgumentException e) - { - throw new CompilerException(e.getMessage(), e); - } - finally - { - for (final VirtualFile file : myModuleToTempDirMap.values()) - { - if (file != null) - { - final File ioFile = new File(file.getPath()); - getAsyncFileService().asyncDelete(ioFile); - } - } - myModuleToTempDirMap.clear(); - } - - if (!myFilesToCompile.isEmpty() && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0) - { - // package-info.java hack - final List outputs = new ArrayList<>(); - myProject.getApplication().runReadAction(() -> { - for (final VirtualFile file : myFilesToCompile) - { - if (PACKAGE_ANNOTATION_FILE_NAME.equals(file.getName()) && !myProcessedPackageInfos.contains(file)) - { - outputs.add(new OutputItemImpl(file)); - } - } - }); - if (!outputs.isEmpty()) - { - mySink.add(null, outputs, VirtualFile.EMPTY_ARRAY); - } - } - } - - public boolean isForceCompileTestsSeparately() - { - return myForceCompileTestsSeparately; - } - - public void setForceCompileTestsSeparately(boolean forceCompileTestsSeparately) - { - myForceCompileTestsSeparately = forceCompileTestsSeparately; - } - - private Map> buildModuleToFilesMap(final List filesToCompile) - { - if (myChunk.getNodes().size() == 1) - { - return Collections.singletonMap(myChunk.getNodes().iterator().next(), Collections.unmodifiableList(filesToCompile)); - } - return CompilerUtil.buildModuleToFilesMap(myCompileContext, filesToCompile); - } - - private AsyncFileService getAsyncFileService() - { - return Application.get().getInstance(AsyncFileService.class); - } - - private void compileModules(final Map> moduleToFilesMap, Map parsingInfo) throws CompilerException - { - try - { - compileChunk(new ModuleChunk(myCompileContext, myChunk, moduleToFilesMap), parsingInfo); - } - catch (IOException e) - { - throw new CompilerException(e.getMessage(), e); - } - } - - private void compileChunk(ModuleChunk chunk, Map parsingInfo) throws IOException - { - final String chunkPresentableName = getPresentableNameFor(chunk); - myModuleName = chunkPresentableName; - - // validate encodings - if (chunk.getModuleCount() > 1) - { - validateEncoding(chunk, chunkPresentableName); - // todo: validation for bytecode target? - } - - runTransformingCompilers(chunk); - - - final List outs = new ArrayList<>(); - File fileToDelete = getOutputDirsToCompileTo(chunk, outs); - - try - { - for (final OutputDir outputDir : outs) - { - chunk.setSourcesFilter(outputDir.getKind()); - - doCompile(chunk, outputDir.getPath(), parsingInfo); - } - } - finally - { - if (fileToDelete != null) - { - getAsyncFileService().asyncDelete(fileToDelete); - } - } - } - - private void validateEncoding(ModuleChunk chunk, String chunkPresentableName) - { - final CompilerEncodingService es = CompilerEncodingService.getInstance(myProject); - Charset charset = null; - for (Module module : chunk.getModules()) - { - final Charset moduleCharset = es.getPreferredModuleEncoding(module); - if (charset == null) - { - charset = moduleCharset; - } - else - { - if (!Comparing.equal(charset, moduleCharset)) - { - // warn user - final Charset chunkEncoding = CompilerEncodingService.getPreferredModuleEncoding(chunk); - final StringBuilder message = new StringBuilder(); - message.append("Modules in chunk ["); - message.append(chunkPresentableName); - message.append("] configured to use different encodings.\n"); - if (chunkEncoding != null) - { - message.append("\"").append(chunkEncoding.name()).append("\" encoding will be used to compile the chunk"); - } - else - { - message.append("Default compiler encoding will be used to compile the chunk"); - } - myCompileContext.addMessage(CompilerMessageCategory.INFORMATION, message.toString(), null, -1, -1); - break; - } - } - } - } - - - private static String getPresentableNameFor(final ModuleChunk chunk) - { - return Application.get().runReadAction((Computable)() -> { - final Module[] modules = chunk.getModules(); - StringBuilder moduleName = new StringBuilder(Math.min(128, modules.length * 8)); - for (int idx = 0; idx < modules.length; idx++) - { - final Module module = modules[idx]; - if (idx > 0) - { - moduleName.append(", "); - } - moduleName.append(module.getName()); - if (moduleName.length() > 128 && idx + 1 < modules.length /*name is already too long and seems to grow longer*/) - { - moduleName.append("..."); - break; - } - } - return moduleName.toString(); - }); - } - - @Nullable - private File getOutputDirsToCompileTo(ModuleChunk chunk, final List dirs) throws IOException - { - File fileToDelete = null; - if (chunk.getModuleCount() == 1) - { // optimization - final Module module = chunk.getModules()[0]; - myProject.getApplication().runReadAction(() -> { - final String sourcesOutputDir = getOutputDir(module); - if (shouldCompileTestsSeparately(module)) - { - if (sourcesOutputDir != null) - { - dirs.add(new OutputDir(sourcesOutputDir, ModuleChunk.SOURCES)); - } - final String testsOutputDir = getTestsOutputDir(module); - if (testsOutputDir == null) - { - LOG.error("Tests output dir is null for module \"" + module.getName() + "\""); - } - else - { - dirs.add(new OutputDir(testsOutputDir, ModuleChunk.TEST_SOURCES)); - } - } - else - { // both sources and test sources go into the same output - if (sourcesOutputDir == null) - { - LOG.error("Sources output dir is null for module \"" + module.getName() + "\""); - } - else - { - dirs.add(new OutputDir(sourcesOutputDir, ModuleChunk.ALL_SOURCES)); - } - } - }); - } - else - { // chunk has several modules - final File outputDir = Files.createTempDirectory("compileOutput").toFile(); - fileToDelete = outputDir; - dirs.add(new OutputDir(outputDir.getPath(), ModuleChunk.ALL_SOURCES)); - } - return fileToDelete; - } - - - private boolean shouldCompileTestsSeparately(Module module) - { - if (myForceCompileTestsSeparately) - { - return true; - } - final String moduleTestOutputDirectory = getTestsOutputDir(module); - if (moduleTestOutputDirectory == null) - { - return false; - } - // here we have test output specified - final String moduleOutputDirectory = getOutputDir(module); - if (moduleOutputDirectory == null) - { - // only test output is specified, so should return true - return true; - } - return !FileUtil.pathsEqual(moduleTestOutputDirectory, moduleOutputDirectory); - } - - private void saveTestData() - { - /*ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { +public class BackendCompilerWrapper { + public static final Key CLASS_PARSING_HANDLER_KEY = Key.create(ClassParsingHandler.class.getName()); + + private static final Logger LOG = Logger.getInstance(BackendCompilerWrapper.class); + + private final BackendCompiler myCompiler; + + private final CompileContextEx myCompileContext; + private final List myFilesToCompile; + private final TranslatingCompiler.OutputSink mySink; + private final TranslatingCompiler myTranslatingCompiler; + private final Chunk myChunk; + private final Project myProject; + private final Map myModuleToTempDirMap = new HashMap<>(); + private final ProjectFileIndex myProjectFileIndex; + private static final String PACKAGE_ANNOTATION_FILE_NAME = "package-info.java"; + private static final FileObject ourStopThreadToken = new FileObject(new File(""), new byte[0]); + public final Map> myFileNameToSourceMap = new HashMap<>(); + private final Set myProcessedPackageInfos = new HashSet<>(); + private final CompileStatistics myStatistics; + private volatile String myModuleName = null; + private boolean myForceCompileTestsSeparately = false; + + public BackendCompilerWrapper( + TranslatingCompiler translatingCompiler, + Chunk chunk, + Project project, + List filesToCompile, + CompileContextEx compileContext, + BackendCompiler compiler, + TranslatingCompiler.OutputSink sink + ) { + myTranslatingCompiler = translatingCompiler; + myChunk = chunk; + myProject = project; + myCompiler = compiler; + myCompileContext = compileContext; + myFilesToCompile = filesToCompile; + mySink = sink; + myProjectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + CompileStatistics stat = compileContext.getUserData(CompileStatistics.KEY); + if (stat == null) { + stat = new CompileStatistics(); + compileContext.putUserData(CompileStatistics.KEY, stat); + } + myStatistics = stat; + } + + public void compile(Map parsingInfo) throws CompilerException, CacheCorruptedException { + Application application = myProject.getApplication(); + try { + if (!myFilesToCompile.isEmpty()) { + if (application.isUnitTestMode()) { + saveTestData(); + } + compileModules(buildModuleToFilesMap(myFilesToCompile), parsingInfo); + } + } + catch (SecurityException e) { + throw new CompilerException(CompilerLocalize.errorCompilerProcessNotStarted(e.getMessage()).get(), e); + } + catch (IllegalArgumentException e) { + throw new CompilerException(e.getMessage(), e); + } + finally { + for (VirtualFile file : myModuleToTempDirMap.values()) { + if (file != null) { + File ioFile = new File(file.getPath()); + getAsyncFileService().asyncDelete(ioFile); + } + } + myModuleToTempDirMap.clear(); + } + + if (!myFilesToCompile.isEmpty() && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0) { + // package-info.java hack + List outputs = new ArrayList<>(); + myProject.getApplication().runReadAction(() -> { + for (VirtualFile file : myFilesToCompile) { + if (PACKAGE_ANNOTATION_FILE_NAME.equals(file.getName()) && !myProcessedPackageInfos.contains(file)) { + outputs.add(new OutputItemImpl(file)); + } + } + }); + if (!outputs.isEmpty()) { + mySink.add(null, outputs, VirtualFile.EMPTY_ARRAY); + } + } + } + + public boolean isForceCompileTestsSeparately() { + return myForceCompileTestsSeparately; + } + + public void setForceCompileTestsSeparately(boolean forceCompileTestsSeparately) { + myForceCompileTestsSeparately = forceCompileTestsSeparately; + } + + private Map> buildModuleToFilesMap(List filesToCompile) { + if (myChunk.getNodes().size() == 1) { + return Collections.singletonMap(myChunk.getNodes().iterator().next(), Collections.unmodifiableList(filesToCompile)); + } + return CompilerUtil.buildModuleToFilesMap(myCompileContext, filesToCompile); + } + + private AsyncFileService getAsyncFileService() { + return Application.get().getInstance(AsyncFileService.class); + } + + @RequiredUIAccess + private void compileModules(Map> moduleToFilesMap, Map parsingInfo) + throws CompilerException { + try { + compileChunk(new ModuleChunk(myCompileContext, myChunk, moduleToFilesMap), parsingInfo); + } + catch (IOException e) { + throw new CompilerException(e.getMessage(), e); + } + } + + @RequiredUIAccess + private void compileChunk(ModuleChunk chunk, Map parsingInfo) throws IOException { + String chunkPresentableName = getPresentableNameFor(chunk); + myModuleName = chunkPresentableName; + + // validate encodings + if (chunk.getModuleCount() > 1) { + validateEncoding(chunk, chunkPresentableName); + // todo: validation for bytecode target? + } + + runTransformingCompilers(chunk); + + List outs = new ArrayList<>(); + File fileToDelete = getOutputDirsToCompileTo(chunk, outs); + + try { + for (OutputDir outputDir : outs) { + chunk.setSourcesFilter(outputDir.getKind()); + + doCompile(chunk, outputDir.getPath(), parsingInfo); + } + } + finally { + if (fileToDelete != null) { + getAsyncFileService().asyncDelete(fileToDelete); + } + } + } + + private void validateEncoding(ModuleChunk chunk, String chunkPresentableName) { + CompilerEncodingService es = CompilerEncodingService.getInstance(myProject); + Charset charset = null; + for (Module module : chunk.getModules()) { + Charset moduleCharset = es.getPreferredModuleEncoding(module); + if (charset == null) { + charset = moduleCharset; + } + else { + if (!Comparing.equal(charset, moduleCharset)) { + // warn user + Charset chunkEncoding = CompilerEncodingService.getPreferredModuleEncoding(chunk); + StringBuilder message = new StringBuilder(); + message.append("Modules in chunk ["); + message.append(chunkPresentableName); + message.append("] configured to use different encodings.\n"); + if (chunkEncoding != null) { + message.append("\"").append(chunkEncoding.name()).append("\" encoding will be used to compile the chunk"); + } + else { + message.append("Default compiler encoding will be used to compile the chunk"); + } + myCompileContext.newInfo(LocalizeValue.ofNullable(message.toString())).add(); + break; + } + } + } + } + + private static String getPresentableNameFor(ModuleChunk chunk) { + return Application.get().runReadAction((Supplier) () -> { + Module[] modules = chunk.getModules(); + StringBuilder moduleName = new StringBuilder(Math.min(128, modules.length * 8)); + for (int idx = 0; idx < modules.length; idx++) { + Module module = modules[idx]; + if (idx > 0) { + moduleName.append(", "); + } + moduleName.append(module.getName()); + if (moduleName.length() > 128 && idx + 1 < modules.length /*name is already too long and seems to grow longer*/) { + moduleName.append("..."); + break; + } + } + return moduleName.toString(); + }); + } + + @Nullable + private File getOutputDirsToCompileTo(ModuleChunk chunk, List dirs) throws IOException { + File fileToDelete = null; + if (chunk.getModuleCount() == 1) { // optimization + Module module = chunk.getModules()[0]; + myProject.getApplication().runReadAction(() -> { + String sourcesOutputDir = getOutputDir(module); + if (shouldCompileTestsSeparately(module)) { + if (sourcesOutputDir != null) { + dirs.add(new OutputDir(sourcesOutputDir, ModuleChunk.SOURCES)); + } + String testsOutputDir = getTestsOutputDir(module); + if (testsOutputDir == null) { + LOG.error("Tests output dir is null for module \"" + module.getName() + "\""); + } + else { + dirs.add(new OutputDir(testsOutputDir, ModuleChunk.TEST_SOURCES)); + } + } + else { // both sources and test sources go into the same output + if (sourcesOutputDir == null) { + LOG.error("Sources output dir is null for module \"" + module.getName() + "\""); + } + else { + dirs.add(new OutputDir(sourcesOutputDir, ModuleChunk.ALL_SOURCES)); + } + } + }); + } + else { // chunk has several modules + File outputDir = Files.createTempDirectory("compileOutput").toFile(); + fileToDelete = outputDir; + dirs.add(new OutputDir(outputDir.getPath(), ModuleChunk.ALL_SOURCES)); + } + return fileToDelete; + } + + + private boolean shouldCompileTestsSeparately(Module module) { + if (myForceCompileTestsSeparately) { + return true; + } + String moduleTestOutputDirectory = getTestsOutputDir(module); + if (moduleTestOutputDirectory == null) { + return false; + } + // here we have test output specified + String moduleOutputDirectory = getOutputDir(module); + if (moduleOutputDirectory == null) { + // only test output is specified, so should return true + return true; + } + return !FileUtil.pathsEqual(moduleTestOutputDirectory, moduleOutputDirectory); + } + + private void saveTestData() { + /*ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { for (VirtualFile file : myFilesToCompile) { CompilerManagerImpl.addCompiledPath(file.getPath()); } } }); */ - } - - private class CompilerParsingHandler extends CompilerParsingThread - { - private final ClassParsingHandler myClassParsingThread; - - private CompilerParsingHandler(ProcessHandler processHandler, - CompileContext context, - OutputParser outputParser, - ClassParsingHandler classParsingThread, - boolean readErrorStream, - boolean trimLines) - { - super(processHandler, outputParser, readErrorStream, trimLines, context); - myClassParsingThread = classParsingThread; - } - - @Override - protected void processCompiledClass(final FileObject classFileToProcess) throws CacheCorruptedException - { - myClassParsingThread.addPath(classFileToProcess); - } - } - - private void doCompile(final ModuleChunk chunk, String outputDir, Map parsingInfo) throws IOException - { - myCompileContext.getProgressIndicator().checkCanceled(); - - if (AccessRule.read(() -> chunk.getFilesToCompile().isEmpty())) - { - return; // should not invoke javac with empty sources list - } - - BackendCompilerProcessBuilder processBuilder = null; - int exitValue = 0; - try - { - ClassParsingHandler classParsingHandler = new ClassParsingHandler(parsingInfo); - - myCompileContext.putUserData(CLASS_PARSING_HANDLER_KEY, classParsingHandler); - - processBuilder = myCompiler.prepareProcess(chunk, outputDir, myCompileContext); - - BackendCompilerMonitor monitor = myCompiler.createMonitor(processBuilder); - - GeneralCommandLine commandLine = AccessRule.read(processBuilder::buildCommandLine); - - assert commandLine != null; - - ProcessHandler process; - try - { - process = processBuilder.createProcess(commandLine); - if (monitor != null) - { - monitor.handleProcessStart(process); - } - } - catch (ExecutionException e) - { - if (monitor != null) - { - monitor.disposeWithTree(); - } - throw new IOException(e); - } - - ExecutorService executorService = AppExecutorUtil.getAppExecutorService(); - Future classParsingFuture = executorService.submit(classParsingHandler); - - OutputParser errorParser = myCompiler.createErrorParser(processBuilder, outputDir, process); - CompilerParsingThread errorParsingThread = errorParser == null ? null - : new CompilerParsingHandler(process, myCompileContext, errorParser, classParsingHandler, true, errorParser.isTrimLines()); - - Future errorParsingFuture = CompletableFuture.completedFuture(null); - if (errorParsingThread != null) - { - errorParsingFuture = executorService.submit(errorParsingThread); - } - - Future outputParsingFuture = CompletableFuture.completedFuture(null); - OutputParser outputParser = myCompiler.createOutputParser(processBuilder, outputDir); - CompilerParsingThread outputParsingHandler = outputParser == null ? null - : new CompilerParsingHandler(process, myCompileContext, outputParser, classParsingHandler, false, outputParser.isTrimLines()); - if (outputParsingHandler != null) - { - outputParsingFuture = executorService.submit(outputParsingHandler); - } - - try - { - process.startNotify(); - - process.waitFor(); - } - catch (Throwable e) - { - if (monitor != null) - { - monitor.disposeWithTree(); - } - - throw new IOException(e); - } - finally - { - if (errorParsingThread != null) - { - errorParsingThread.stopParsing(); - } - - if (outputParsingHandler != null) - { - outputParsingHandler.stopParsing(); - } - classParsingHandler.stopParsing(); - - if (monitor != null) - { - monitor.disposeWithTree(); - } - - waitABit(classParsingFuture); - waitABit(errorParsingFuture); - waitABit(outputParsingFuture); - - registerParsingException(outputParsingHandler); - registerParsingException(errorParsingThread); - - assert outputParsingHandler == null || !outputParsingHandler.processing; - assert errorParsingThread == null || !errorParsingThread.processing; - assert classParsingHandler == null || !classParsingHandler.processing; - } - } - finally - { - if (processBuilder != null) - { - processBuilder.clearTempFiles(); - } - compileFinished(exitValue, chunk, outputDir); - myModuleName = null; - } - } - - private static void waitABit(final Future threadFuture) - { - if (threadFuture != null) - { - try - { - threadFuture.get(); - } - catch (InterruptedException | java.util.concurrent.ExecutionException ignored) - { - LOG.info("Thread interrupted", ignored); - } - } - } - - private void registerParsingException(final CompilerParsingThread outputParsingThread) - { - Throwable error = outputParsingThread == null ? null : outputParsingThread.getError(); - if (error != null) - { - String message = error.getMessage(); - if (error instanceof CacheCorruptedException) - { - myCompileContext.requestRebuildNextTime(message); - } - else - { - myCompileContext.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1); - } - } - } - - private void runTransformingCompilers(final ModuleChunk chunk) - { - final JavaSourceTransformingCompiler[] transformers = - CompilerManager.getInstance(myProject).getCompilers(JavaSourceTransformingCompiler.class); - if (transformers.length == 0) - { - return; - } - if (LOG.isDebugEnabled()) - { - LOG.debug("Running transforming compilers..."); - } - final Module[] modules = chunk.getModules(); - for (final JavaSourceTransformingCompiler transformer : transformers) - { - final Map originalToCopyFileMap = new HashMap<>(); - final Application application = Application.get(); - application.invokeAndWait( - () -> { - for (final Module module : modules) - { - for (final VirtualFile file : chunk.getFilesToCompile(module)) - { - final VirtualFile untransformed = chunk.getOriginalFile(file); - if (transformer.isTransformable(untransformed)) - { - application.runWriteAction(() -> { - try - { - // if untransformed != file, the file is already a (possibly transformed) copy of the original - // 'untransformed' file. - // If this is the case, just use already created copy and do not copy file content once again - final VirtualFile fileCopy = untransformed.equals(file) ? createFileCopy(getTempDir(module), file) : file; - originalToCopyFileMap.put(file, fileCopy); - } - catch (IOException e) - { - // skip it - } - }); - } - } - } - }, - myCompileContext.getProgressIndicator().getModalityState() - ); - - // do actual transform - for (final Module module : modules) - { - final List filesToCompile = chunk.getFilesToCompile(module); - for (int j = 0; j < filesToCompile.size(); j++) - { - final VirtualFile file = filesToCompile.get(j); - final VirtualFile fileCopy = originalToCopyFileMap.get(file); - if (fileCopy != null) - { - final boolean ok = transformer.transform(myCompileContext, fileCopy, chunk.getOriginalFile(file)); - if (ok) - { - chunk.substituteWithTransformedVersion(module, j, fileCopy); - } - } - } - } - } - } - - private VirtualFile createFileCopy(VirtualFile tempDir, final VirtualFile file) throws IOException - { - final String fileName = file.getName(); - if (tempDir.findChild(fileName) != null) - { - int idx = 0; - while (true) - { - //noinspection HardCodedStringLiteral - final String dirName = "dir" + idx++; - final VirtualFile dir = tempDir.findChild(dirName); - if (dir == null) - { - tempDir = tempDir.createChildDirectory(this, dirName); - break; - } - if (dir.findChild(fileName) == null) - { - tempDir = dir; - break; - } - } - } - return VirtualFileUtil.copyFile(this, file, tempDir); - } - - private VirtualFile getTempDir(Module module) throws IOException - { - VirtualFile tempDir = myModuleToTempDirMap.get(module); - if (tempDir == null) - { - final String projectName = myProject.getName(); - final String moduleName = module.getName(); - File tempDirectory = Files.createTempDirectory(projectName + "_" + moduleName).toFile(); - tempDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempDirectory); - if (tempDir == null) - { - LOG.error("Cannot locate temp directory " + tempDirectory.getPath()); - } - myModuleToTempDirMap.put(module, tempDir); - } - return tempDir; - } - - private void compileFinished(int exitValue, final ModuleChunk chunk, final String outputDir) - { - if (exitValue != 0 && !myCompileContext.getProgressIndicator().isCanceled() - && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0) - { - myCompileContext.addMessage( - CompilerMessageCategory.ERROR, - CompilerLocalize.errorCompilerInternalError(exitValue).get(), - null, - -1, - -1 - ); - } - - final List toRefresh = new ArrayList<>(); - final Map> results = new HashMap<>(); - try - { - final FileTypeManager typeManager = FileTypeManager.getInstance(); - final String outputDirPath = outputDir.replace(File.separatorChar, '/'); - try - { - for (final Module module : chunk.getModules()) - { - for (final VirtualFile root : chunk.getSourceRoots(module)) - { - final String packagePrefix = myProjectFileIndex.getPackageNameByDirectory(root); - if (LOG.isDebugEnabled()) - { - LOG.debug( - "Building output items for " + root.getPresentableUrl() + ";" + - " output dir = " + outputDirPath + "; packagePrefix = \"" + packagePrefix + "\"" - ); - } - buildOutputItemsList(outputDirPath, module, root, typeManager, root, packagePrefix, toRefresh, results); - } - } - } - catch (CacheCorruptedException e) - { - myCompileContext.requestRebuildNextTime(CompilerLocalize.errorCompilerCachesCorrupted().get()); - if (LOG.isDebugEnabled()) - { - LOG.debug(e); - } - } - } - finally - { - CompilerUtil.refreshIOFiles(toRefresh); - for (Iterator>> it = results.entrySet().iterator(); it.hasNext(); ) - { - Map.Entry> entry = it.next(); - mySink.add(entry.getKey(), entry.getValue(), VirtualFile.EMPTY_ARRAY); - it.remove(); // to free memory - } - } - myFileNameToSourceMap.clear(); // clear the map before the next use - } - - private void buildOutputItemsList( - final String outputDir, - final Module module, - VirtualFile from, - final FileTypeManager typeManager, - final VirtualFile sourceRoot, - final String packagePrefix, - final List filesToRefresh, - final Map> results - ) throws CacheCorruptedException - { - final Ref exRef = new Ref<>(null); - final ModuleFileIndex fileIndex = ModuleRootManager.getInstance(module).getFileIndex(); - final GlobalSearchScope srcRootScope = GlobalSearchScope.moduleScope(module).intersectWith(GlobalSearchScopesCore.directoryScope(myProject, - sourceRoot, true)); - - final Collection registeredInputTypes = CompilerManager.getInstance(myProject).getRegisteredInputTypes(myTranslatingCompiler); - - final ContentIterator contentIterator = child -> { - try - { - if (child.isValid()) - { - if (!child.isDirectory() && registeredInputTypes.contains(child.getFileType())) - { - updateOutputItemsList(outputDir, child, sourceRoot, packagePrefix, filesToRefresh, results, srcRootScope); - } - } - return true; - } - catch (CacheCorruptedException e) - { - exRef.set(e); - return false; - } - }; - if (fileIndex.isInContent(from)) - { - // use file index for iteration to handle 'inner modules' and excludes properly - fileIndex.iterateContentUnderDirectory(from, contentIterator); - } - else - { - // seems to be a root for generated sources - VirtualFileUtil.visitChildrenRecursively(from, new VirtualFileVisitor() - { - @Override - public boolean visitFile(VirtualFile file) - { - if (!file.isDirectory()) - { - contentIterator.processFile(file); - } - return true; - } - }); - } - final CacheCorruptedException exc = exRef.get(); - if (exc != null) - { - throw exc; - } - } - - private void putName(String sourceFileName, int classQName, String relativePathToSource, String pathToClass) - { - if (LOG.isDebugEnabled()) - { - LOG.debug("Registering [sourceFileName, relativePathToSource, pathToClass] = [" + sourceFileName + "; " + relativePathToSource + - "; " + pathToClass + "]"); - } - Set paths = myFileNameToSourceMap.get(sourceFileName); - - if (paths == null) - { - paths = new HashSet<>(); - myFileNameToSourceMap.put(sourceFileName, paths); - } - paths.add(new CompiledClass(classQName, relativePathToSource, pathToClass)); - } - - private void updateOutputItemsList(final String outputDir, - final VirtualFile srcFile, - VirtualFile sourceRoot, - final String packagePrefix, - final List filesToRefresh, - Map> results, - final GlobalSearchScope srcRootScope) throws CacheCorruptedException - { - CompositeDependencyCache dependencyCache = myCompileContext.getDependencyCache(); - JavaDependencyCache child = dependencyCache.findChild(JavaDependencyCache.class); - final Cache newCache = child.getNewClassesCache(); - final Set paths = myFileNameToSourceMap.get(srcFile.getName()); - if (paths == null || paths.isEmpty()) - { - return; - } - final String filePath = "/" + calcPackagePath(srcFile, sourceRoot, packagePrefix); - for (final CompiledClass cc : paths) - { - myCompileContext.getProgressIndicator().checkCanceled(); - if (LOG.isDebugEnabled()) - { - LOG.debug("Checking [pathToClass; relPathToSource] = " + cc); - } - - boolean pathsEquals = FileUtil.pathsEqual(filePath, cc.relativePathToSource); - if (!pathsEquals) - { - final String qName = child.resolve(cc.qName); - if (qName != null) - { - pathsEquals = myProject.getApplication().runReadAction((Computable)() -> { - final JavaPsiFacade facade = JavaPsiFacade.getInstance(myProject); - PsiClass psiClass = facade.findClass(qName, srcRootScope); - if (psiClass == null) - { - final int dollarIndex = qName.indexOf("$"); - if (dollarIndex >= 0) - { - final String topLevelClassName = qName.substring(0, dollarIndex); - psiClass = facade.findClass(topLevelClassName, srcRootScope); - } - } - if (psiClass != null) - { - final VirtualFile vFile = psiClass.getContainingFile().getVirtualFile(); - return vFile != null && vFile.equals(srcFile); - } - return false; - }); - } - } - - if (pathsEquals) - { - final String outputPath = cc.pathToClass.replace(File.separatorChar, '/'); - final Couple realLocation = moveToRealLocation(outputDir, outputPath, srcFile, filesToRefresh); - if (realLocation != null) - { - Collection outputs = results.get(realLocation.getFirst()); - if (outputs == null) - { - outputs = new ArrayList<>(); - results.put(realLocation.getFirst(), outputs); - } - outputs.add(new OutputItemImpl(realLocation.getSecond(), srcFile)); - if (PACKAGE_ANNOTATION_FILE_NAME.equals(srcFile.getName())) - { - myProcessedPackageInfos.add(srcFile); - } - if (CompilerManager.MAKE_ENABLED) - { - newCache.setPath(cc.qName, realLocation.getSecond()); - } - if (LOG.isDebugEnabled()) - { - LOG.debug("Added output item: [outputDir; outputPath; sourceFile] = [" + realLocation.getFirst() + "; " + - realLocation.getSecond() + "; " + srcFile.getPresentableUrl() + "]"); - } - } - else - { - myCompileContext.addMessage(CompilerMessageCategory.ERROR, "Failed to copy from temporary location to output directory: " + - outputPath + " (see idea.log for details)", null, -1, -1); - if (LOG.isDebugEnabled()) - { - LOG.debug("Failed to move to real location: " + outputPath + "; from " + outputDir); - } - } - } - } - } - - /** - * @param srcFile - * @param sourceRoot - * @param packagePrefix - * @return A 'package'-path to a given src file relative to a specified root. "/" slashes must be used - */ - protected static String calcPackagePath(VirtualFile srcFile, VirtualFile sourceRoot, String packagePrefix) - { - final String prefix = packagePrefix != null && packagePrefix.length() > 0 ? packagePrefix.replace('.', '/') + "/" : ""; - return prefix + VirtualFileUtil.getRelativePath(srcFile, sourceRoot, '/'); - } - - @Nullable - private Couple moveToRealLocation( - String tempOutputDir, - String pathToClass, - VirtualFile sourceFile, - final List - filesToRefresh - ) - { - final Module module = myCompileContext.getModuleByFile(sourceFile); - if (module == null) - { - final String message = "Cannot determine module for source file: " + sourceFile.getPresentableUrl() + ";\n" + - "Corresponding output file: " + pathToClass; - LOG.info(message); - myCompileContext.addMessage(CompilerMessageCategory.WARNING, message, sourceFile.getUrl(), -1, -1); - // do not move: looks like source file has been invalidated, need recompilation - return Couple.of(tempOutputDir, pathToClass); - } - final String realOutputDir; - if (myCompileContext.isInTestSourceContent(sourceFile)) - { - realOutputDir = getTestsOutputDir(module); - LOG.assertTrue(realOutputDir != null); - } - else - { - realOutputDir = getOutputDir(module); - LOG.assertTrue(realOutputDir != null); - } - - if (FileUtil.pathsEqual(tempOutputDir, realOutputDir)) - { // no need to move - filesToRefresh.add(new File(pathToClass)); - return Couple.of(realOutputDir, pathToClass); - } - - final String realPathToClass = realOutputDir + pathToClass.substring(tempOutputDir.length()); - final File fromFile = new File(pathToClass); - final File toFile = new File(realPathToClass); - - boolean success = fromFile.renameTo(toFile); - if (!success) - { - // assuming cause of the fail: intermediate dirs do not exist - FileUtil.createParentDirs(toFile); - // retry after making non-existent dirs - success = fromFile.renameTo(toFile); - } - if (!success) - { // failed to move the file: e.g. because source and destination reside on different mountpoints. - try - { - FileUtil.copy(fromFile, toFile, FilePermissionCopier.BY_NIO2); - FileUtil.delete(fromFile); - success = true; - } - catch (IOException e) - { - LOG.info(e); - success = false; - } - } - if (success) - { - filesToRefresh.add(toFile); - return Couple.of(realOutputDir, realPathToClass); - } - return null; - } - - private final Map myModuleToTestsOutput = new HashMap<>(); - - private String getTestsOutputDir(final Module module) - { - if (myModuleToTestsOutput.containsKey(module)) - { - return myModuleToTestsOutput.get(module); - } - final VirtualFile outputDirectory = myCompileContext.getModuleOutputDirectoryForTests(module); - final String out = outputDirectory != null ? outputDirectory.getPath() : null; - myModuleToTestsOutput.put(module, out); - return out; - } - - private final Map myModuleToOutput = new HashMap<>(); - - private String getOutputDir(final Module module) - { - if (myModuleToOutput.containsKey(module)) - { - return myModuleToOutput.get(module); - } - final VirtualFile outputDirectory = myCompileContext.getModuleOutputDirectory(module); - final String out = outputDirectory != null ? outputDirectory.getPath() : null; - myModuleToOutput.put(module, out); - return out; - } - - private void sourceFileProcessed() - { - myStatistics.incFilesCount(); - updateStatistics(); - } - - private void updateStatistics() - { - String moduleName = myModuleName; - final LocalizeValue msg = moduleName != null - ? CompilerLocalize.statisticsFilesClassesModule(myStatistics.getFilesCount(), myStatistics.getClassesCount(), moduleName) - : CompilerLocalize.statisticsFilesClasses(myStatistics.getFilesCount(), myStatistics.getClassesCount()); - myCompileContext.getProgressIndicator().setText2(msg); - //myCompileContext.getProgressIndicator().setFraction(1.0* myProcessedFilesCount /myTotalFilesToCompile); - } - - public class ClassParsingHandler implements Runnable - { - private final BlockingQueue myPaths = new ArrayBlockingQueue<>(50000); - private CacheCorruptedException myError = null; - private final JavaDependencyCache myJavaDependencyCache; - private final Map myParsingInfo; - - private ClassParsingHandler(Map parsingInfo) - { - myParsingInfo = parsingInfo; - myJavaDependencyCache = myCompileContext.getDependencyCache().findChild(JavaDependencyCache.class); - } - - private volatile boolean processing; - - @Override - public void run() - { - processing = true; - try - { - while (true) - { - FileObject path = myPaths.take(); - - if (path == ourStopThreadToken) - { - break; - } - processPath(path); - } - } - catch (InterruptedException e) - { - LOG.error(e); - } - catch (CacheCorruptedException e) - { - myError = e; - } - finally - { - processing = false; - } - } - - public void addPath(FileObject path) throws CacheCorruptedException - { - if (myError != null) - { - throw myError; - } - myPaths.offer(path); - } - - public void stopParsing() - { - myPaths.offer(ourStopThreadToken); - } - - private void processPath(FileObject fileObject) throws CacheCorruptedException - { - File file = fileObject.getFile(); - final String path = file.getPath(); - try - { - byte[] fileContent = fileObject.getOrLoadContent(); - // the file is assumed to exist! - int newClassQName = myJavaDependencyCache.reparseClassFile(file, fileContent); - final Cache newClassesCache = myJavaDependencyCache.getNewClassesCache(); - final String sourceFileName = newClassesCache.getSourceFileName(newClassQName); - final String qName = myJavaDependencyCache.resolve(newClassQName); - String relativePathToSource = "/" + JavaMakeUtil.createRelativePathToSource(qName, sourceFileName); - putName(sourceFileName, newClassQName, relativePathToSource, path); - - fileObject.setClassId(newClassQName); - myParsingInfo.put(file, fileObject); - } - catch (ClsFormatException e) - { - final String m = e.getMessage(); - String message = CompilerLocalize.errorBadClassFileFormat(StringUtil.isEmpty(m) ? path : m + "\n" + path).get(); - myCompileContext.addMessage(CompilerMessageCategory.ERROR, message, null, -1, -1); - LOG.info(e); - } - catch (IOException e) - { - myCompileContext.addMessage(CompilerMessageCategory.ERROR, e.getMessage(), null, -1, -1); - LOG.info(e); - } - finally - { - myStatistics.incClassesCount(); - updateStatistics(); - } - } - } - - private static final class CompileStatistics - { - private static final Key KEY = Key.create("_Compile_Statistics_"); - private int myClassesCount; - private int myFilesCount; - - public int getClassesCount() - { - return myClassesCount; - } - - public int incClassesCount() - { - return ++myClassesCount; - } - - public int getFilesCount() - { - return myFilesCount; - } - - public int incFilesCount() - { - return ++myFilesCount; - } - } + } + + private class CompilerParsingHandler extends CompilerParsingThread { + private final ClassParsingHandler myClassParsingThread; + + private CompilerParsingHandler( + ProcessHandler processHandler, + CompileContext context, + OutputParser outputParser, + ClassParsingHandler classParsingThread, + boolean readErrorStream, + boolean trimLines + ) { + super(processHandler, outputParser, readErrorStream, trimLines, context); + myClassParsingThread = classParsingThread; + } + + @Override + protected void processCompiledClass(FileObject classFileToProcess) throws CacheCorruptedException { + myClassParsingThread.addPath(classFileToProcess); + } + } + + private void doCompile(ModuleChunk chunk, String outputDir, Map parsingInfo) throws IOException { + myCompileContext.getProgressIndicator().checkCanceled(); + + if (ReadAction.compute(() -> chunk.getFilesToCompile().isEmpty())) { + return; // should not invoke javac with empty sources list + } + + BackendCompilerProcessBuilder processBuilder = null; + int exitValue = 0; + try { + ClassParsingHandler classParsingHandler = new ClassParsingHandler(parsingInfo); + + myCompileContext.putUserData(CLASS_PARSING_HANDLER_KEY, classParsingHandler); + + processBuilder = myCompiler.prepareProcess(chunk, outputDir, myCompileContext); + + BackendCompilerMonitor monitor = myCompiler.createMonitor(processBuilder); + + GeneralCommandLine commandLine = ReadAction.compute(processBuilder::buildCommandLine); + + assert commandLine != null; + + ProcessHandler process; + try { + process = processBuilder.createProcess(commandLine); + if (monitor != null) { + monitor.handleProcessStart(process); + } + } + catch (ExecutionException e) { + if (monitor != null) { + monitor.disposeWithTree(); + } + throw new IOException(e); + } + + ExecutorService executorService = AppExecutorUtil.getAppExecutorService(); + Future classParsingFuture = executorService.submit(classParsingHandler); + + OutputParser errorParser = myCompiler.createErrorParser(processBuilder, outputDir, process); + CompilerParsingThread errorParsingThread = errorParser == null ? null + : new CompilerParsingHandler(process, myCompileContext, errorParser, classParsingHandler, true, errorParser.isTrimLines()); + + Future errorParsingFuture = CompletableFuture.completedFuture(null); + if (errorParsingThread != null) { + errorParsingFuture = executorService.submit(errorParsingThread); + } + + Future outputParsingFuture = CompletableFuture.completedFuture(null); + OutputParser outputParser = myCompiler.createOutputParser(processBuilder, outputDir); + CompilerParsingThread outputParsingHandler = outputParser == null ? null + : new CompilerParsingHandler( + process, + myCompileContext, + outputParser, + classParsingHandler, + false, + outputParser.isTrimLines() + ); + if (outputParsingHandler != null) { + outputParsingFuture = executorService.submit(outputParsingHandler); + } + + try { + process.startNotify(); + + process.waitFor(); + } + catch (Throwable e) { + if (monitor != null) { + monitor.disposeWithTree(); + } + + throw new IOException(e); + } + finally { + if (errorParsingThread != null) { + errorParsingThread.stopParsing(); + } + + if (outputParsingHandler != null) { + outputParsingHandler.stopParsing(); + } + classParsingHandler.stopParsing(); + + if (monitor != null) { + monitor.disposeWithTree(); + } + + waitABit(classParsingFuture); + waitABit(errorParsingFuture); + waitABit(outputParsingFuture); + + registerParsingException(outputParsingHandler); + registerParsingException(errorParsingThread); + + assert outputParsingHandler == null || !outputParsingHandler.processing; + assert errorParsingThread == null || !errorParsingThread.processing; + assert classParsingHandler == null || !classParsingHandler.processing; + } + } + finally { + if (processBuilder != null) { + processBuilder.clearTempFiles(); + } + compileFinished(exitValue, chunk, outputDir); + myModuleName = null; + } + } + + private static void waitABit(Future threadFuture) { + if (threadFuture != null) { + try { + threadFuture.get(); + } + catch (InterruptedException | java.util.concurrent.ExecutionException ignored) { + LOG.info("Thread interrupted", ignored); + } + } + } + + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") + private void registerParsingException(CompilerParsingThread outputParsingThread) { + Throwable error = outputParsingThread == null ? null : outputParsingThread.getError(); + if (error != null) { + LocalizeValue message = LocalizeValue.ofNullable(error.getMessage()); + if (error instanceof CacheCorruptedException) { + myCompileContext.requestRebuildNextTime(message); + } + else { + myCompileContext.newError(message).add(); + } + } + } + + @RequiredUIAccess + private void runTransformingCompilers(ModuleChunk chunk) { + JavaSourceTransformingCompiler[] transformers = + CompilerManager.getInstance(myProject).getCompilers(JavaSourceTransformingCompiler.class); + if (transformers.length == 0) { + return; + } + if (LOG.isDebugEnabled()) { + LOG.debug("Running transforming compilers..."); + } + Module[] modules = chunk.getModules(); + for (JavaSourceTransformingCompiler transformer : transformers) { + Map originalToCopyFileMap = new HashMap<>(); + Application application = Application.get(); + application.invokeAndWait( + () -> { + for (Module module : modules) { + for (VirtualFile file : chunk.getFilesToCompile(module)) { + VirtualFile untransformed = chunk.getOriginalFile(file); + if (transformer.isTransformable(untransformed)) { + application.runWriteAction(() -> { + try { + // if untransformed != file, the file is already a (possibly transformed) copy of the original + // 'untransformed' file. + // If this is the case, just use already created copy and do not copy file content once again + VirtualFile fileCopy = + untransformed.equals(file) ? createFileCopy(getTempDir(module), file) : file; + originalToCopyFileMap.put(file, fileCopy); + } + catch (IOException e) { + // skip it + } + }); + } + } + } + }, + myCompileContext.getProgressIndicator().getModalityState() + ); + + // do actual transform + for (Module module : modules) { + List filesToCompile = chunk.getFilesToCompile(module); + for (int j = 0; j < filesToCompile.size(); j++) { + VirtualFile file = filesToCompile.get(j); + VirtualFile fileCopy = originalToCopyFileMap.get(file); + if (fileCopy != null) { + boolean ok = transformer.transform(myCompileContext, fileCopy, chunk.getOriginalFile(file)); + if (ok) { + chunk.substituteWithTransformedVersion(module, j, fileCopy); + } + } + } + } + } + } + + @RequiredWriteAction + private VirtualFile createFileCopy(VirtualFile tempDir, VirtualFile file) throws IOException { + String fileName = file.getName(); + if (tempDir.findChild(fileName) != null) { + int idx = 0; + while (true) { + //noinspection HardCodedStringLiteral + String dirName = "dir" + idx++; + VirtualFile dir = tempDir.findChild(dirName); + if (dir == null) { + tempDir = tempDir.createChildDirectory(this, dirName); + break; + } + if (dir.findChild(fileName) == null) { + tempDir = dir; + break; + } + } + } + return VirtualFileUtil.copyFile(this, file, tempDir); + } + + private VirtualFile getTempDir(Module module) throws IOException { + VirtualFile tempDir = myModuleToTempDirMap.get(module); + if (tempDir == null) { + String projectName = myProject.getName(); + String moduleName = module.getName(); + File tempDirectory = Files.createTempDirectory(projectName + "_" + moduleName).toFile(); + tempDir = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(tempDirectory); + if (tempDir == null) { + LOG.error("Cannot locate temp directory " + tempDirectory.getPath()); + } + myModuleToTempDirMap.put(module, tempDir); + } + return tempDir; + } + + private void compileFinished(int exitValue, ModuleChunk chunk, String outputDir) { + if (exitValue != 0 && !myCompileContext.getProgressIndicator().isCanceled() + && myCompileContext.getMessageCount(CompilerMessageCategory.ERROR) == 0) { + myCompileContext.newError(CompilerLocalize.errorCompilerInternalError(exitValue)).add(); + } + + List toRefresh = new ArrayList<>(); + Map> results = new HashMap<>(); + try { + FileTypeManager typeManager = FileTypeManager.getInstance(); + String outputDirPath = outputDir.replace(File.separatorChar, '/'); + try { + for (Module module : chunk.getModules()) { + for (VirtualFile root : chunk.getSourceRoots(module)) { + String packagePrefix = myProjectFileIndex.getPackageNameByDirectory(root); + if (LOG.isDebugEnabled()) { + LOG.debug( + "Building output items for ", root.getPresentableUrl(), "; output dir = ", outputDirPath, + "; packagePrefix = \"", packagePrefix, "\"" + ); + } + buildOutputItemsList(outputDirPath, module, root, typeManager, root, packagePrefix, toRefresh, results); + } + } + } + catch (CacheCorruptedException e) { + myCompileContext.requestRebuildNextTime(CompilerLocalize.errorCompilerCachesCorrupted()); + if (LOG.isDebugEnabled()) { + LOG.debug(e); + } + } + } + finally { + CompilerUtil.refreshIOFiles(toRefresh); + for (Iterator>> it = results.entrySet().iterator(); it.hasNext(); ) { + Map.Entry> entry = it.next(); + mySink.add(entry.getKey(), entry.getValue(), VirtualFile.EMPTY_ARRAY); + it.remove(); // to free memory + } + } + myFileNameToSourceMap.clear(); // clear the map before the next use + } + + private void buildOutputItemsList( + String outputDir, + Module module, + VirtualFile from, + FileTypeManager typeManager, + VirtualFile sourceRoot, + String packagePrefix, + List filesToRefresh, + Map> results + ) throws CacheCorruptedException { + SimpleReference exRef = new SimpleReference<>(null); + ModuleFileIndex fileIndex = ModuleRootManager.getInstance(module).getFileIndex(); + GlobalSearchScope srcRootScope = GlobalSearchScope.moduleScope(module) + .intersectWith(GlobalSearchScopesCore.directoryScope(myProject, sourceRoot, true)); + + Collection registeredInputTypes = CompilerManager.getInstance(myProject).getRegisteredInputTypes(myTranslatingCompiler); + + final ContentIterator contentIterator = child -> { + try { + if (child.isValid()) { + if (!child.isDirectory() && registeredInputTypes.contains(child.getFileType())) { + updateOutputItemsList(outputDir, child, sourceRoot, packagePrefix, filesToRefresh, results, srcRootScope); + } + } + return true; + } + catch (CacheCorruptedException e) { + exRef.set(e); + return false; + } + }; + if (fileIndex.isInContent(from)) { + // use file index for iteration to handle 'inner modules' and excludes properly + fileIndex.iterateContentUnderDirectory(from, contentIterator); + } + else { + // seems to be a root for generated sources + VirtualFileUtil.visitChildrenRecursively(from, new VirtualFileVisitor() { + @Override + public boolean visitFile(VirtualFile file) { + if (!file.isDirectory()) { + contentIterator.processFile(file); + } + return true; + } + }); + } + CacheCorruptedException exc = exRef.get(); + if (exc != null) { + throw exc; + } + } + + private void putName(String sourceFileName, int classQName, String relativePathToSource, String pathToClass) { + if (LOG.isDebugEnabled()) { + LOG.debug("Registering [sourceFileName, relativePathToSource, pathToClass] = [" + sourceFileName + "; " + relativePathToSource + + "; " + pathToClass + "]"); + } + Set paths = myFileNameToSourceMap.get(sourceFileName); + + if (paths == null) { + paths = new HashSet<>(); + myFileNameToSourceMap.put(sourceFileName, paths); + } + paths.add(new CompiledClass(classQName, relativePathToSource, pathToClass)); + } + + private void updateOutputItemsList( + String outputDir, + VirtualFile srcFile, + VirtualFile sourceRoot, + String packagePrefix, + List filesToRefresh, + Map> results, + GlobalSearchScope srcRootScope + ) throws CacheCorruptedException { + CompositeDependencyCache dependencyCache = myCompileContext.getDependencyCache(); + JavaDependencyCache child = dependencyCache.findChild(JavaDependencyCache.class); + Cache newCache = child.getNewClassesCache(); + Set paths = myFileNameToSourceMap.get(srcFile.getName()); + if (paths == null || paths.isEmpty()) { + return; + } + String filePath = "/" + calcPackagePath(srcFile, sourceRoot, packagePrefix); + for (CompiledClass cc : paths) { + myCompileContext.getProgressIndicator().checkCanceled(); + if (LOG.isDebugEnabled()) { + LOG.debug("Checking [pathToClass; relPathToSource] = " + cc); + } + + boolean pathsEquals = FileUtil.pathsEqual(filePath, cc.relativePathToSource); + if (!pathsEquals) { + String qName = child.resolve(cc.qName); + if (qName != null) { + pathsEquals = myProject.getApplication().runReadAction((Supplier) () -> { + JavaPsiFacade facade = JavaPsiFacade.getInstance(myProject); + PsiClass psiClass = facade.findClass(qName, srcRootScope); + if (psiClass == null) { + int dollarIndex = qName.indexOf("$"); + if (dollarIndex >= 0) { + String topLevelClassName = qName.substring(0, dollarIndex); + psiClass = facade.findClass(topLevelClassName, srcRootScope); + } + } + if (psiClass != null) { + VirtualFile vFile = psiClass.getContainingFile().getVirtualFile(); + return vFile != null && vFile.equals(srcFile); + } + return false; + }); + } + } + + if (pathsEquals) { + String outputPath = cc.pathToClass.replace(File.separatorChar, '/'); + Couple realLocation = moveToRealLocation(outputDir, outputPath, srcFile, filesToRefresh); + if (realLocation != null) { + Collection outputs = results.get(realLocation.getFirst()); + if (outputs == null) { + outputs = new ArrayList<>(); + results.put(realLocation.getFirst(), outputs); + } + outputs.add(new OutputItemImpl(realLocation.getSecond(), srcFile)); + if (PACKAGE_ANNOTATION_FILE_NAME.equals(srcFile.getName())) { + myProcessedPackageInfos.add(srcFile); + } + if (CompilerManager.MAKE_ENABLED) { + newCache.setPath(cc.qName, realLocation.getSecond()); + } + if (LOG.isDebugEnabled()) { + LOG.debug("Added output item: [outputDir; outputPath; sourceFile] = [" + realLocation.getFirst() + "; " + + realLocation.getSecond() + "; " + srcFile.getPresentableUrl() + "]"); + } + } + else { + myCompileContext.newError(LocalizeValue.localizeTODO( + "Failed to copy from temporary location to output directory: " + outputPath + " (see idea.log for details)" + )).add(); + if (LOG.isDebugEnabled()) { + LOG.debug("Failed to move to real location: " + outputPath + "; from " + outputDir); + } + } + } + } + } + + /** + * @param srcFile + * @param sourceRoot + * @param packagePrefix + * @return A 'package'-path to a given src file relative to a specified root. "/" slashes must be used + */ + protected static String calcPackagePath(VirtualFile srcFile, VirtualFile sourceRoot, String packagePrefix) { + String prefix = packagePrefix != null && packagePrefix.length() > 0 ? packagePrefix.replace('.', '/') + "/" : ""; + return prefix + VirtualFileUtil.getRelativePath(srcFile, sourceRoot, '/'); + } + + @Nullable + private Couple moveToRealLocation( + String tempOutputDir, + String pathToClass, + VirtualFile sourceFile, + List filesToRefresh + ) { + Module module = myCompileContext.getModuleByFile(sourceFile); + if (module == null) { + String message = "Cannot determine module for source file: " + sourceFile.getPresentableUrl() + ";\n" + + "Corresponding output file: " + pathToClass; + LOG.info(message); + myCompileContext.newWarning(LocalizeValue.localizeTODO(message)).add(); + // do not move: looks like source file has been invalidated, need recompilation + return Couple.of(tempOutputDir, pathToClass); + } + String realOutputDir; + if (myCompileContext.isInTestSourceContent(sourceFile)) { + realOutputDir = getTestsOutputDir(module); + LOG.assertTrue(realOutputDir != null); + } + else { + realOutputDir = getOutputDir(module); + LOG.assertTrue(realOutputDir != null); + } + + if (FileUtil.pathsEqual(tempOutputDir, realOutputDir)) { // no need to move + filesToRefresh.add(new File(pathToClass)); + return Couple.of(realOutputDir, pathToClass); + } + + String realPathToClass = realOutputDir + pathToClass.substring(tempOutputDir.length()); + File fromFile = new File(pathToClass); + File toFile = new File(realPathToClass); + + boolean success = fromFile.renameTo(toFile); + if (!success) { + // assuming cause of the fail: intermediate dirs do not exist + FileUtil.createParentDirs(toFile); + // retry after making non-existent dirs + success = fromFile.renameTo(toFile); + } + if (!success) { // failed to move the file: e.g. because source and destination reside on different mount-points. + try { + FileUtil.copy(fromFile, toFile, FilePermissionCopier.BY_NIO2); + FileUtil.delete(fromFile); + success = true; + } + catch (IOException e) { + LOG.info(e); + success = false; + } + } + if (success) { + filesToRefresh.add(toFile); + return Couple.of(realOutputDir, realPathToClass); + } + return null; + } + + private final Map myModuleToTestsOutput = new HashMap<>(); + + private String getTestsOutputDir(Module module) { + if (myModuleToTestsOutput.containsKey(module)) { + return myModuleToTestsOutput.get(module); + } + VirtualFile outputDirectory = myCompileContext.getModuleOutputDirectoryForTests(module); + String out = outputDirectory != null ? outputDirectory.getPath() : null; + myModuleToTestsOutput.put(module, out); + return out; + } + + private final Map myModuleToOutput = new HashMap<>(); + + private String getOutputDir(Module module) { + if (myModuleToOutput.containsKey(module)) { + return myModuleToOutput.get(module); + } + VirtualFile outputDirectory = myCompileContext.getModuleOutputDirectory(module); + String out = outputDirectory != null ? outputDirectory.getPath() : null; + myModuleToOutput.put(module, out); + return out; + } + + private void sourceFileProcessed() { + myStatistics.incFilesCount(); + updateStatistics(); + } + + private void updateStatistics() { + String moduleName = myModuleName; + LocalizeValue msg = moduleName != null + ? CompilerLocalize.statisticsFilesClassesModule(myStatistics.getFilesCount(), myStatistics.getClassesCount(), moduleName) + : CompilerLocalize.statisticsFilesClasses(myStatistics.getFilesCount(), myStatistics.getClassesCount()); + myCompileContext.getProgressIndicator().setText2(msg); + //myCompileContext.getProgressIndicator().setFraction(1.0* myProcessedFilesCount /myTotalFilesToCompile); + } + + public class ClassParsingHandler implements Runnable { + private final BlockingQueue myPaths = new ArrayBlockingQueue<>(50000); + private CacheCorruptedException myError = null; + private final JavaDependencyCache myJavaDependencyCache; + private final Map myParsingInfo; + + private ClassParsingHandler(Map parsingInfo) { + myParsingInfo = parsingInfo; + myJavaDependencyCache = myCompileContext.getDependencyCache().findChild(JavaDependencyCache.class); + } + + private volatile boolean processing; + + @Override + public void run() { + processing = true; + try { + while (true) { + FileObject path = myPaths.take(); + + if (path == ourStopThreadToken) { + break; + } + processPath(path); + } + } + catch (InterruptedException e) { + LOG.error(e); + } + catch (CacheCorruptedException e) { + myError = e; + } + finally { + processing = false; + } + } + + public void addPath(FileObject path) throws CacheCorruptedException { + if (myError != null) { + throw myError; + } + myPaths.offer(path); + } + + public void stopParsing() { + myPaths.offer(ourStopThreadToken); + } + + private void processPath(FileObject fileObject) throws CacheCorruptedException { + File file = fileObject.getFile(); + String path = file.getPath(); + try { + byte[] fileContent = fileObject.getOrLoadContent(); + // the file is assumed to exist! + int newClassQName = myJavaDependencyCache.reparseClassFile(file, fileContent); + Cache newClassesCache = myJavaDependencyCache.getNewClassesCache(); + String sourceFileName = newClassesCache.getSourceFileName(newClassQName); + String qName = myJavaDependencyCache.resolve(newClassQName); + String relativePathToSource = "/" + JavaMakeUtil.createRelativePathToSource(qName, sourceFileName); + putName(sourceFileName, newClassQName, relativePathToSource, path); + + fileObject.setClassId(newClassQName); + myParsingInfo.put(file, fileObject); + } + catch (ClsFormatException e) { + String m = e.getMessage(); + myCompileContext.newError(CompilerLocalize.errorBadClassFileFormat(StringUtil.isEmpty(m) ? path : m + "\n" + path)).add(); + LOG.info(e); + } + catch (IOException e) { + myCompileContext.newError(LocalizeValue.ofNullable(e.getMessage())).add(); + LOG.info(e); + } + finally { + myStatistics.incClassesCount(); + updateStatistics(); + } + } + } + + private static final class CompileStatistics { + private static final Key KEY = Key.create("_Compile_Statistics_"); + private int myClassesCount; + private int myFilesCount; + + public int getClassesCount() { + return myClassesCount; + } + + public int incClassesCount() { + return ++myClassesCount; + } + + public int getFilesCount() { + return myFilesCount; + } + + public int incFilesCount() { + return ++myFilesCount; + } + } } diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/CompilerParsingThread.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/CompilerParsingThread.java index 0414b3ba43..5a6b922c97 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/CompilerParsingThread.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/CompilerParsingThread.java @@ -19,6 +19,7 @@ import consulo.compiler.CacheCorruptedException; import consulo.compiler.CompileContext; import consulo.compiler.CompilerMessageCategory; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.process.ProcessHandler; import consulo.process.ProcessOutputTypes; @@ -27,6 +28,7 @@ import consulo.util.dataholder.Key; import org.jspecify.annotations.Nullable; + import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -34,218 +36,185 @@ * @author Eugene Zhuravlev * Date: Mar 30, 2004 */ -public class CompilerParsingThread implements Runnable, OutputParser.Callback -{ - private static final Logger LOG = Logger.getInstance(CompilerParsingThread.class); - public static final String TERMINATION_STRING = "__terminate_read__"; - - private ProcessHandler myProcessHandler; - private final OutputParser myOutputParser; - private final boolean myTrimLines; - private Throwable myError = null; - private final boolean myIsUnitTestMode; - private FileObject myClassFileToProcess = null; - private String myLastReadLine = null; - private String myPushBackLine = null; - private volatile boolean myProcessExited = false; - private final CompileContext myContext; - - private final BlockingQueue myLines = new ArrayBlockingQueue<>(50); - - public CompilerParsingThread(ProcessHandler processHandler, OutputParser outputParser, final boolean readErrorStream, boolean trimLines, CompileContext context) - { - myProcessHandler = processHandler; - myOutputParser = outputParser; - myTrimLines = trimLines; - myContext = context; - - myIsUnitTestMode = false; - - processHandler.addProcessListener(new ProcessAdapter() - { - @Override - public void onTextAvailable(ProcessEvent event, Key outputType) - { - if(!readErrorStream && outputType == ProcessOutputTypes.STDERR) - { - return; - } - - myLines.offer(event.getText().trim()); - } - }); - } - - volatile boolean processing; - - @Override - public void run() - { - processing = true; - try - { - while(!isProcessTerminated()) - { - if(!myIsUnitTestMode && myProcessHandler == null) - { - break; - } - - if(isCanceled()) - { - break; - } - - if(!myOutputParser.processMessageLine(this)) - { - break; - } - } - - if(myClassFileToProcess != null) - { - processCompiledClass(myClassFileToProcess); - myClassFileToProcess = null; - } - } - catch(Throwable e) - { - myError = e; - LOG.warn(e); - } - finally - { - processing = false; - } - } - - private void killProcess() - { - if(myProcessHandler != null) - { - myProcessHandler = null; - } - } - - public Throwable getError() - { - return myError; - } - - @Override - public String getCurrentLine() - { - return myLastReadLine; - } - - @Override - public final String getNextLine() - { - final String pushBack = myPushBackLine; - if(pushBack != null) - { - myPushBackLine = null; - myLastReadLine = pushBack; - return pushBack; - } - final String line = readLine(); - if(LOG.isDebugEnabled()) - { - LOG.debug("LIne read: #" + line + "#"); - } - if(TERMINATION_STRING.equals(line)) - { - myLastReadLine = null; - } - else - { - myLastReadLine = line == null ? null : myTrimLines ? line.trim() : line; - } - return myLastReadLine; - } - - @Override - public void pushBack(String line) - { - myLastReadLine = null; - myPushBackLine = line; - } - - @Override - public final void fileGenerated(FileObject path) - { - // javac first logs file generated, then starts to write the file to disk, - // so this thread sometimes can stumble on not yet existing file, - // hence this complex logic - FileObject previousPath = myClassFileToProcess; - myClassFileToProcess = path; - if(previousPath != null) - { - try - { - processCompiledClass(previousPath); - } - catch(CacheCorruptedException e) - { - myError = e; - LOG.info(e); - killProcess(); - } - } - } - - protected boolean isCanceled() - { - return myContext.getProgressIndicator().isCanceled(); - } - - @Override - public void setProgressText(String text) - { - myContext.getProgressIndicator().setText(text); - } - - @Override - public void fileProcessed(String path) - { - } - - @Override - public void message(CompilerMessageCategory category, String message, String url, int lineNum, int columnNum) - { - myContext.addMessage(category, message, url, lineNum, columnNum); - } - - protected void processCompiledClass(final FileObject classFileToProcess) throws CacheCorruptedException - { - } - - @Nullable - private String readLine() - { - if(isProcessTerminated()) - { - return null; - } - - try - { - return myLines.take(); - } - catch(InterruptedException e) - { - return null; - } - } - - public void stopParsing() - { - myLines.offer(TERMINATION_STRING); - myProcessExited = true; - } - - private boolean isProcessTerminated() - { - return myProcessExited; - } +public class CompilerParsingThread implements Runnable, OutputParser.Callback { + private static final Logger LOG = Logger.getInstance(CompilerParsingThread.class); + public static final String TERMINATION_STRING = "__terminate_read__"; + + private ProcessHandler myProcessHandler; + private final OutputParser myOutputParser; + private final boolean myTrimLines; + private Throwable myError = null; + private final boolean myIsUnitTestMode; + private FileObject myClassFileToProcess = null; + private String myLastReadLine = null; + private String myPushBackLine = null; + private volatile boolean myProcessExited = false; + private final CompileContext myContext; + + private final BlockingQueue myLines = new ArrayBlockingQueue<>(50); + + public CompilerParsingThread( + ProcessHandler processHandler, + OutputParser outputParser, + final boolean readErrorStream, + boolean trimLines, + CompileContext context + ) { + myProcessHandler = processHandler; + myOutputParser = outputParser; + myTrimLines = trimLines; + myContext = context; + + myIsUnitTestMode = false; + + processHandler.addProcessListener(new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + if (!readErrorStream && outputType == ProcessOutputTypes.STDERR) { + return; + } + + myLines.offer(event.getText().trim()); + } + }); + } + + volatile boolean processing; + + @Override + public void run() { + processing = true; + try { + while (!isProcessTerminated()) { + if (!myIsUnitTestMode && myProcessHandler == null) { + break; + } + + if (isCanceled()) { + break; + } + + if (!myOutputParser.processMessageLine(this)) { + break; + } + } + + if (myClassFileToProcess != null) { + processCompiledClass(myClassFileToProcess); + myClassFileToProcess = null; + } + } + catch (Throwable e) { + myError = e; + LOG.warn(e); + } + finally { + processing = false; + } + } + + private void killProcess() { + if (myProcessHandler != null) { + myProcessHandler = null; + } + } + + public Throwable getError() { + return myError; + } + + @Override + public String getCurrentLine() { + return myLastReadLine; + } + + @Override + public final String getNextLine() { + String pushBack = myPushBackLine; + if (pushBack != null) { + myPushBackLine = null; + myLastReadLine = pushBack; + return pushBack; + } + String line = readLine(); + if (LOG.isDebugEnabled()) { + LOG.debug("LIne read: #" + line + "#"); + } + if (TERMINATION_STRING.equals(line)) { + myLastReadLine = null; + } + else { + myLastReadLine = line == null ? null : myTrimLines ? line.trim() : line; + } + return myLastReadLine; + } + + @Override + public void pushBack(String line) { + myLastReadLine = null; + myPushBackLine = line; + } + + @Override + public final void fileGenerated(FileObject path) { + // javac first logs file generated, then starts to write the file to disk, + // so this thread sometimes can stumble on not yet existing file, + // hence this complex logic + FileObject previousPath = myClassFileToProcess; + myClassFileToProcess = path; + if (previousPath != null) { + try { + processCompiledClass(previousPath); + } + catch (CacheCorruptedException e) { + myError = e; + LOG.info(e); + killProcess(); + } + } + } + + protected boolean isCanceled() { + return myContext.getProgressIndicator().isCanceled(); + } + + @Override + public void setProgressText(LocalizeValue text) { + myContext.getProgressIndicator().setText(text); + } + + @Override + public void fileProcessed(String path) { + } + + @Override + public CompileContext.MessageBuilder newMessage(CompilerMessageCategory category, LocalizeValue message) { + return myContext.newMessage(category, message); + } + + protected void processCompiledClass(FileObject classFileToProcess) throws CacheCorruptedException { + } + + @Nullable + private String readLine() { + if (isProcessTerminated()) { + return null; + } + + try { + return myLines.take(); + } + catch (InterruptedException e) { + return null; + } + } + + public void stopParsing() { + myLines.offer(TERMINATION_STRING); + myProcessExited = true; + } + + private boolean isProcessTerminated() { + return myProcessExited; + } } diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java index 126c789593..69f1903fed 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java @@ -28,6 +28,7 @@ import consulo.language.content.ProductionResourceContentFolderTypeProvider; import consulo.language.content.TestResourceContentFolderTypeProvider; import consulo.language.util.ModuleUtilCore; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.module.Module; import consulo.module.content.ProjectFileIndex; @@ -54,103 +55,103 @@ */ @ExtensionImpl(id = "java-compiler") public class JavaCompiler implements TranslatingCompiler { - private static final Logger LOGGER = Logger.getInstance(JavaCompiler.class); - - public static final Key> ourOutputFileParseInfo = Key.create("ourOutputFileParseInfo"); - - private final Project myProject; - - @Inject - public JavaCompiler(Project project) { - myProject = project; - } - - @Override - public String getDescription() { - return CompilerLocalize.javaCompilerDescription().get(); - } - - @Override - public boolean isCompilableFile(VirtualFile file, CompileContext context) { - return file.getFileType() == JavaFileType.INSTANCE; - } - - @Override - public void compile(CompileContext context, Chunk moduleChunk, VirtualFile[] files, OutputSink sink) { - boolean found = false; - for (Module module : moduleChunk.getNodes()) { - JavaModuleExtension extension = ModuleUtilCore.getExtension(module, JavaModuleExtension.class); - if (extension != null) { - found = true; - break; - } + private static final Logger LOGGER = Logger.getInstance(JavaCompiler.class); + + public static final Key> ourOutputFileParseInfo = Key.create("ourOutputFileParseInfo"); + + private final Project myProject; + + @Inject + public JavaCompiler(Project project) { + myProject = project; + } + + @Override + public String getDescription() { + return CompilerLocalize.javaCompilerDescription().get(); + } + + @Override + public boolean isCompilableFile(VirtualFile file, CompileContext context) { + return file.getFileType() == JavaFileType.INSTANCE; } - if (!found) { - return; + @Override + public void compile(CompileContext context, Chunk moduleChunk, VirtualFile[] files, OutputSink sink) { + boolean found = false; + for (Module module : moduleChunk.getNodes()) { + JavaModuleExtension extension = ModuleUtilCore.getExtension(module, JavaModuleExtension.class); + if (extension != null) { + found = true; + break; + } + } + + if (!found) { + return; + } + + Map parsingInfo = Maps.newHashMap(FileUtil.FILE_HASHING_STRATEGY); + context.putUserData(ourOutputFileParseInfo, parsingInfo); + + BackendCompiler backEndCompiler = getBackEndCompiler(); + BackendCompilerWrapper wrapper = new BackendCompilerWrapper( + this, + moduleChunk, + myProject, + filterResourceFiles(context, files), + (CompileContextEx) context, + backEndCompiler, + sink + ); + try { + wrapper.compile(parsingInfo); + } + catch (CompilerException e) { + context.newError(LocalizeValue.of(ExceptionUtil.getThrowableText(e))); + LOGGER.info(e); + } + catch (CacheCorruptedException e) { + LOGGER.info(e); + context.requestRebuildNextTime(LocalizeValue.ofNullable(e.getMessage())); + } } - Map parsingInfo = Maps.newHashMap(FileUtil.FILE_HASHING_STRATEGY); - context.putUserData(ourOutputFileParseInfo, parsingInfo); - - final BackendCompiler backEndCompiler = getBackEndCompiler(); - final BackendCompilerWrapper wrapper = new BackendCompilerWrapper( - this, - moduleChunk, - myProject, - filterResourceFiles(context, files), - (CompileContextEx)context, - backEndCompiler, - sink - ); - try { - wrapper.compile(parsingInfo); + private static List filterResourceFiles(CompileContext compileContext, VirtualFile[] virtualFiles) { + ProjectFileIndex fileIndex = ProjectFileIndex.getInstance(compileContext.getProject()); + + List list = new ArrayList<>(virtualFiles.length); + for (VirtualFile file : virtualFiles) { + ContentFolderTypeProvider provider = fileIndex.getContentFolderTypeForFile(file); + if (provider == ProductionResourceContentFolderTypeProvider.getInstance() || provider == TestResourceContentFolderTypeProvider.getInstance()) { + continue; + } + list.add(file); + } + return list; } - catch (CompilerException e) { - context.addMessage(CompilerMessageCategory.ERROR, ExceptionUtil.getThrowableText(e), null, -1, -1); - LOGGER.info(e); + + @Override + public FileType[] getInputFileTypes() { + return new FileType[]{JavaFileType.INSTANCE}; + } + + @Override + public FileType[] getOutputFileTypes() { + return new FileType[]{JavaClassFileType.INSTANCE}; } - catch (CacheCorruptedException e) { - LOGGER.info(e); - context.requestRebuildNextTime(e.getMessage()); + + @Override + public boolean validateConfiguration(CompileScope scope) { + return getBackEndCompiler().checkCompiler(scope); } - } - - private static List filterResourceFiles(CompileContext compileContext, VirtualFile[] virtualFiles) { - ProjectFileIndex fileIndex = ProjectFileIndex.getInstance(compileContext.getProject()); - - List list = new ArrayList<>(virtualFiles.length); - for (VirtualFile file : virtualFiles) { - ContentFolderTypeProvider provider = fileIndex.getContentFolderTypeForFile(file); - if (provider == ProductionResourceContentFolderTypeProvider.getInstance() || provider == TestResourceContentFolderTypeProvider.getInstance()) { - continue; - } - list.add(file); + + @Override + public void registerCompilableFileTypes(Consumer fileTypeConsumer) { + fileTypeConsumer.accept(JavaFileType.INSTANCE); + } + + private BackendCompiler getBackEndCompiler() { + return JavaCompilerConfiguration.getInstance(myProject).getActiveCompiler(); } - return list; - } - - @Override - public FileType[] getInputFileTypes() { - return new FileType[]{JavaFileType.INSTANCE}; - } - - @Override - public FileType[] getOutputFileTypes() { - return new FileType[]{JavaClassFileType.INSTANCE}; - } - - @Override - public boolean validateConfiguration(CompileScope scope) { - return getBackEndCompiler().checkCompiler(scope); - } - - @Override - public void registerCompilableFileTypes(Consumer fileTypeConsumer) { - fileTypeConsumer.accept(JavaFileType.INSTANCE); - } - - private BackendCompiler getBackEndCompiler() { - return JavaCompilerConfiguration.getInstance(myProject).getActiveCompiler(); - } } diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/FilePathActionJavac.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/FilePathActionJavac.java index 65abdd5c1e..db77cee054 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/FilePathActionJavac.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/FilePathActionJavac.java @@ -30,63 +30,54 @@ /** * @author Eugene Zhuravlev - * Date: Sep 14, 2005 + * Date: Sep 14, 2005 */ -public class FilePathActionJavac extends JavacParserAction -{ - private static final Logger LOG = Logger.getInstance(FilePathActionJavac.class); - private static final Pattern ourPattern = Pattern.compile("^\\w+\\[(.+)\\]$", Pattern.CASE_INSENSITIVE); +public class FilePathActionJavac extends JavacParserAction { + private static final Logger LOG = Logger.getInstance(FilePathActionJavac.class); + private static final Pattern ourPattern = Pattern.compile("^\\w+\\[(.+)\\]$", Pattern.CASE_INSENSITIVE); - private final Matcher myJdk7FormatMatcher; + private final Matcher myJdk7FormatMatcher; - public FilePathActionJavac(final Matcher matcher) - { - super(matcher); - myJdk7FormatMatcher = ourPattern.matcher(""); - } + public FilePathActionJavac(Matcher matcher) { + super(matcher); + myJdk7FormatMatcher = ourPattern.matcher(""); + } - @Override - protected void doExecute(final String line, final String originalPath, final OutputParser.Callback callback) - { - if (LOG.isDebugEnabled()) - { - LOG.debug("Process parsing message: " + originalPath); - } + @Override + protected void doExecute(String line, String originalPath, OutputParser.Callback callback) { + if (LOG.isDebugEnabled()) { + LOG.debug("Process parsing message: " + originalPath); + } - String filePath = originalPath; - // for jdk7: cut off characters wrapping the path. e.g. "RegularFileObject[C:/tmp/bugs/src/a/Demo1.java]" - if (myJdk7FormatMatcher.reset(filePath).matches()) - { - filePath = myJdk7FormatMatcher.group(1); - } + String filePath = originalPath; + // for jdk7: cut off characters wrapping the path. e.g. "RegularFileObject[C:/tmp/bugs/src/a/Demo1.java]" + if (myJdk7FormatMatcher.reset(filePath).matches()) { + filePath = myJdk7FormatMatcher.group(1); + } - // jdk 9 specific - // C:\\Users\\VISTALL\\Documents\\Consulo\\untitled71\\out\\production\\untitled71:org\\example\\Main.class - int i = filePath.lastIndexOf(':'); - if (i != -1) - { - // check next character - if it slash, it's not module separator - char next = filePath.charAt(i + 1); - if (next != '\\' && next != '/') - { - char[] chars = filePath.toCharArray(); - chars[i] = '/'; - filePath = FileUtil.toSystemIndependentName(new String(chars)); - } - } + // jdk 9 specific + // C:\\Users\\VISTALL\\Documents\\Consulo\\untitled71\\out\\production\\untitled71:org\\example\\Main.class + int i = filePath.lastIndexOf(':'); + if (i != -1) { + // check next character - if it slash, it's not module separator + char next = filePath.charAt(i + 1); + if (next != '\\' && next != '/') { + char[] chars = filePath.toCharArray(); + chars[i] = '/'; + filePath = FileUtil.toSystemIndependentName(new String(chars)); + } + } - int index = filePath.lastIndexOf('/'); - final String name = index >= 0 ? filePath.substring(index + 1) : filePath; + int index = filePath.lastIndexOf('/'); + String name = index >= 0 ? filePath.substring(index + 1) : filePath; - CharSequence extension = FileUtil.getExtension((CharSequence) name); - if (Comparing.equal(extension, JavaFileType.INSTANCE.getDefaultExtension())) - { - callback.fileProcessed(filePath); - callback.setProgressText(CompilerLocalize.progressParsingFile(name).get()); - } - else if (Comparing.equal(extension, JavaClassFileType.INSTANCE.getDefaultExtension())) - { - callback.fileGenerated(new FileObject(new File(filePath))); - } - } + CharSequence extension = FileUtil.getExtension((CharSequence) name); + if (Comparing.equal(extension, JavaFileType.INSTANCE.getDefaultExtension())) { + callback.fileProcessed(filePath); + callback.setProgressText(CompilerLocalize.progressParsingFile(name)); + } + else if (Comparing.equal(extension, JavaClassFileType.INSTANCE.getDefaultExtension())) { + callback.fileGenerated(new FileObject(new File(filePath))); + } + } } diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java index c93eb9bc09..6015221fa8 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java @@ -32,111 +32,90 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class JavacOutputParser extends OutputParser implements JavacResourcesReaderConstants -{ - private final int myTabSize; - private String WARNING_PREFIX = "warning:"; // default value +public class JavacOutputParser extends OutputParser implements JavacResourcesReaderConstants { + private final int myTabSize; + private String WARNING_PREFIX = "warning:"; // default value - public JavacOutputParser(Project project) - { - myTabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(JavaFileType.INSTANCE); - if (project.getApplication().isUnitTestMode()) - { - // emulate patterns setup if 'embedded' javac is used (javac is started not via JavacRunner) - addJavacPattern(MSG_PARSING_STARTED + CATEGORY_VALUE_DIVIDER + "[parsing started {0}]"); - addJavacPattern(MSG_PARSING_COMPLETED + CATEGORY_VALUE_DIVIDER + "[parsing completed {0}ms]"); - addJavacPattern(MSG_LOADING + CATEGORY_VALUE_DIVIDER + "[loading {0}]"); - addJavacPattern(MSG_CHECKING + CATEGORY_VALUE_DIVIDER + "[checking {0}]"); - addJavacPattern(MSG_WROTE + CATEGORY_VALUE_DIVIDER + "[wrote {0}]"); - } - } + public JavacOutputParser(Project project) { + myTabSize = CodeStyleSettingsManager.getSettings(project).getTabSize(JavaFileType.INSTANCE); + if (project.getApplication().isUnitTestMode()) { + // emulate patterns setup if 'embedded' javac is used (javac is started not via JavacRunner) + addJavacPattern(MSG_PARSING_STARTED + CATEGORY_VALUE_DIVIDER + "[parsing started {0}]"); + addJavacPattern(MSG_PARSING_COMPLETED + CATEGORY_VALUE_DIVIDER + "[parsing completed {0}ms]"); + addJavacPattern(MSG_LOADING + CATEGORY_VALUE_DIVIDER + "[loading {0}]"); + addJavacPattern(MSG_CHECKING + CATEGORY_VALUE_DIVIDER + "[checking {0}]"); + addJavacPattern(MSG_WROTE + CATEGORY_VALUE_DIVIDER + "[wrote {0}]"); + } + } - @Override - public boolean processMessageLine(Callback callback) - { - if (super.processMessageLine(callback)) - { - return true; - } - final String line = callback.getCurrentLine(); - if (line == null) - { - return false; - } - if (MSG_PATTERNS_START.equals(line)) - { - myParserActions.clear(); - while (true) - { - final String patternLine = callback.getNextLine(); - if (MSG_PATTERNS_END.equals(patternLine)) - { - break; - } - addJavacPattern(patternLine); - } - return true; - } + @Override + public boolean processMessageLine(Callback callback) { + if (super.processMessageLine(callback)) { + return true; + } + String line = callback.getCurrentLine(); + if (line == null) { + return false; + } + if (MSG_PATTERNS_START.equals(line)) { + myParserActions.clear(); + while (true) { + String patternLine = callback.getNextLine(); + if (MSG_PATTERNS_END.equals(patternLine)) { + break; + } + addJavacPattern(patternLine); + } + return true; + } - int colonIndex1 = line.indexOf(':'); - if (colonIndex1 == 1) - { // drive letter - colonIndex1 = line.indexOf(':', colonIndex1 + 1); - } + int colonIndex1 = line.indexOf(':'); + if (colonIndex1 == 1) { // drive letter + colonIndex1 = line.indexOf(':', colonIndex1 + 1); + } - if (colonIndex1 >= 0) - { // looks like found something like file path - String part1 = line.substring(0, colonIndex1).trim(); - if (part1.equalsIgnoreCase("error") /*jikes*/ || part1.equalsIgnoreCase("Caused by")) - { - addMessage(callback, CompilerMessageCategory.ERROR, line.substring(colonIndex1)); - return true; - } - if (part1.equalsIgnoreCase("warning")) - { - addMessage(callback, CompilerMessageCategory.WARNING, line.substring(colonIndex1)); - return true; - } - if (part1.equals("javac")) - { - addMessage(callback, CompilerMessageCategory.ERROR, line); - return true; - } + if (colonIndex1 >= 0) { // looks like found something like file path + String part1 = line.substring(0, colonIndex1).trim(); + if (part1.equalsIgnoreCase("error") /*jikes*/ || part1.equalsIgnoreCase("Caused by")) { + callback.newError(line.substring(colonIndex1)).add(); + return true; + } + if (part1.equalsIgnoreCase("warning")) { + callback.newWarning(line.substring(colonIndex1)).add(); + return true; + } + if (part1.equals("javac")) { + callback.newError(line).add(); + return true; + } - final int colonIndex2 = line.indexOf(':', colonIndex1 + 1); - if (colonIndex2 >= 0) - { - final String filePath = part1.replace(File.separatorChar, '/'); - if (!new File(filePath).exists()) - { - // the part one turned out to be something else than a file path - return true; - } - try - { - final int lineNum = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim()); - String message = line.substring(colonIndex2 + 1).trim(); - CompilerMessageCategory category = CompilerMessageCategory.ERROR; - if (message.startsWith(WARNING_PREFIX)) - { - message = message.substring(WARNING_PREFIX.length()).trim(); - category = CompilerMessageCategory.WARNING; - } + int colonIndex2 = line.indexOf(':', colonIndex1 + 1); + if (colonIndex2 >= 0) { + String filePath = part1.replace(File.separatorChar, '/'); + if (!new File(filePath).exists()) { + // the part one turned out to be something else than a file path + return true; + } + try { + int lineNum = Integer.parseInt(line.substring(colonIndex1 + 1, colonIndex2).trim()); + String message = line.substring(colonIndex2 + 1).trim(); + CompilerMessageCategory category = CompilerMessageCategory.ERROR; + if (message.startsWith(WARNING_PREFIX)) { + message = message.substring(WARNING_PREFIX.length()).trim(); + category = CompilerMessageCategory.WARNING; + } - List messages = new ArrayList<>(); - messages.add(message); - int colNum = 0; - String prevLine = null; - do - { - final String nextLine = callback.getNextLine(); - if (nextLine == null) - { - return false; - } - if (nextLine.trim().equals("^")) - { - // wtf is that? why use editorColumNumber + List messages = new ArrayList<>(); + messages.add(message); + int colNum = 0; + String prevLine = null; + do { + String nextLine = callback.getNextLine(); + if (nextLine == null) { + return false; + } + if (nextLine.trim().equals("^")) { + // wtf is that? why use editorColumnNumber // final CharSequence chars = prevLine == null ? line : prevLine; // final int offset = Math.max(0, Math.min(chars.length(), nextLine.indexOf('^'))); // colNum = EditorUtil.calcColumnNumber(null, chars, 0, offset, myTabSize); @@ -150,178 +129,146 @@ public boolean processMessageLine(Callback callback) // { // callback.pushBack(messageEnd); // } - break; - } - if (prevLine != null) - { - messages.add(prevLine); - } - prevLine = nextLine; - } - while (true); + break; + } + if (prevLine != null) { + messages.add(prevLine); + } + prevLine = nextLine; + } + while (true); - if (colNum >= 0) - { - messages = convertMessages(messages); - final StringBuilder buf = new StringBuilder(); - for (final String m : messages) - { - if (buf.length() > 0) - { - buf.append("\n"); - } - buf.append(m); - } - addMessage(callback, category, buf.toString(), VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath), lineNum, colNum + 1); - return true; - } - } - catch (NumberFormatException ignored) - { - } - } - } + if (colNum >= 0) { + messages = convertMessages(messages); + StringBuilder buf = new StringBuilder(); + for (String m : messages) { + if (buf.length() > 0) { + buf.append("\n"); + } + buf.append(m); + } + callback.newMessage(category, buf.toString()) + .url(VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath)) + .position(lineNum, colNum + 1) + .add(); + return true; + } + } + catch (NumberFormatException ignored) { + } + } + } - if (line.endsWith("java.lang.OutOfMemoryError")) - { - addMessage(callback, CompilerMessageCategory.ERROR, CompilerLocalize.errorJavacOutOfMemory().get()); - return true; - } + if (line.endsWith("java.lang.OutOfMemoryError")) { + callback.newError(CompilerLocalize.errorJavacOutOfMemory()); + return true; + } - addMessage(callback, CompilerMessageCategory.INFORMATION, line); - return true; - } + callback.newInfo(line).add(); + return true; + } - private static boolean isMessageEnd(String line) - { - return line != null && line.length() > 0 && Character.isWhitespace(line.charAt(0)); - } + private static boolean isMessageEnd(String line) { + return line != null && line.length() > 0 && Character.isWhitespace(line.charAt(0)); + } + private static List convertMessages(List messages) { + if (messages.size() <= 1) { + return messages; + } + String line0 = messages.get(0); + String line1 = messages.get(1); + int colonIndex = line1.indexOf(':'); + if (colonIndex > 0) { + String part1 = line1.substring(0, colonIndex).trim(); + // jikes + if ("symbol".equals(part1)) { + String symbol = line1.substring(colonIndex + 1).trim(); + messages.remove(1); + if (messages.size() >= 2) { + messages.remove(1); + } + messages.set(0, line0 + " " + symbol); + } + } + return messages; + } - private static List convertMessages(List messages) - { - if (messages.size() <= 1) - { - return messages; - } - final String line0 = messages.get(0); - final String line1 = messages.get(1); - final int colonIndex = line1.indexOf(':'); - if (colonIndex > 0) - { - String part1 = line1.substring(0, colonIndex).trim(); - // jikes - if ("symbol".equals(part1)) - { - String symbol = line1.substring(colonIndex + 1).trim(); - messages.remove(1); - if (messages.size() >= 2) - { - messages.remove(1); - } - messages.set(0, line0 + " " + symbol); - } - } - return messages; - } + private void addJavacPattern(String line) { + int dividerIndex = line.indexOf(CATEGORY_VALUE_DIVIDER); + if (dividerIndex < 0) { + // by reports it may happen for some IBM JDKs (empty string?) + return; + } + String category = line.substring(0, dividerIndex); + final String resourceBundleValue = line.substring(dividerIndex + 1); + if (MSG_PARSING_COMPLETED.equals(category) || MSG_PARSING_STARTED.equals(category) || MSG_WROTE.equals(category)) { + myParserActions.add(new FilePathActionJavac(createMatcher(resourceBundleValue))); + } + else if (MSG_CHECKING.equals(category)) { + myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) { + @Override + protected void doExecute(String line, String parsedData, Callback callback) { + callback.setProgressText(CompilerLocalize.progressCompilingClass(parsedData)); + } + }); + } + else if (MSG_LOADING.equals(category)) { + myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) { + @Override + protected void doExecute(String line, @Nullable String parsedData, Callback callback) { + callback.setProgressText(CompilerLocalize.progressLoadingClasses()); + } + }); + } + else if (MSG_NOTE.equals(category)) { + myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) { + @Override + protected void doExecute(String line, @Nullable String filePath, Callback callback) { + boolean fileExists = filePath != null && new File(filePath).exists(); + if (fileExists) { + callback.newWarning(line) + .url(VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath)) + .add(); + } + else { + callback.newInfo(line).add(); + } + } + }); + } + else if (MSG_WARNING.equals(category)) { + WARNING_PREFIX = resourceBundleValue; + } + else if (MSG_STATISTICS.equals(category)) { + myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) { + @Override + protected void doExecute(String line, @Nullable String parsedData, Callback callback) { + // empty + } + }); + } + else if (MSG_IGNORED.equals(category)) { + myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) { + @Override + protected void doExecute(String line, @Nullable String parsedData, Callback callback) { + // ignored + } + }); + } + } - private void addJavacPattern(final String line) - { - final int dividerIndex = line.indexOf(CATEGORY_VALUE_DIVIDER); - if (dividerIndex < 0) - { - // by reports it may happen for some IBM JDKs (empty string?) - return; - } - final String category = line.substring(0, dividerIndex); - final String resourceBundleValue = line.substring(dividerIndex + 1); - if (MSG_PARSING_COMPLETED.equals(category) || MSG_PARSING_STARTED.equals(category) || MSG_WROTE.equals(category)) - { - myParserActions.add(new FilePathActionJavac(createMatcher(resourceBundleValue))); - } - else if (MSG_CHECKING.equals(category)) - { - myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) - { - @Override - protected void doExecute(final String line, String parsedData, final Callback callback) - { - callback.setProgressText(CompilerLocalize.progressCompilingClass(parsedData).get()); - } - }); - } - else if (MSG_LOADING.equals(category)) - { - myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) - { - @Override - protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) - { - callback.setProgressText(CompilerLocalize.progressLoadingClasses().get()); - } - }); - } - else if (MSG_NOTE.equals(category)) - { - myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) - { - @Override - protected void doExecute(final String line, @Nullable final String filePath, final Callback callback) - { - final boolean fileExists = filePath != null && new File(filePath).exists(); - if (fileExists) - { - addMessage(callback, CompilerMessageCategory.WARNING, line, VirtualFileManager.constructUrl(LocalFileSystem.PROTOCOL, filePath), -1, -1); - } - else - { - addMessage(callback, CompilerMessageCategory.INFORMATION, line); - } - } - }); - } - else if (MSG_WARNING.equals(category)) - { - WARNING_PREFIX = resourceBundleValue; - } - else if (MSG_STATISTICS.equals(category)) - { - myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) - { - @Override - protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) - { - // empty - } - }); - } - else if (MSG_IGNORED.equals(category)) - { - myParserActions.add(new JavacParserAction(createMatcher(resourceBundleValue)) - { - @Override - protected void doExecute(final String line, @Nullable String parsedData, final Callback callback) - { - // ignored - } - }); - } - } + /** + * made public for Tests, do not use this method directly + */ + public static Matcher createMatcher(String resourceBundleValue) { + String regexp = resourceBundleValue.replaceAll("([\\[\\]\\(\\)\\.\\*])", "\\\\$1"); + regexp = regexp.replaceAll("\\{\\d+\\}", "(.+)"); + return Pattern.compile(regexp, Pattern.CASE_INSENSITIVE).matcher(""); + } - - /** - * made public for Tests, do not use this method directly - */ - public static Matcher createMatcher(final String resourceBundleValue) - { - String regexp = resourceBundleValue.replaceAll("([\\[\\]\\(\\)\\.\\*])", "\\\\$1"); - regexp = regexp.replaceAll("\\{\\d+\\}", "(.+)"); - return Pattern.compile(regexp, Pattern.CASE_INSENSITIVE).matcher(""); - } - - @Override - public boolean isTrimLines() - { - return false; - } + @Override + public boolean isTrimLines() { + return false; + } } diff --git a/java-compiler-impl/src/main/java/consulo/java/compiler/bytecodeProcessing/JavaBytecodeProcessorCompiler.java b/java-compiler-impl/src/main/java/consulo/java/compiler/bytecodeProcessing/JavaBytecodeProcessorCompiler.java index c8ca8f927f..f76edb920f 100644 --- a/java-compiler-impl/src/main/java/consulo/java/compiler/bytecodeProcessing/JavaBytecodeProcessorCompiler.java +++ b/java-compiler-impl/src/main/java/consulo/java/compiler/bytecodeProcessing/JavaBytecodeProcessorCompiler.java @@ -31,6 +31,7 @@ import consulo.language.content.ProductionContentFolderTypeProvider; import consulo.language.content.TestContentFolderTypeProvider; import consulo.language.util.ModuleUtilCore; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.module.Module; import consulo.util.collection.Chunk; @@ -39,6 +40,7 @@ import consulo.virtualFileSystem.util.VirtualFileUtil; import org.jspecify.annotations.Nullable; + import java.io.DataInput; import java.io.File; import java.io.IOException; @@ -51,170 +53,174 @@ */ @ExtensionImpl(id = "java-bytecode-processor") public class JavaBytecodeProcessorCompiler implements ClassInstrumentingCompiler { - private static final Logger LOGGER = Logger.getInstance(JavaBytecodeProcessorCompiler.class); - - private static class MyProcessingItem implements ProcessingItem { - private final File myFile; - private final FileObject myFileObject; - private final Module myModule; - private ValidityState myValidityState; - - public MyProcessingItem(File file, FileObject fileObject, Module module) { - myFile = file; - myFileObject = fileObject; - myModule = module; - myValidityState = new TimestampValidityState(file.lastModified()); - } + private static final Logger LOGGER = Logger.getInstance(JavaBytecodeProcessorCompiler.class); + + private static class MyProcessingItem implements ProcessingItem { + private final File myFile; + private final FileObject myFileObject; + private final Module myModule; + private ValidityState myValidityState; + + public MyProcessingItem(File file, FileObject fileObject, Module module) { + myFile = file; + myFileObject = fileObject; + myModule = module; + myValidityState = new TimestampValidityState(file.lastModified()); + } - private void save(byte[] data) throws IOException { - myFileObject.save(data); - myValidityState = new TimestampValidityState(myFile.lastModified()); - } + private void save(byte[] data) throws IOException { + myFileObject.save(data); + myValidityState = new TimestampValidityState(myFile.lastModified()); + } - @Override - public File getFile() { - return myFile; - } + @Override + public File getFile() { + return myFile; + } - @Nullable - @Override - public ValidityState getValidityState() { - return myValidityState; - } - } - - @Override - public ProcessingItem[] getProcessingItems(CompileContext compileContext) { - List list = new LinkedList<>(); - Module[] affectedModules = compileContext.getCompileScope().getAffectedModules(); - for (Module affectedModule : affectedModules) { - scanDirectory(compileContext, affectedModule, ProductionContentFolderTypeProvider.getInstance(), list); - scanDirectory(compileContext, affectedModule, TestContentFolderTypeProvider.getInstance(), list); - } - return list.toArray(new ProcessingItem[list.size()]); - } - - private static void scanDirectory(CompileContext compileContext, - Module module, - ContentFolderTypeProvider provider, - List list) { - Map map = compileContext.getUserData(JavaCompiler.ourOutputFileParseInfo); - if (map == null) { - return; + @Nullable + @Override + public ValidityState getValidityState() { + return myValidityState; + } } - JavaModuleExtension extension = ModuleUtilCore.getExtension(module, JavaModuleExtension.class); - if (extension == null) { - return; + @Override + public ProcessingItem[] getProcessingItems(CompileContext compileContext) { + List list = new LinkedList<>(); + Module[] affectedModules = compileContext.getCompileScope().getAffectedModules(); + for (Module affectedModule : affectedModules) { + scanDirectory(compileContext, affectedModule, ProductionContentFolderTypeProvider.getInstance(), list); + scanDirectory(compileContext, affectedModule, TestContentFolderTypeProvider.getInstance(), list); + } + return list.toArray(new ProcessingItem[list.size()]); } - String compilerOutputUrl = ModuleCompilerPathsManager.getInstance(module).getCompilerOutputUrl(provider); - if (compilerOutputUrl == null) { - return; - } + private static void scanDirectory( + CompileContext compileContext, + Module module, + ContentFolderTypeProvider provider, + List list + ) { + Map map = compileContext.getUserData(JavaCompiler.ourOutputFileParseInfo); + if (map == null) { + return; + } - String path = VirtualFileUtil.urlToPath(compilerOutputUrl); - File outputDir = new File(path); + JavaModuleExtension extension = ModuleUtilCore.getExtension(module, JavaModuleExtension.class); + if (extension == null) { + return; + } - FileUtil.visitFiles(outputDir, file -> { - FileObject fileObject = map.get(file); - if (fileObject == null) { - return true; - } + String compilerOutputUrl = ModuleCompilerPathsManager.getInstance(module).getCompilerOutputUrl(provider); + if (compilerOutputUrl == null) { + return; + } - list.add(new MyProcessingItem(file, fileObject, module)); - return true; - }); - } + String path = VirtualFileUtil.urlToPath(compilerOutputUrl); + File outputDir = new File(path); - @Override - public ProcessingItem[] process(CompileContext compileContext, ProcessingItem[] processingItems) { - JavaDependencyCache dependencyCache = ((CompileContextEx)compileContext).getDependencyCache().findChild(JavaDependencyCache.class); + FileUtil.visitFiles(outputDir, file -> { + FileObject fileObject = map.get(file); + if (fileObject == null) { + return true; + } - final Cache cache; - try { - cache = dependencyCache.getCache(); - } - catch (CacheCorruptedException e) { - compileContext.addMessage(CompilerMessageCategory.ERROR, "Cache corrupted. Please rebuild project", null, -1, -1); - return ProcessingItem.EMPTY_ARRAY; + list.add(new MyProcessingItem(file, fileObject, module)); + return true; + }); } - Module[] affectedModules = compileContext.getCompileScope().getAffectedModules(); - for (Module affectedModule : affectedModules) { - InstrumentationClassFinder classFinder = createClassFinder(compileContext, affectedModule); - - for (ProcessingItem processingItem : processingItems) { - MyProcessingItem temp = (MyProcessingItem)processingItem; - - if (temp.myModule != affectedModule) { - continue; - } + @Override + public ProcessingItem[] process(CompileContext compileContext, ProcessingItem[] processingItems) { + JavaDependencyCache dependencyCache = ((CompileContextEx) compileContext).getDependencyCache().findChild(JavaDependencyCache.class); + Cache cache; try { - FileObject fileObject = temp.myFileObject; - for (JavaBytecodeProcessor processor : Application.get().getExtensionList(JavaBytecodeProcessor.class)) { - byte[] bytes = processor.processClassFile(compileContext, - affectedModule, - dependencyCache, - cache, - fileObject.getClassId(), - temp.myFile, - fileObject::getOrLoadContent, - classFinder); - if (bytes != null) { - temp.save(bytes); - } - } + cache = dependencyCache.getCache(); } catch (CacheCorruptedException e) { - compileContext.addMessage(CompilerMessageCategory.ERROR, "Cache corrupted. Please rebuild project", null, -1, -1); - return ProcessingItem.EMPTY_ARRAY; + compileContext.newError(LocalizeValue.localizeTODO("Cache corrupted. Please rebuild project")).add(); + return ProcessingItem.EMPTY_ARRAY; } - catch (IOException e) { - LOGGER.error(e); + + Module[] affectedModules = compileContext.getCompileScope().getAffectedModules(); + for (Module affectedModule : affectedModules) { + InstrumentationClassFinder classFinder = createClassFinder(compileContext, affectedModule); + + for (ProcessingItem processingItem : processingItems) { + MyProcessingItem temp = (MyProcessingItem) processingItem; + + if (temp.myModule != affectedModule) { + continue; + } + + try { + FileObject fileObject = temp.myFileObject; + for (JavaBytecodeProcessor processor : Application.get().getExtensionList(JavaBytecodeProcessor.class)) { + byte[] bytes = processor.processClassFile( + compileContext, + affectedModule, + dependencyCache, + cache, + fileObject.getClassId(), + temp.myFile, + fileObject::getOrLoadContent, + classFinder + ); + if (bytes != null) { + temp.save(bytes); + } + } + } + catch (CacheCorruptedException e) { + compileContext.newError(LocalizeValue.localizeTODO("Cache corrupted. Please rebuild project")).add(); + return ProcessingItem.EMPTY_ARRAY; + } + catch (IOException e) { + LOGGER.error(e); + } + } } - } + return processingItems; + } + + public static InstrumentationClassFinder createClassFinder(CompileContext context, Module module) { + ModuleChunk moduleChunk = + new ModuleChunk((CompileContextEx) context, new Chunk<>(module), Collections.>emptyMap()); + + Set compilationBootClasspath = JavaCompilerUtil.getCompilationBootClasspath(context, moduleChunk); + Set compilationClasspath = JavaCompilerUtil.getCompilationClasspath(context, moduleChunk); + + return new InstrumentationClassFinder(toUrls(compilationBootClasspath), toUrls(compilationClasspath)); } - return processingItems; - } - - public static InstrumentationClassFinder createClassFinder(CompileContext context, final Module module) { - ModuleChunk moduleChunk = - new ModuleChunk((CompileContextEx)context, new Chunk<>(module), Collections.>emptyMap()); - - Set compilationBootClasspath = JavaCompilerUtil.getCompilationBootClasspath(context, moduleChunk); - Set compilationClasspath = JavaCompilerUtil.getCompilationClasspath(context, moduleChunk); - - return new InstrumentationClassFinder(toUrls(compilationBootClasspath), toUrls(compilationClasspath)); - } - - private static URL[] toUrls(Set files) { - List urls = new ArrayList<>(files.size()); - for (VirtualFile file : files) { - try { - File javaFile = VirtualFileUtil.virtualToIoFile(file); - urls.add(javaFile.getCanonicalFile().toURI().toURL()); - } - catch (Exception e) { - LOGGER.error(e); - } + + private static URL[] toUrls(Set files) { + List urls = new ArrayList<>(files.size()); + for (VirtualFile file : files) { + try { + File javaFile = VirtualFileUtil.virtualToIoFile(file); + urls.add(javaFile.getCanonicalFile().toURI().toURL()); + } + catch (Exception e) { + LOGGER.error(e); + } + } + return urls.toArray(new URL[urls.size()]); + } + + @Override + public String getDescription() { + return "JavaBytecodeCompiler"; + } + + @Override + public boolean validateConfiguration(CompileScope compileScope) { + return true; + } + + @Override + public ValidityState createValidityState(DataInput dataInput) throws IOException { + return TimestampValidityState.load(dataInput); } - return urls.toArray(new URL[urls.size()]); - } - - @Override - public String getDescription() { - return "JavaBytecodeCompiler"; - } - - @Override - public boolean validateConfiguration(CompileScope compileScope) { - return true; - } - - @Override - public ValidityState createValidityState(DataInput dataInput) throws IOException { - return TimestampValidityState.load(dataInput); - } } diff --git a/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java b/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java index 6873c48b27..07f9386e02 100644 --- a/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java +++ b/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java @@ -5,9 +5,10 @@ import consulo.application.util.concurrent.AppExecutorUtil; import consulo.compiler.CacheCorruptedException; import consulo.compiler.CompileContext; -import consulo.compiler.CompilerMessageCategory; import consulo.compiler.localize.CompilerLocalize; import consulo.java.rt.common.compiler.JavaCompilerInterface; +import consulo.localize.LocalizeValue; +import consulo.navigation.Navigatable; import consulo.process.ProcessHandler; import consulo.process.event.ProcessAdapter; import consulo.process.event.ProcessEvent; @@ -29,155 +30,185 @@ /** * @author VISTALL - * @since 13/03/2021 + * @since 2021-03-13 */ public class JavaToolMonitor implements BackendCompilerMonitor, JavaCompilerInterface.Iface { - private final TSimpleServer myServer; + private final TSimpleServer myServer; - private final CompileContext myCompileContext; + private final CompileContext myCompileContext; - private final BackendCompilerWrapper.ClassParsingHandler myClassParsingHandler; - private final Future myClassParsingFuture; + private final BackendCompilerWrapper.ClassParsingHandler myClassParsingHandler; + private final Future myClassParsingFuture; - private Path myProjectFilePath; - private ProcessHandler myProcess; + private Path myProjectFilePath; + private ProcessHandler myProcess; - public JavaToolMonitor(NewBackendCompilerProcessBuilder processBuilder) { - myCompileContext = processBuilder.getCompileContext(); + public JavaToolMonitor(NewBackendCompilerProcessBuilder processBuilder) { + myCompileContext = processBuilder.getCompileContext(); - myProjectFilePath = Paths.get(myCompileContext.getProject().getBasePath()); + myProjectFilePath = Paths.get(myCompileContext.getProject().getBasePath()); - myClassParsingHandler = myCompileContext.getUserData(BackendCompilerWrapper.CLASS_PARSING_HANDLER_KEY); + myClassParsingHandler = myCompileContext.getUserData(BackendCompilerWrapper.CLASS_PARSING_HANDLER_KEY); - assert myClassParsingHandler != null; + assert myClassParsingHandler != null; - TServerSocket localhost = null; - try { - localhost = new TServerSocket(new InetSocketAddress("localhost", processBuilder.getPort())); - } - catch (TTransportException e) { - throw new IllegalArgumentException(e); - } + TServerSocket localhost; + try { + localhost = new TServerSocket(new InetSocketAddress("localhost", processBuilder.getPort())); + } + catch (TTransportException e) { + throw new IllegalArgumentException(e); + } - JavaCompilerInterface.Processor processor = new JavaCompilerInterface.Processor<>(this); + JavaCompilerInterface.Processor processor = new JavaCompilerInterface.Processor<>(this); - myServer = new TSimpleServer(new TServer.Args(localhost).processor(processor)); + myServer = new TSimpleServer(new TServer.Args(localhost).processor(processor)); - myClassParsingFuture = AppExecutorUtil.getAppExecutorService().submit(myClassParsingHandler); + myClassParsingFuture = AppExecutorUtil.getAppExecutorService().submit(myClassParsingHandler); - AppExecutorUtil.getAppExecutorService().execute(myServer::serve); - } + AppExecutorUtil.getAppExecutorService().execute(myServer::serve); + } - @Override - public void handleProcessStart(ProcessHandler process) { - myProcess = process; - process.addProcessListener(new ProcessAdapter() { - @Override - public void onTextAvailable(ProcessEvent event, Key outputType) { - String text = event.getText().trim(); - if (text.startsWith("SLF4J")) { - return; - } + @Override + public void handleProcessStart(ProcessHandler process) { + myProcess = process; + process.addProcessListener(new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + String text = event.getText().trim(); + if (text.startsWith("SLF4J")) { + return; + } + + if (text.isEmpty()) { + return; + } + + if (text.startsWith("java.lang.OutOfMemoryError")) { + myCompileContext.newError(CompilerLocalize.errorJavacOutOfMemory()).add(); + return; + } + + if (text.startsWith("Error:")) { + myCompileContext.newError(LocalizeValue.of(text)).add(); + return; + } + + myCompileContext.newInfo(LocalizeValue.of(text)).add(); + } + }); + } - if (text.isEmpty()) { - return; - } + @Override + public void dispose() { + myClassParsingHandler.stopParsing(); - if (text.startsWith("java.lang.OutOfMemoryError")) { - log(CompilerMessageCategory.ERROR, CompilerLocalize.errorJavacOutOfMemory().get(), null, -1, -1); - return; + if (myClassParsingFuture != null) { + myClassParsingFuture.cancel(false); } - if (text.startsWith("Error:")) { - log(CompilerMessageCategory.ERROR, text, null, -1, 1); - return; + if (myServer != null) { + myServer.stop(); } - - log(CompilerMessageCategory.INFORMATION, text, null, -1, 1); - } - }); - } - - @Override - public void dispose() { - myClassParsingHandler.stopParsing(); - - if (myClassParsingFuture != null) { - myClassParsingFuture.cancel(false); } - if (myServer != null) { - myServer.stop(); + @Override + public void logInfo(String message, String fileUri, long lineNumber, long columnNumber) throws TException { + new MessageBuilderWrapper(myCompileContext.newInfo(LocalizeValue.of(message))) + .url(fileUri) + .position((int) lineNumber, (int) columnNumber) + .add(); } - } - - @Override - public void logInfo(String message, String fileUri, long lineNumber, long columnNumber) throws TException { - log(CompilerMessageCategory.INFORMATION, message, fileUri, lineNumber, columnNumber); - } - - @Override - public void logError(String message, String fileUri, long lineNumber, long columnNumber) throws TException { - log(CompilerMessageCategory.ERROR, message, fileUri, lineNumber, columnNumber); - } - - @Override - public void logWarning(String message, String fileUri, long lineNumber, long columnNumber) throws TException { - log(CompilerMessageCategory.WARNING, message, fileUri, lineNumber, columnNumber); - } - - @Override - public void fileWrote(String filePath) throws TException { - try { - myClassParsingHandler.addPath(new FileObject(new File(filePath))); - } - catch (CacheCorruptedException e) { - if (myProcess != null) { - myProcess.destroyProcess(); - } + + @Override + public void logError(String message, String fileUri, long lineNumber, long columnNumber) throws TException { + new MessageBuilderWrapper(myCompileContext.newError(LocalizeValue.of(message))) + .url(fileUri) + .position((int) lineNumber, (int) columnNumber) + .add(); } - } - - // public void parsingFileStarted(String fileUri) throws TException - // { - // String filePath = null; - // try - // { - // URI uri = new URI(fileUri); - // Path path = Paths.get(uri).toAbsolutePath(); - // - // Path relativize = myProjectFilePath.relativize(path); - // if (relativize == null) - // { - // filePath = path.toString(); - // } - // else - // { - // filePath = relativize.toString(); - // } - // } - // catch (Exception ignored) - // { - // } - // - // if (filePath != null) - // { - // myCompileContext.getProgressIndicator().setText(CompilerBundle.message("progress.parsing.file", filePath)); - // } - // } - - private void log(CompilerMessageCategory category, String message, String fileUri, long lineNumber, long columnNumber) { - String fileUrl = null; - try { - URI uri = new URI(fileUri); - VirtualFile fileByURL = VirtualFileUtil.findFileByURL(uri.toURL()); - if (fileByURL != null) { - fileUrl = fileByURL.getUrl(); - } + + @Override + public void logWarning(String message, String fileUri, long lineNumber, long columnNumber) throws TException { + new MessageBuilderWrapper(myCompileContext.newWarning(LocalizeValue.of(message))) + .url(fileUri) + .position((int) lineNumber, (int) columnNumber) + .add(); } - catch (Exception ignored) { + + @Override + public void fileWrote(String filePath) throws TException { + try { + myClassParsingHandler.addPath(new FileObject(new File(filePath))); + } + catch (CacheCorruptedException e) { + if (myProcess != null) { + myProcess.destroyProcess(); + } + } } - myCompileContext.addMessage(category, message, fileUrl, (int)lineNumber, (int)columnNumber); - } + // public void parsingFileStarted(String fileUri) throws TException + // { + // String filePath = null; + // try + // { + // URI uri = new URI(fileUri); + // Path path = Paths.get(uri).toAbsolutePath(); + // + // Path relativize = myProjectFilePath.relativize(path); + // if (relativize == null) + // { + // filePath = path.toString(); + // } + // else + // { + // filePath = relativize.toString(); + // } + // } + // catch (Exception ignored) + // { + // } + // + // if (filePath != null) + // { + // myCompileContext.getProgressIndicator().setText(CompilerBundle.message("progress.parsing.file", filePath)); + // } + // } + + private class MessageBuilderWrapper implements CompileContext.MessageBuilder { + private final CompileContext.MessageBuilder myDelegate; + + private MessageBuilderWrapper(CompileContext.MessageBuilder delegate) { + myDelegate = delegate; + } + + @Override + public CompileContext.MessageBuilder url(String url) { + try { + VirtualFile fileByURL = VirtualFileUtil.findFileByURL(new URI(url).toURL()); + if (fileByURL != null) { + return url(fileByURL.getUrl()); + } + } + catch (Exception ignored) { + } + return this; + } + + @Override + public CompileContext.MessageBuilder position(int line, int column) { + return myDelegate.position(line, column); + } + + @Override + public CompileContext.MessageBuilder navigatable(Navigatable navigatable) { + return myDelegate.navigatable(navigatable); + } + + @Override + public void add() { + myDelegate.add(); + } + } } diff --git a/java-intellilang/src/main/java/consulo/java/impl/intelliLang/pattern/compiler/AnnotationBasedInstrumentingCompiler.java b/java-intellilang/src/main/java/consulo/java/impl/intelliLang/pattern/compiler/AnnotationBasedInstrumentingCompiler.java index 0bcd5a8b53..ff74ba18fc 100644 --- a/java-intellilang/src/main/java/consulo/java/impl/intelliLang/pattern/compiler/AnnotationBasedInstrumentingCompiler.java +++ b/java-intellilang/src/main/java/consulo/java/impl/intelliLang/pattern/compiler/AnnotationBasedInstrumentingCompiler.java @@ -22,10 +22,13 @@ import com.intellij.java.language.psi.JavaPsiFacade; import com.intellij.java.language.psi.PsiClass; import com.intellij.java.language.psi.PsiJavaFile; -import consulo.application.ApplicationManager; +import consulo.annotation.access.RequiredReadAction; +import consulo.application.Application; import consulo.application.progress.ProgressIndicator; -import consulo.application.util.function.Processor; -import consulo.compiler.*; +import consulo.compiler.ClassInstrumentingCompiler; +import consulo.compiler.CompileContext; +import consulo.compiler.ModuleCompilerPathsManager; +import consulo.compiler.ValidityState; import consulo.compiler.scope.CompileScope; import consulo.content.bundle.Sdk; import consulo.internal.org.objectweb.asm.ClassReader; @@ -33,10 +36,10 @@ import consulo.java.language.bundle.JavaSdkTypeUtil; import consulo.java.language.module.extension.JavaModuleExtension; import consulo.language.content.ProductionContentFolderTypeProvider; -import consulo.language.psi.PsiFile; import consulo.language.psi.scope.GlobalSearchScope; import consulo.language.psi.search.PsiSearchHelper; import consulo.language.util.ModuleUtilCore; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.module.Module; import consulo.module.content.ProjectFileIndex; @@ -52,166 +55,168 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; /** * Based on NotNullVerifyingCompiler, kindly provided by JetBrains for reference. */ public abstract class AnnotationBasedInstrumentingCompiler implements ClassInstrumentingCompiler { - private static final Logger LOG = Logger.getInstance("org.intellij.lang.pattern.compiler.AnnotationBasedInstrumentingCompiler"); + private static final Logger LOG = Logger.getInstance("org.intellij.lang.pattern.compiler.AnnotationBasedInstrumentingCompiler"); - public AnnotationBasedInstrumentingCompiler() { - } - - public ProcessingItem[] getProcessingItems(final CompileContext context) { - if (!isEnabled()) { - return ProcessingItem.EMPTY_ARRAY; + public AnnotationBasedInstrumentingCompiler() { } - final Project project = context.getProject(); - final Set result = new HashSet(); - final PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(context.getProject()); - - DumbService.getInstance(project).waitForSmartMode(); - - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - final String[] names = getAnnotationNames(project); - for (String name : names) { - final GlobalSearchScope scope = GlobalSearchScope.projectScope(project); - - final PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(name, GlobalSearchScope.allScope(project)); - if (psiClass == null) { - context.addMessage(CompilerMessageCategory.ERROR, "Cannot find class " + name, null, -1, -1); - continue; - } - - // wow, this is a sweet trick... ;) - searchHelper.processAllFilesWithWord(StringUtil.getShortName(name), scope, new Processor() { - public boolean process(PsiFile psifile) { - if (JavaLanguage.INSTANCE == psifile.getLanguage() && psifile.getVirtualFile() != null && psifile instanceof PsiJavaFile) { - addClassFiles((PsiJavaFile)psifile, result, project); - } - return true; - } - }, true); + @Override + public ProcessingItem[] getProcessingItems(CompileContext context) { + if (!isEnabled()) { + return ProcessingItem.EMPTY_ARRAY; } - } - }); - return result.toArray(new ProcessingItem[result.size()]); - } - - private static void addClassFiles(PsiJavaFile srcFile, Set result, final Project project) { - - final VirtualFile sourceFile = srcFile.getVirtualFile(); - assert sourceFile != null; - - final ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); - final Module module = index.getModuleForFile(sourceFile); - if (module != null) { - final Sdk jdk = ModuleUtilCore.getSdk(module, JavaModuleExtension.class); - final boolean jdk6 = jdk != null && JavaSdkTypeUtil.isOfVersionOrHigher(jdk, JavaSdkVersion.JDK_1_6); - - final ModuleCompilerPathsManager compilerPathsManager = ModuleCompilerPathsManager.getInstance(module); - final VirtualFile compilerOutputPath = compilerPathsManager.getCompilerOutput(ProductionContentFolderTypeProvider.getInstance()); - if (compilerOutputPath != null) { - final String packageName = srcFile.getPackageName(); - final VirtualFile packageDir = - packageName.length() > 0 ? compilerOutputPath.findFileByRelativePath(packageName.replace('.', '/')) : compilerOutputPath; - - if (packageDir != null && packageDir.isDirectory()) { - final PsiClass[] classes = srcFile.getClasses(); - final VirtualFile[] children = packageDir.getChildren(); - for (VirtualFile classFile : children) { - if (classFile.isDirectory() || !"class".equals(classFile.getExtension())) { - // no point in looking at directories or non-class files - continue; - } - final String name = classFile.getName(); - for (PsiClass clazz : classes) { - final String className = clazz.getName(); - if (className != null && name.startsWith(className)) { - result.add(new InstrumentationItem(classFile, jdk6)); - } + + Project project = context.getProject(); + Set result = new HashSet<>(); + PsiSearchHelper searchHelper = PsiSearchHelper.SERVICE.getInstance(context.getProject()); + + DumbService.getInstance(project).waitForSmartMode(); + + Application.get().runReadAction(() -> { + String[] names = getAnnotationNames(project); + for (String name : names) { + GlobalSearchScope scope = GlobalSearchScope.projectScope(project); + + PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(name, GlobalSearchScope.allScope(project)); + if (psiClass == null) { + context.newError(LocalizeValue.localizeTODO("Cannot find class " + name)).add(); + continue; + } + + // wow, this is a sweet trick... ;) + searchHelper.processAllFilesWithWord( + StringUtil.getShortName(name), + scope, + psiFile -> { + if (JavaLanguage.INSTANCE == psiFile.getLanguage() + && psiFile.getVirtualFile() != null + && psiFile instanceof PsiJavaFile javaFile) { + addClassFiles(javaFile, result, project); + } + return true; + }, + true + ); } - } - } - } + }); + return result.toArray(new ProcessingItem[result.size()]); } - } - public ProcessingItem[] process(final CompileContext context, final ProcessingItem[] items) { - final ProgressIndicator progressIndicator = context.getProgressIndicator(); - progressIndicator.setText(getProgressMessage()); - - final Project project = context.getProject(); - final ArrayList result = new ArrayList(items.length); - ApplicationManager.getApplication().runReadAction(new Runnable() { - - public void run() { - int filesProcessed = 0; - - for (ProcessingItem pi : items) { - final InstrumentationItem item = ((InstrumentationItem)pi); - final VirtualFile classFile = item.getClassFile(); - - try { - final byte[] bytes = classFile.contentsToByteArray(); - final ClassReader classreader; - try { - classreader = new ClassReader(bytes, 0, bytes.length); - } - catch (Exception e) { - LOG.debug("ASM failed to read class file <" + classFile.getPresentableUrl() + ">", e); - continue; + @RequiredReadAction + private static void addClassFiles(PsiJavaFile srcFile, Set result, Project project) { + VirtualFile sourceFile = srcFile.getVirtualFile(); + assert sourceFile != null; + + ProjectFileIndex index = ProjectRootManager.getInstance(project).getFileIndex(); + Module module = index.getModuleForFile(sourceFile); + if (module != null) { + Sdk jdk = ModuleUtilCore.getSdk(module, JavaModuleExtension.class); + boolean jdk6 = jdk != null && JavaSdkTypeUtil.isOfVersionOrHigher(jdk, JavaSdkVersion.JDK_1_6); + + ModuleCompilerPathsManager compilerPathsManager = ModuleCompilerPathsManager.getInstance(module); + VirtualFile compilerOutputPath = compilerPathsManager.getCompilerOutput(ProductionContentFolderTypeProvider.getInstance()); + if (compilerOutputPath != null) { + String packageName = srcFile.getPackageName(); + VirtualFile packageDir = packageName.length() > 0 + ? compilerOutputPath.findFileByRelativePath(packageName.replace('.', '/')) + : compilerOutputPath; + + if (packageDir != null && packageDir.isDirectory()) { + PsiClass[] classes = srcFile.getClasses(); + for (VirtualFile classFile : packageDir.getChildren()) { + if (classFile.isDirectory() || !"class".equals(classFile.getExtension())) { + // no point in looking at directories or non-class files + continue; + } + String name = classFile.getName(); + for (PsiClass clazz : classes) { + String className = clazz.getName(); + if (className != null && name.startsWith(className)) { + result.add(new InstrumentationItem(classFile, jdk6)); + } + } + } + } } + } + } - final ClassWriter classwriter = new PsiClassWriter(project, item.isJDK6()); - final Instrumenter instrumenter = createInstrumenter(classwriter); - - classreader.accept(instrumenter, 0); - - if (instrumenter.instrumented()) { - // only dump the class if it has actually been instrumented - final FileOutputStream out = new FileOutputStream(classFile.getPath()); - try { - out.write(classwriter.toByteArray()); - } - finally { - out.close(); - } + @Override + public ProcessingItem[] process(CompileContext context, ProcessingItem[] items) { + ProgressIndicator progressIndicator = context.getProgressIndicator(); + progressIndicator.setText(getProgressMessage()); + + Project project = context.getProject(); + List result = new ArrayList<>(items.length); + Application.get().runReadAction(() -> { + int filesProcessed = 0; + + for (ProcessingItem pi : items) { + InstrumentationItem item = ((InstrumentationItem) pi); + VirtualFile classFile = item.getClassFile(); + + try { + byte[] bytes = classFile.contentsToByteArray(); + ClassReader classreader; + try { + classreader = new ClassReader(bytes, 0, bytes.length); + } + catch (Exception e) { + LOG.debug("ASM failed to read class file <" + classFile.getPresentableUrl() + ">", e); + continue; + } + + ClassWriter classwriter = new PsiClassWriter(project, item.isJDK6()); + Instrumenter instrumenter = createInstrumenter(classwriter); + + classreader.accept(instrumenter, 0); + + if (instrumenter.instrumented()) { + // only dump the class if it has actually been instrumented + FileOutputStream out = new FileOutputStream(classFile.getPath()); + try { + out.write(classwriter.toByteArray()); + } + finally { + out.close(); + } + } + + result.add(item); + progressIndicator.setFraction(++filesProcessed / (double) items.length); + } + catch (InstrumentationException | IOException e) { + context.newError(LocalizeValue.localizeTODO("[" + getDescription() + "]: " + e.getLocalizedMessage())).add(); + } } + }); + return result.toArray(new ProcessingItem[result.size()]); + } - result.add(item); - progressIndicator.setFraction(++filesProcessed / (double)items.length); - } - catch (InstrumentationException e) { - context.addMessage(CompilerMessageCategory.ERROR, "[" + getDescription() + "]: " + e.getLocalizedMessage(), null, -1, -1); - } - catch (IOException e) { - context.addMessage(CompilerMessageCategory.ERROR, "[" + getDescription() + "]: " + e.getLocalizedMessage(), null, -1, -1); - } - } - } - }); - return result.toArray(new ProcessingItem[result.size()]); - } - - protected abstract boolean isEnabled(); + protected abstract boolean isEnabled(); - protected abstract String[] getAnnotationNames(Project project); + protected abstract String[] getAnnotationNames(Project project); - protected abstract Instrumenter createInstrumenter(ClassWriter classwriter); + protected abstract Instrumenter createInstrumenter(ClassWriter classwriter); - protected abstract String getProgressMessage(); + protected abstract String getProgressMessage(); - public boolean validateConfiguration(CompileScope compilescope) { - return true; - } + @Override + public boolean validateConfiguration(CompileScope compilescope) { + return true; + } - @Nullable - public ValidityState createValidityState(DataInput datainputstream) throws IOException { -// return TimestampValidityState.load(datainputstream); - return null; - } + @Nullable + @Override + public ValidityState createValidityState(DataInput dataInputStream) throws IOException { +// return TimestampValidityState.load(dataInputStream); + return null; + } } diff --git a/java-language-impl/src/main/java/com/intellij/java/impl/externalSystem/JavacOutputParser.java b/java-language-impl/src/main/java/com/intellij/java/impl/externalSystem/JavacOutputParser.java index 6f73bd6e37..b1f8b3d736 100644 --- a/java-language-impl/src/main/java/com/intellij/java/impl/externalSystem/JavacOutputParser.java +++ b/java-language-impl/src/main/java/com/intellij/java/impl/externalSystem/JavacOutputParser.java @@ -10,6 +10,7 @@ import consulo.build.ui.output.BuildOutputInstantReader; import consulo.build.ui.output.BuildOutputParser; import consulo.compiler.CompilerManager; +import consulo.localize.LocalizeValue; import consulo.util.collection.ContainerUtil; import consulo.util.io.FileUtil; import consulo.util.lang.StringUtil; @@ -43,11 +44,8 @@ public JavacOutputParser(BuildEventFactory buildEventFactory, String... fileExte } @Override - public boolean parse( - String line, - BuildOutputInstantReader reader, - Consumer messageConsumer - ) { + public boolean parse(String line, BuildOutputInstantReader reader, Consumer messageConsumer) { + LocalizeValue lineValue = LocalizeValue.of(line); int colonIndex1 = line.indexOf(COLON); if (colonIndex1 == 1) { // drive letter colonIndex1 = line.indexOf(COLON, colonIndex1 + 1); @@ -62,8 +60,8 @@ public boolean parse( reader.getParentEventId(), MessageEvent.Kind.ERROR, CompilerManager.NOTIFICATION_GROUP, - text, - line + LocalizeValue.of(text), + lineValue )); return true; } @@ -74,8 +72,8 @@ public boolean parse( reader.getParentEventId(), MessageEvent.Kind.WARNING, CompilerManager.NOTIFICATION_GROUP, - text, - line + LocalizeValue.of(text), + lineValue )); return true; } @@ -84,8 +82,8 @@ public boolean parse( reader.getParentEventId(), MessageEvent.Kind.ERROR, CompilerManager.NOTIFICATION_GROUP, - line, - line + lineValue, + lineValue )); return true; } @@ -97,15 +95,14 @@ public boolean parse( if (file.isFile()) { message = message.substring(javaFileExtensionIndex + ".java".length() + 1); String detailedMessage = amendNextInfoLinesIfNeeded(file.getPath() + ":\n" + message, reader); - messageConsumer - .accept(myBuildEventFactory.createFileMessageEvent( - reader.getParentEventId(), - MessageEvent.Kind.INFO, - CompilerManager.NOTIFICATION_GROUP, - message, - detailedMessage, - new FilePosition(file, 0, 0) - )); + messageConsumer.accept(myBuildEventFactory.createFileMessageEvent( + reader.getParentEventId(), + MessageEvent.Kind.INFO, + CompilerManager.NOTIFICATION_GROUP, + LocalizeValue.of(message), + LocalizeValue.of(detailedMessage), + new FilePosition(file, 0, 0) + )); return true; } } @@ -175,15 +172,14 @@ else if (text.startsWith(ERROR_PREFIX)) { if (column >= 0) { String message = StringUtil.join(convertMessages(messageList), "\n"); String detailedMessage = line + "\n" + outputCollector.getOutput(); //NON-NLS - messageConsumer - .accept(myBuildEventFactory.createFileMessageEvent( - reader.getParentEventId(), - kind, - CompilerManager.NOTIFICATION_GROUP, - message, - detailedMessage, - new FilePosition(file, lineNumber - 1, column) - )); + messageConsumer.accept(myBuildEventFactory.createFileMessageEvent( + reader.getParentEventId(), + kind, + CompilerManager.NOTIFICATION_GROUP, + LocalizeValue.of(message), + LocalizeValue.of(detailedMessage), + new FilePosition(file, lineNumber - 1, column) + )); return true; } } @@ -197,8 +193,8 @@ else if (text.startsWith(ERROR_PREFIX)) { reader.getParentEventId(), MessageEvent.Kind.ERROR, CompilerManager.NOTIFICATION_GROUP, - BuildLocalize.buildEventMessageOutMemory().get(), - line + BuildLocalize.buildEventMessageOutMemory(), + lineValue )); return true; } @@ -234,7 +230,6 @@ private static boolean isMessageEnd(@Nullable String line) { return line != null && line.length() > 0 && Character.isWhitespace(line.charAt(0)); } - private static List convertMessages(List messages) { if (messages.size() <= 1) { return messages; From e472cfd32342d0429fc006c53908b9d1fe5d0e2e Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 24 Jun 2026 00:52:38 +0300 Subject: [PATCH 2/2] Fixes. --- .../intellij/java/compiler/impl/javaCompiler/JavaCompiler.java | 2 +- .../compiler/impl/javaCompiler/javac/JavacOutputParser.java | 2 +- .../java/compiler/impl/javaCompiler/JavaToolMonitor.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java index 69f1903fed..349be3bb19 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/JavaCompiler.java @@ -108,7 +108,7 @@ public void compile(CompileContext context, Chunk moduleChunk, VirtualFi wrapper.compile(parsingInfo); } catch (CompilerException e) { - context.newError(LocalizeValue.of(ExceptionUtil.getThrowableText(e))); + context.newError(LocalizeValue.of(ExceptionUtil.getThrowableText(e))).add(); LOGGER.info(e); } catch (CacheCorruptedException e) { diff --git a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java index 6015221fa8..8939c41694 100644 --- a/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java +++ b/java-compiler-impl/src/main/java/com/intellij/java/compiler/impl/javaCompiler/javac/JavacOutputParser.java @@ -160,7 +160,7 @@ public boolean processMessageLine(Callback callback) { } if (line.endsWith("java.lang.OutOfMemoryError")) { - callback.newError(CompilerLocalize.errorJavacOutOfMemory()); + callback.newError(CompilerLocalize.errorJavacOutOfMemory()).add(); return true; } diff --git a/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java b/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java index 07f9386e02..89f060d790 100644 --- a/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java +++ b/java-compiler-impl/src/main/java/consulo/java/compiler/impl/javaCompiler/JavaToolMonitor.java @@ -188,7 +188,7 @@ public CompileContext.MessageBuilder url(String url) { try { VirtualFile fileByURL = VirtualFileUtil.findFileByURL(new URI(url).toURL()); if (fileByURL != null) { - return url(fileByURL.getUrl()); + myDelegate.url(fileByURL.getUrl()); } } catch (Exception ignored) {