private static Map<String, String> buildClassToSourceMap( ModuleChunk chunk, CompileContext context, Set<String> toCompilePaths, Map<ModuleBuildTarget, String> finalOutputs) throws IOException { final Map<String, String> class2Src = new HashMap<String, String>(); JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance() .getOrCreateCompilerConfiguration(context.getProjectDescriptor().getProject()); for (ModuleBuildTarget target : chunk.getTargets()) { String moduleOutputPath = finalOutputs.get(target); final SourceToOutputMapping srcToOut = context.getProjectDescriptor().dataManager.getSourceToOutputMap(target); for (String src : srcToOut.getSources()) { if (!toCompilePaths.contains(src) && isGroovyFile(src) && !configuration.getCompilerExcludes().isExcluded(new File(src))) { final Collection<String> outs = srcToOut.getOutputs(src); if (outs != null) { for (String out : outs) { if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) { final String className = out.substring(moduleOutputPath.length(), out.length() - ".class".length()) .replace('/', '.'); class2Src.put(className, src); } } } } } } return class2Src; }
private static Map<String, String> buildClassToSourceMap( ModuleChunk chunk, CompileContext context, Set<String> toCompilePaths, String moduleOutputPath) throws IOException { final Map<String, String> class2Src = new HashMap<String, String>(); for (Module module : chunk.getModules()) { final String moduleName = module.getName().toLowerCase(Locale.US); final SourceToOutputMapping srcToOut = context.getDataManager().getSourceToOutputMap(moduleName, context.isCompilingTests()); for (String src : srcToOut.getKeys()) { if (!toCompilePaths.contains(src) && isGroovyFile(src)) { final Collection<String> outs = srcToOut.getState(src); if (outs != null) { for (String out : outs) { if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) { final String className = out.substring(moduleOutputPath.length(), out.length() - ".class".length()) .replace('/', '.'); class2Src.put(className, src); } } } } } } return class2Src; }
private static void addStubRootsToJavacSourcePath( CompileContext context, Map<ModuleBuildTarget, String> generationOutputs) { final BuildRootIndex rootsIndex = context.getProjectDescriptor().getBuildRootIndex(); for (ModuleBuildTarget target : generationOutputs.keySet()) { File root = new File(generationOutputs.get(target)); rootsIndex.associateTempRoot( context, target, new JavaSourceRootDescriptor(root, target, true, true, "", Collections.<File>emptySet())); } }
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); } }
private static void rememberStubSources( CompileContext context, Map<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>> compiled) { Map<String, String> stubToSrc = STUB_TO_SRC.get(context); if (stubToSrc == null) { STUB_TO_SRC.set(context, stubToSrc = new HashMap<String, String>()); } for (Collection<GroovycOutputParser.OutputItem> items : compiled.values()) { for (GroovycOutputParser.OutputItem item : items) { stubToSrc.put(FileUtil.toSystemIndependentName(item.outputPath), item.sourcePath); } } }
private static void setContinuation( CompileContext context, ModuleChunk chunk, @Nullable GroovycContinuation continuation) { clearContinuation(context, chunk); if (continuation != null) { if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) { LOG.info("registering continuation for " + chunk); } Map<ModuleChunk, GroovycContinuation> map = CONTINUATIONS.get(context); if (map == null) CONTINUATIONS.set(context, map = ContainerUtil.newConcurrentMap()); map.put(chunk, continuation); } }
private static String ensureCorrectOutput( ModuleChunk chunk, GroovycOutputParser.OutputItem item, Map<ModuleBuildTarget, String> generationOutputs, String compilerOutput, @NotNull ModuleBuildTarget srcTarget) throws IOException { if (chunk.getModules().size() > 1 && !srcTarget.equals(chunk.representativeTarget())) { File output = new File(item.outputPath); String srcTargetOutput = generationOutputs.get(srcTarget); if (srcTargetOutput == null) { LOG.info( "No output for " + srcTarget + "; outputs=" + generationOutputs + "; targets = " + chunk.getTargets()); return item.outputPath; } // todo honor package prefixes File correctRoot = new File(srcTargetOutput); File correctOutput = new File(correctRoot, FileUtil.getRelativePath(new File(compilerOutput), output)); FileUtil.rename(output, correctOutput); return correctOutput.getPath(); } return item.outputPath; }
private static Map<ModuleBuildTarget, String> getStubGenerationOutputs( ModuleChunk chunk, CompileContext context) throws IOException { Map<ModuleBuildTarget, String> generationOutputs = new HashMap<ModuleBuildTarget, String>(); File commonRoot = getStubRoot(context); for (ModuleBuildTarget target : chunk.getTargets()) { File targetRoot = new File( commonRoot, target.getModule().getName() + File.separator + target.getTargetType().getTypeId()); if (targetRoot.exists() && !FileUtil.deleteWithRenaming(targetRoot)) { throw new IOException("External make cannot clean " + targetRoot.getPath()); } if (!targetRoot.mkdirs()) { throw new IOException("External make cannot create " + targetRoot.getPath()); } generationOutputs.put(target, targetRoot.getPath()); } return generationOutputs; }
public void process(CompileContext context, OutputFileObject out) { Map<String, String> stubToSrc = STUB_TO_SRC.get(context); if (stubToSrc == null) { return; } File src = out.getSourceFile(); if (src == null) { return; } String groovy = stubToSrc.get(FileUtil.toSystemIndependentName(src.getPath())); if (groovy == null) { return; } try { final File groovyFile = new File(groovy); if (!FSOperations.isMarkedDirty(context, CompilationRound.CURRENT, groovyFile)) { FSOperations.markDirty(context, CompilationRound.NEXT, groovyFile); FILES_MARKED_DIRTY_FOR_NEXT_ROUND.set(context, Boolean.TRUE); } } catch (IOException e) { LOG.error(e); } }
@Nullable public static Map<ModuleBuildTarget, String> getCanonicalModuleOutputs( CompileContext context, ModuleChunk chunk, Builder builder) { Map<ModuleBuildTarget, String> finalOutputs = new LinkedHashMap<ModuleBuildTarget, String>(); for (ModuleBuildTarget target : chunk.getTargets()) { File moduleOutputDir = target.getOutputDir(); if (moduleOutputDir == null) { context.processMessage( new CompilerMessage( builder.getPresentableName(), BuildMessage.Kind.ERROR, "Output directory not specified for module " + target.getModule().getName())); return null; } //noinspection ResultOfMethodCallIgnored moduleOutputDir.mkdirs(); String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath()); assert moduleOutputPath != null; finalOutputs.put( target, moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/"); } return finalOutputs; }
public ModuleLevelBuilder.ExitCode build( final CompileContext context, final ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException { if (GreclipseBuilder.useGreclipse(context)) return ExitCode.NOTHING_DONE; long start = 0; try { JpsGroovySettings settings = JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject()); Ref<Boolean> hasStubExcludes = Ref.create(false); final List<File> toCompile = collectChangedFiles(context, dirtyFilesHolder, myForStubs, false, hasStubExcludes); if (toCompile.isEmpty()) { return hasFilesToCompileForNextRound(context) ? ExitCode.ADDITIONAL_PASS_REQUIRED : ExitCode.NOTHING_DONE; } if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) { LOG.info("forStubs=" + myForStubs); } Map<ModuleBuildTarget, String> finalOutputs = getCanonicalModuleOutputs(context, chunk, this); if (finalOutputs == null) { return ExitCode.ABORT; } start = System.currentTimeMillis(); Map<ModuleBuildTarget, String> generationOutputs = myForStubs ? getStubGenerationOutputs(chunk, context) : finalOutputs; String compilerOutput = generationOutputs.get(chunk.representativeTarget()); GroovycOutputParser parser = runGroovycOrContinuation( context, chunk, settings, finalOutputs, compilerOutput, toCompile, hasStubExcludes.get()); Map<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>> compiled = processCompiledFiles( context, chunk, generationOutputs, compilerOutput, parser.getSuccessfullyCompiled()); if (checkChunkRebuildNeeded(context, parser)) { clearContinuation(context, chunk); return ExitCode.CHUNK_REBUILD_REQUIRED; } if (myForStubs) { addStubRootsToJavacSourcePath(context, generationOutputs); rememberStubSources(context, compiled); } for (CompilerMessage message : parser.getCompilerMessages()) { context.processMessage(message); } if (!myForStubs) { updateDependencies(context, toCompile, compiled, outputConsumer, this); } return hasFilesToCompileForNextRound(context) ? ExitCode.ADDITIONAL_PASS_REQUIRED : ExitCode.OK; } catch (Exception e) { throw new ProjectBuildException(e); } finally { if (start > 0 && LOG.isDebugEnabled()) { LOG.debug( myBuilderName + " took " + (System.currentTimeMillis() - start) + " on " + chunk.getName()); } if (!myForStubs) { FILES_MARKED_DIRTY_FOR_NEXT_ROUND.set(context, null); } } }
@Nullable private static GroovycContinuation takeContinuation(CompileContext context, ModuleChunk chunk) { Map<ModuleChunk, GroovycContinuation> map = CONTINUATIONS.get(context); return map == null ? null : map.remove(chunk); }
@NotNull private GroovycOutputParser runGroovycOrContinuation( CompileContext context, ModuleChunk chunk, JpsGroovySettings settings, Map<ModuleBuildTarget, String> finalOutputs, String compilerOutput, List<File> toCompile, boolean hasStubExcludes) throws Exception { GroovycContinuation continuation = takeContinuation(context, chunk); if (continuation != null) { if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) { LOG.info("using continuation for " + chunk); } return continuation.continueCompilation(); } final Set<String> toCompilePaths = getPathsToCompile(toCompile); JpsSdk<JpsDummyElement> jdk = getJdk(chunk); String version = jdk == null ? SystemInfo.JAVA_RUNTIME_VERSION : jdk.getVersionString(); boolean inProcess = "true".equals(System.getProperty("groovyc.in.process", "true")); boolean mayDependOnUtilJar = version != null && StringUtil.compareVersionNumbers(version, "1.6") >= 0; boolean optimizeClassLoading = !inProcess && mayDependOnUtilJar && ourOptimizeThreshold != 0 && toCompilePaths.size() >= ourOptimizeThreshold; Map<String, String> class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, finalOutputs); final String encoding = context .getProjectDescriptor() .getEncodingConfiguration() .getPreferredModuleChunkEncoding(chunk); List<String> patchers = new ArrayList<String>(); for (GroovyBuilderExtension extension : JpsServiceManager.getInstance().getExtensions(GroovyBuilderExtension.class)) { patchers.addAll(extension.getCompilationUnitPatchers(context, chunk)); } Collection<String> classpath = generateClasspath(context, chunk); if (LOG.isDebugEnabled()) { LOG.debug("Optimized class loading: " + optimizeClassLoading); LOG.debug("Groovyc classpath: " + classpath); } final File tempFile = GroovycOutputParser.fillFileWithGroovycParameters( compilerOutput, toCompilePaths, finalOutputs.values(), class2Src, encoding, patchers, optimizeClassLoading ? StringUtil.join(classpath, File.pathSeparator) : ""); GroovycFlavor groovyc = inProcess ? new InProcessGroovyc(finalOutputs.values(), hasStubExcludes) : new ForkedGroovyc(optimizeClassLoading, chunk); GroovycOutputParser parser = new GroovycOutputParser(chunk, context); continuation = groovyc.runGroovyc(classpath, myForStubs, settings, tempFile, parser); setContinuation(context, chunk, continuation); return parser; }
@Override public ExitCode build( final CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException, IOException { if (!useGreclipse(context)) return ModuleLevelBuilder.ExitCode.NOTHING_DONE; try { final List<File> toCompile = GroovyBuilder.collectChangedFiles( context, dirtyFilesHolder, false, true, Ref.create(false)); if (toCompile.isEmpty()) { return ExitCode.NOTHING_DONE; } Map<ModuleBuildTarget, String> outputDirs = GroovyBuilder.getCanonicalModuleOutputs(context, chunk, this); if (outputDirs == null) { return ExitCode.ABORT; } JpsProject project = context.getProjectDescriptor().getProject(); GreclipseSettings greclipseSettings = GreclipseJpsCompilerSettings.getSettings(project); if (greclipseSettings == null) { String message = "Compiler settings component not initialized for " + project; LOG.error(message); context.processMessage( new CompilerMessage(getPresentableName(), BuildMessage.Kind.ERROR, message)); return ExitCode.ABORT; } ClassLoader loader = createGreclipseLoader(greclipseSettings.greclipsePath); if (loader == null) { context.processMessage( new CompilerMessage( getPresentableName(), BuildMessage.Kind.ERROR, "Invalid jar path in the compiler settings: '" + greclipseSettings.greclipsePath + "'")); return ExitCode.ABORT; } final JpsJavaExtensionService javaExt = JpsJavaExtensionService.getInstance(); final JpsJavaCompilerConfiguration compilerConfig = javaExt.getCompilerConfiguration(project); assert compilerConfig != null; final Set<JpsModule> modules = chunk.getModules(); ProcessorConfigProfile profile = null; if (modules.size() == 1) { profile = compilerConfig.getAnnotationProcessingProfile(modules.iterator().next()); } else { String message = JavaBuilder.validateCycle(chunk, javaExt, compilerConfig, modules); if (message != null) { context.processMessage( new CompilerMessage(getPresentableName(), BuildMessage.Kind.ERROR, message)); return ExitCode.ABORT; } } String mainOutputDir = outputDirs.get(chunk.representativeTarget()); final List<String> args = createCommandLine(context, chunk, toCompile, mainOutputDir, profile, greclipseSettings); if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) { LOG.debug("Compiling with args: " + args); } Boolean notified = COMPILER_VERSION_INFO.get(context); if (notified != Boolean.TRUE) { context.processMessage( new CompilerMessage( "", BuildMessage.Kind.INFO, "Using Groovy-Eclipse to compile Java & Groovy sources")); COMPILER_VERSION_INFO.set(context, Boolean.TRUE); } context.processMessage( new ProgressMessage("Compiling java & groovy [" + chunk.getPresentableShortName() + "]")); StringWriter out = new StringWriter(); StringWriter err = new StringWriter(); HashMap<String, List<String>> outputMap = ContainerUtil.newHashMap(); boolean success = performCompilation(args, out, err, outputMap, context, chunk); List<GroovycOutputParser.OutputItem> items = ContainerUtil.newArrayList(); for (String src : outputMap.keySet()) { //noinspection ConstantConditions for (String classFile : outputMap.get(src)) { items.add( new GroovycOutputParser.OutputItem( FileUtil.toSystemIndependentName(mainOutputDir + classFile), FileUtil.toSystemIndependentName(src))); } } Map<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>> successfullyCompiled = GroovyBuilder.processCompiledFiles(context, chunk, outputDirs, mainOutputDir, items); EclipseOutputParser parser = new EclipseOutputParser(getPresentableName(), chunk); List<CompilerMessage> messages = ContainerUtil.concat( parser.parseMessages(out.toString()), parser.parseMessages(err.toString())); boolean hasError = false; for (CompilerMessage message : messages) { if (message.getKind() == BuildMessage.Kind.ERROR) { hasError = true; } context.processMessage(message); } if (!success && !hasError) { context.processMessage( new CompilerMessage( getPresentableName(), BuildMessage.Kind.ERROR, "Compilation failed")); } GroovyBuilder.updateDependencies( context, toCompile, successfullyCompiled, outputConsumer, this); return ExitCode.OK; } catch (Exception e) { throw new ProjectBuildException(e); } }
public Builder.ExitCode build(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException { ExitCode exitCode = ExitCode.OK; final Map<File, Module> toCompile = new HashMap<File, Module>(); try { context.processFilesToRecompile( chunk, new FileProcessor() { @Override public boolean apply(Module module, File file, String sourceRoot) throws Exception { final String path = file.getPath(); if (isGroovyFile(path)) { // todo file type check toCompile.put(file, module); } return true; } }); if (toCompile.isEmpty()) { return exitCode; } final Set<String> cp = new LinkedHashSet<String>(); // groovy_rt.jar // IMPORTANT! must be the first in classpath cp.add(ClasspathBootstrap.getResourcePath(GroovyCompilerWrapper.class).getPath()); for (File file : context .getProjectPaths() .getClasspathFiles(chunk, ClasspathKind.compile(context.isCompilingTests()), false)) { cp.add(FileUtil.toCanonicalPath(file.getPath())); } for (File file : context .getProjectPaths() .getClasspathFiles(chunk, ClasspathKind.runtime(context.isCompilingTests()), false)) { cp.add(FileUtil.toCanonicalPath(file.getPath())); } final File tempFile = FileUtil.createTempFile("ideaGroovyToCompile", ".txt", true); final Module representativeModule = chunk.getModules().iterator().next(); File moduleOutputDir = context .getProjectPaths() .getModuleOutputDir(representativeModule, context.isCompilingTests()); final File dir = myForStubs ? FileUtil.createTempDirectory(/*new File("/tmp/stubs/"), */ "groovyStubs", null) : moduleOutputDir; assert dir != null; final Set<String> toCompilePaths = new LinkedHashSet<String>(); for (File file : toCompile.keySet()) { toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath())); } String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath()); if (!moduleOutputPath.endsWith("/")) { moduleOutputPath += "/"; } Map<String, String> class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutputPath); String encoding = "UTF-8"; // todo encoding List<String> patchers = Collections.emptyList(); // todo patchers GroovycOSProcessHandler.fillFileWithGroovycParameters( tempFile, FileUtil.toCanonicalPath(dir.getPath()), toCompilePaths, FileUtil.toSystemDependentName(moduleOutputPath), class2Src, encoding, patchers); if (myForStubs) { JavaBuilder.addTempSourcePathRoot(context, dir); } // todo CompilerUtil.addLocaleOptions() // todo different outputs in a chunk // todo module jdk path final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine( SystemProperties.getJavaHome() + "/bin/java", "org.jetbrains.groovy.compiler.rt.GroovycRunner", Collections.<String>emptyList(), new ArrayList<String>(cp), Arrays.asList( "-Xmx384m" /*, "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5858"*/), Arrays.<String>asList(myForStubs ? "stubs" : "groovyc", tempFile.getPath())); deleteCorrespondingOutputFiles(context, toCompile); List<GroovycOSProcessHandler.OutputItem> successfullyCompiled = Collections.emptyList(); try { final Process process = Runtime.getRuntime().exec(cmd.toArray(new String[cmd.size()])); GroovycOSProcessHandler handler = new GroovycOSProcessHandler(process, null) { @Override protected void updateStatus(@Nullable String status) { context.processMessage( new ProgressMessage(status == null ? GROOVY_COMPILER_IN_OPERATION : status)); } }; handler.startNotify(); handler.waitFor(); successfullyCompiled = handler.getSuccessfullyCompiled(); final List<CompilerMessage> messages = handler.getCompilerMessages(); for (CompilerMessage message : messages) { context.processMessage(message); } boolean hasMessages = !messages.isEmpty(); final StringBuffer unparsedBuffer = handler.getStdErr(); if (unparsedBuffer.length() != 0) { context.processMessage( new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, unparsedBuffer.toString())); } final int exitValue = handler.getProcess().exitValue(); if (!hasMessages && exitValue != 0) { context.processMessage( new CompilerMessage( BUILDER_NAME, BuildMessage.Kind.ERROR, "Internal groovyc error: code " + exitValue)); } } finally { if (!myForStubs) { final Mappings delta = context.createDelta(); final List<File> successfullyCompiledFiles = new ArrayList<File>(); if (!successfullyCompiled.isEmpty()) { final Callbacks.Backend callback = delta.getCallback(); for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) { final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath); final String outputPath = FileUtil.toSystemIndependentName(item.outputPath); final RootDescriptor moduleAndRoot = context.getModuleAndRoot(new File(sourcePath)); if (moduleAndRoot != null) { final String moduleName = moduleAndRoot.module.getName().toLowerCase(Locale.US); context .getDataManager() .getSourceToOutputMap(moduleName, moduleAndRoot.isTestRoot) .appendData(sourcePath, outputPath); } callback.associate( outputPath, Callbacks.getDefaultLookup(sourcePath), new ClassReader(FileUtil.loadFileBytes(new File(outputPath)))); successfullyCompiledFiles.add(new File(sourcePath)); } } final boolean needSecondPass = updateMappings(context, delta, chunk, toCompile.keySet(), successfullyCompiledFiles); if (needSecondPass) { exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED; } } } return exitCode; } catch (Exception e) { throw new ProjectBuildException(e); } }