public ExitCode build( final CompileContext context, final ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException { if (!IS_ENABLED.get(context, Boolean.TRUE)) { return ExitCode.NOTHING_DONE; } try { final Map<File, ModuleBuildTarget> filesToCompile = new THashMap<File, ModuleBuildTarget>(FileUtil.FILE_HASHING_STRATEGY); dirtyFilesHolder.processDirtyFiles( new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() { public boolean apply( ModuleBuildTarget target, File file, JavaSourceRootDescriptor descriptor) throws IOException { if (JAVA_SOURCES_FILTER.accept(file)) { filesToCompile.put(file, target); } return true; } }); if (context.isMake()) { final ProjectBuilderLogger logger = context.getLoggingManager().getProjectBuilderLogger(); if (logger.isEnabled()) { if (filesToCompile.size() > 0) { logger.logCompiledFiles(filesToCompile.keySet(), BUILDER_NAME, "Compiling files:"); } } } return compile(context, chunk, dirtyFilesHolder, filesToCompile.keySet(), outputConsumer); } catch (ProjectBuildException e) { throw e; } catch (Exception e) { String message = e.getMessage(); if (message == null) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); final PrintStream stream = new PrintStream(out); try { e.printStackTrace(stream); } finally { stream.close(); } message = "Internal error: \n" + out.toString(); } context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message)); throw new ProjectBuildException(message, e); } }
public void save(@NotNull final OutputFileObject fileObject) { if (JavaFileObject.Kind.CLASS != fileObject.getKind()) { // generated sources or resources must be saved synchronously, because some compilers (e.g. // eclipse) // may want to read generated text for further compilation try { final BinaryContent content = fileObject.getContent(); if (content != null) { content.saveToFile(fileObject.getFile()); } } catch (IOException e) { myContext.processMessage( new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage())); } } submitAsyncTask( myContext, new Runnable() { public void run() { try { for (ClassPostProcessor processor : ourClassProcessors) { processor.process(myContext, fileObject); } } finally { myDelegateOutputFileSink.save(fileObject); } } }); }
private static int getJavacServerHeapSize(CompileContext context) { final JpsProject project = context.getProjectDescriptor().getProject(); final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); final JpsJavaCompilerOptions options = config.getCurrentCompilerOptions(); return options.MAXIMUM_HEAP_SIZE; }
@Override public void buildStarted(CompileContext context) { final JpsProject project = context.getProjectDescriptor().getProject(); final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getCompilerConfiguration(project); final String compilerId = config == null ? JavaCompilers.JAVAC_ID : config.getJavaCompilerId(); if (LOG.isDebugEnabled()) { LOG.debug("Java compiler ID: " + compilerId); } final boolean isJavac = JavaCompilers.JAVAC_ID.equalsIgnoreCase(compilerId) || JavaCompilers.JAVAC_API_ID.equalsIgnoreCase(compilerId); final boolean isEclipse = JavaCompilers.ECLIPSE_ID.equalsIgnoreCase(compilerId) || JavaCompilers.ECLIPSE_EMBEDDED_ID.equalsIgnoreCase(compilerId); IS_ENABLED.set(context, isJavac || isEclipse); String messageText = null; if (isJavac) { messageText = "Using javac " + System.getProperty("java.version") + " to compile java sources"; } else if (isEclipse) { messageText = "Using eclipse compiler to compile java sources"; } COMPILER_VERSION_INFO.set(context, new AtomicReference<String>(messageText)); }
public void outputLineAvailable(String line) { if (!StringUtil.isEmpty(line)) { if (line.contains("java.lang.OutOfMemoryError")) { myContext.processMessage( new CompilerMessage( BUILDER_NAME, BuildMessage.Kind.ERROR, "OutOfMemoryError: insufficient memory")); myErrorCount++; } else { final BuildMessage.Kind kind = getKindByMessageText(line); if (kind == BuildMessage.Kind.ERROR) { myErrorCount++; } else if (kind == BuildMessage.Kind.WARNING) { myWarningCount++; } myContext.processMessage(new CompilerMessage(BUILDER_NAME, kind, line)); } } }
private static void loadCommonJavacOptions(CompileContext context) { final List<String> options = new ArrayList<String>(); final List<String> vmOptions = new ArrayList<String>(); final JpsProject project = context.getProjectDescriptor().getProject(); final JpsJavaCompilerConfiguration compilerConfig = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); final JpsJavaCompilerOptions compilerOptions = compilerConfig.getCurrentCompilerOptions(); if (compilerOptions.DEBUGGING_INFO) { options.add("-g"); } if (compilerOptions.DEPRECATION) { options.add("-deprecation"); } if (compilerOptions.GENERATE_NO_WARNINGS) { options.add("-nowarn"); } if (compilerOptions instanceof EclipseCompilerOptions) { final EclipseCompilerOptions eclipseOptions = (EclipseCompilerOptions) compilerOptions; if (eclipseOptions.PROCEED_ON_ERROR) { options.add("-proceedOnError"); } } final String customArgs = compilerOptions.ADDITIONAL_OPTIONS_STRING; if (customArgs != null) { final StringTokenizer customOptsTokenizer = new StringTokenizer(customArgs, " \t\r\n"); boolean skip = false; while (customOptsTokenizer.hasMoreTokens()) { final String userOption = customOptsTokenizer.nextToken(); if (FILTERED_OPTIONS.contains(userOption)) { skip = true; continue; } if (!skip) { if (!FILTERED_SINGLE_OPTIONS.contains(userOption)) { if (userOption.startsWith("-J-")) { vmOptions.add(userOption.substring("-J".length())); } else { options.add(userOption); } } } } } if (useEclipseCompiler(context)) { for (String option : options) { if (option.startsWith("-proceedOnError")) { Utils.PROCEED_ON_ERROR_KEY.set(context, Boolean.TRUE); break; } } } JAVAC_OPTIONS.set(context, options); JAVAC_VM_OPTIONS.set(context, vmOptions); }
private static boolean useEclipseCompiler(CompileContext context) { if (!USE_EMBEDDED_JAVAC) { return false; } JpsProject project = context.getProjectDescriptor().getProject(); final JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getCompilerConfiguration(project); final String compilerId = configuration != null ? configuration.getJavaCompilerId() : null; return JavaCompilers.ECLIPSE_ID.equalsIgnoreCase(compilerId) || JavaCompilers.ECLIPSE_EMBEDDED_ID.equalsIgnoreCase(compilerId); }
private static Map<File, Set<File>> buildOutputDirectoriesMap( CompileContext context, ModuleChunk chunk) { final Map<File, Set<File>> map = new THashMap<File, Set<File>>(FileUtil.FILE_HASHING_STRATEGY); for (ModuleBuildTarget target : chunk.getTargets()) { final File outputDir = target.getOutputDir(); if (outputDir == null) { continue; } final Set<File> roots = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY); for (JavaSourceRootDescriptor descriptor : context.getProjectDescriptor().getBuildRootIndex().getTargetRoots(target, context)) { roots.add(descriptor.root); } map.put(outputDir, roots); } return map; }
public void report(Diagnostic<? extends JavaFileObject> diagnostic) { final CompilerMessage.Kind kind; switch (diagnostic.getKind()) { case ERROR: kind = BuildMessage.Kind.ERROR; myErrorCount++; break; case MANDATORY_WARNING: case WARNING: case NOTE: kind = BuildMessage.Kind.WARNING; myWarningCount++; break; default: kind = BuildMessage.Kind.INFO; } File sourceFile = null; try { // for eclipse compiler just an attempt to call getSource() may lead to an NPE, // so calling this method under try/catch to avoid induced compiler errors final JavaFileObject source = diagnostic.getSource(); sourceFile = source != null ? Utils.convertToFile(source.toUri()) : null; } catch (Exception e) { LOG.info(e); } final String srcPath = sourceFile != null ? FileUtil.toSystemIndependentName(sourceFile.getPath()) : null; String message = diagnostic.getMessage(Locale.US); if (Utils.IS_TEST_MODE) { LOG.info(message); } myContext.processMessage( new CompilerMessage( BUILDER_NAME, kind, message, srcPath, diagnostic.getStartPosition(), diagnostic.getEndPosition(), diagnostic.getPosition(), diagnostic.getLineNumber(), diagnostic.getColumnNumber())); }
private static int getCompilerSdkVersion(CompileContext context) { final Integer cached = JAVA_COMPILER_VERSION_KEY.get(context); if (cached != null) { return cached; } int javaVersion = convertToNumber(SystemProperties.getJavaVersion()); if (!USE_EMBEDDED_JAVAC) { // in case of external javac, run compiler from the newest jdk that is used in the project for (JpsSdk<?> sdk : context.getProjectDescriptor().getProjectJavaSdks()) { final String version = sdk.getVersionString(); final int ver = convertToNumber(version); if (ver > javaVersion) { javaVersion = ver; } } } JAVA_COMPILER_VERSION_KEY.set(context, javaVersion); return javaVersion; }
private static synchronized JavacServerClient ensureJavacServerLaunched(CompileContext context) throws Exception { final ExternalJavacDescriptor descriptor = ExternalJavacDescriptor.KEY.get(context); if (descriptor != null) { return descriptor.client; } // start server here final int port = findFreePort(); final int heapSize = getJavacServerHeapSize(context); // defaulting to the same jdk that used to run the build process String javaHome = SystemProperties.getJavaHome(); int javaVersion = convertToNumber(SystemProperties.getJavaVersion()); for (JpsSdk<?> sdk : context.getProjectDescriptor().getProjectJavaSdks()) { final String version = sdk.getVersionString(); final int ver = convertToNumber(version); if (ver > javaVersion) { javaVersion = ver; javaHome = sdk.getHomePath(); } } final BaseOSProcessHandler processHandler = JavacServerBootstrap.launchJavacServer( javaHome, heapSize, port, Utils.getSystemRoot(), getCompilationVMOptions(context)); final JavacServerClient client = new JavacServerClient(); try { client.connect("127.0.0.1", port); } catch (Throwable ex) { processHandler.destroyProcess(); throw new Exception("Failed to connect to external javac process: ", ex); } ExternalJavacDescriptor.KEY.set(context, new ExternalJavacDescriptor(processHandler, client)); return client; }
public static void addCompilationOptions( List<String> options, CompileContext context, ModuleChunk chunk, @Nullable ProcessorConfigProfile profile) { if (!isEncodingSet(options)) { final CompilerEncodingConfiguration config = context.getProjectDescriptor().getEncodingConfiguration(); final String encoding = config.getPreferredModuleChunkEncoding(chunk); if (config.getAllModuleChunkEncodings(chunk).size() > 1) { final StringBuilder msgBuilder = new StringBuilder(); msgBuilder.append("Multiple encodings set for module chunk ").append(chunk.getName()); if (encoding != null) { msgBuilder.append("\n\"").append(encoding).append("\" will be used by compiler"); } context.processMessage( new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, msgBuilder.toString())); } if (!StringUtil.isEmpty(encoding)) { options.add("-encoding"); options.add(encoding); } } final String langLevel = getLanguageLevel(chunk.getModules().iterator().next()); if (!StringUtil.isEmpty(langLevel)) { options.add("-source"); options.add(langLevel); } JpsJavaCompilerConfiguration compilerConfiguration = JpsJavaExtensionService.getInstance() .getOrCreateCompilerConfiguration(context.getProjectDescriptor().getProject()); String bytecodeTarget = null; int chunkSdkVersion = -1; for (JpsModule module : chunk.getModules()) { final JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE); if (sdk != null) { final int moduleSdkVersion = convertToNumber(sdk.getVersionString()); if (moduleSdkVersion != 0 /*could determine the version*/ && (chunkSdkVersion < 0 || chunkSdkVersion > moduleSdkVersion)) { chunkSdkVersion = moduleSdkVersion; } } final String moduleTarget = compilerConfiguration.getByteCodeTargetLevel(module.getName()); if (moduleTarget == null) { continue; } if (bytecodeTarget == null) { bytecodeTarget = moduleTarget; } else { if (moduleTarget.compareTo(bytecodeTarget) < 0) { bytecodeTarget = moduleTarget; // use the lower possible target among modules that form the chunk } } } if (bytecodeTarget != null) { options.add("-target"); options.add(bytecodeTarget); } else { if (chunkSdkVersion > 0 && getCompilerSdkVersion(context) > chunkSdkVersion) { // force lower bytecode target level to match the version of sdk assigned to this chunk options.add("-target"); options.add("1." + chunkSdkVersion); } } if (profile != null && profile.isEnabled()) { // configuring annotation processing if (!profile.isObtainProcessorsFromClasspath()) { final String processorsPath = profile.getProcessorPath(); options.add("-processorpath"); options.add( processorsPath == null ? "" : FileUtil.toSystemDependentName(processorsPath.trim())); } final Set<String> processors = profile.getProcessors(); if (!processors.isEmpty()) { options.add("-processor"); options.add(StringUtil.join(processors, ",")); } for (Map.Entry<String, String> optionEntry : profile.getProcessorOptions().entrySet()) { options.add("-A" + optionEntry.getKey() + "=" + optionEntry.getValue()); } final File srcOutput = ProjectPaths.getAnnotationProcessorGeneratedSourcesOutputDir( chunk.getModules().iterator().next(), chunk.containsTests(), profile); if (srcOutput != null) { srcOutput.mkdirs(); options.add("-s"); options.add(srcOutput.getPath()); } } else { options.add("-proc:none"); } }
private boolean compileJava( final CompileContext context, ModuleChunk chunk, Collection<File> files, Collection<File> classpath, Collection<File> platformCp, Collection<File> sourcePath, DiagnosticOutputConsumer diagnosticSink, final OutputFileConsumer outputSink) throws Exception { final TasksCounter counter = new TasksCounter(); COUNTER_KEY.set(context, counter); final JpsJavaExtensionService javaExt = JpsJavaExtensionService.getInstance(); final JpsJavaCompilerConfiguration compilerConfig = javaExt.getCompilerConfiguration(context.getProjectDescriptor().getProject()); assert compilerConfig != null; final Set<JpsModule> modules = chunk.getModules(); ProcessorConfigProfile profile = null; if (modules.size() == 1) { final JpsModule module = modules.iterator().next(); profile = compilerConfig.getAnnotationProcessingProfile(module); } else { // perform cycle-related validations Pair<String, LanguageLevel> pair = null; for (JpsModule module : modules) { final LanguageLevel moduleLevel = javaExt.getLanguageLevel(module); if (pair == null) { pair = Pair.create(module.getName(), moduleLevel); // first value } else { if (!Comparing.equal(pair.getSecond(), moduleLevel)) { final String message = "Modules " + pair.getFirst() + " and " + module.getName() + " must have the same language level because of cyclic dependencies between them"; diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, message)); return true; } } } // check that all chunk modules are excluded from annotation processing for (JpsModule module : modules) { final ProcessorConfigProfile prof = compilerConfig.getAnnotationProcessingProfile(module); if (prof.isEnabled()) { final String message = "Annotation processing is not supported for module cycles. Please ensure that all modules from cycle [" + chunk.getName() + "] are excluded from annotation processing"; diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, message)); return true; } } } final Map<File, Set<File>> outs = buildOutputDirectoriesMap(context, chunk); final List<String> options = getCompilationOptions(context, chunk, profile); final ClassProcessingConsumer classesConsumer = new ClassProcessingConsumer(context, outputSink); if (LOG.isDebugEnabled()) { LOG.debug( "Compiling chunk [" + chunk.getName() + "] with options: \"" + StringUtil.join(options, " ") + "\""); } try { final boolean rc; if (USE_EMBEDDED_JAVAC) { final boolean useEclipse = useEclipseCompiler(context); rc = JavacMain.compile( options, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer, context.getCancelStatus(), useEclipse); } else { final JavacServerClient client = ensureJavacServerLaunched(context); final RequestFuture<JavacServerResponseHandler> future = client.sendCompileRequest( options, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer); while (!future.waitFor(100L, TimeUnit.MILLISECONDS)) { if (context.getCancelStatus().isCanceled()) { future.cancel(false); } } rc = future.getMessageHandler().isTerminatedSuccessfully(); } return rc; } finally { counter.await(); } }
private ExitCode compile( final CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, Collection<File> files, OutputConsumer outputConsumer) throws Exception { ExitCode exitCode = ExitCode.NOTHING_DONE; final boolean hasSourcesToCompile = !files.isEmpty(); if (!hasSourcesToCompile && !dirtyFilesHolder.hasRemovedFiles()) { return exitCode; } final ProjectDescriptor pd = context.getProjectDescriptor(); JavaBuilderUtil.ensureModuleHasJdk( chunk.representativeTarget().getModule(), context, BUILDER_NAME); final Collection<File> classpath = ProjectPaths.getCompilationClasspath(chunk, false /*context.isProjectRebuild()*/); final Collection<File> platformCp = ProjectPaths.getPlatformCompilationClasspath(chunk, false /*context.isProjectRebuild()*/); // begin compilation round final DiagnosticSink diagnosticSink = new DiagnosticSink(context); final Mappings delta = pd.dataManager.getMappings().createDelta(); final Callbacks.Backend mappingsCallback = delta.getCallback(); final OutputFilesSink outputSink = new OutputFilesSink(context, outputConsumer, mappingsCallback, chunk.getName()); try { if (hasSourcesToCompile) { final AtomicReference<String> ref = COMPILER_VERSION_INFO.get(context); final String versionInfo = ref.getAndSet(null); // display compiler version info only once per compile session if (versionInfo != null) { LOG.info(versionInfo); context.processMessage(new CompilerMessage("", BuildMessage.Kind.INFO, versionInfo)); } exitCode = ExitCode.OK; final Set<File> srcPath = new HashSet<File>(); final BuildRootIndex index = pd.getBuildRootIndex(); for (ModuleBuildTarget target : chunk.getTargets()) { for (JavaSourceRootDescriptor rd : index.getTempTargetRoots(target, context)) { srcPath.add(rd.root); } } final String chunkName = chunk.getName(); context.processMessage(new ProgressMessage("Parsing java... [" + chunkName + "]")); final int filesCount = files.size(); boolean compiledOk = true; if (filesCount > 0) { LOG.info( "Compiling " + filesCount + " java files; module: " + chunkName + (chunk.containsTests() ? " (tests)" : "")); if (LOG.isDebugEnabled()) { for (File file : files) { LOG.debug("Compiling " + file.getPath()); } LOG.debug(" classpath for " + chunkName + ":"); for (File file : classpath) { LOG.debug(" " + file.getAbsolutePath()); } LOG.debug(" platform classpath for " + chunkName + ":"); for (File file : platformCp) { LOG.debug(" " + file.getAbsolutePath()); } } compiledOk = compileJava( context, chunk, files, classpath, platformCp, srcPath, diagnosticSink, outputSink); } context.checkCanceled(); if (!compiledOk && diagnosticSink.getErrorCount() == 0) { diagnosticSink.report( new PlainMessageDiagnostic( Diagnostic.Kind.ERROR, "Compilation failed: internal java compiler error")); } if (!Utils.PROCEED_ON_ERROR_KEY.get(context, Boolean.FALSE) && diagnosticSink.getErrorCount() > 0) { if (!compiledOk) { diagnosticSink.report( new PlainMessageDiagnostic( Diagnostic.Kind.OTHER, "Errors occurred while compiling module '" + chunkName + "'")); } throw new ProjectBuildException( "Compilation failed: errors: " + diagnosticSink.getErrorCount() + "; warnings: " + diagnosticSink.getWarningCount()); } } } finally { if (JavaBuilderUtil.updateMappings( context, delta, dirtyFilesHolder, chunk, files, outputSink.getSuccessfullyCompiled())) { exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED; } } return exitCode; }