2 * Copyright 2000-2012 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.jetbrains.jps.incremental.groovy;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.util.Key;
21 import com.intellij.openapi.util.Ref;
22 import com.intellij.openapi.util.SystemInfo;
23 import com.intellij.openapi.util.io.FileUtil;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.util.ArrayUtil;
26 import com.intellij.util.Consumer;
27 import com.intellij.util.Function;
28 import com.intellij.util.SystemProperties;
29 import com.intellij.util.containers.ContainerUtilRt;
30 import com.intellij.util.lang.UrlClassLoader;
31 import gnu.trove.THashMap;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34 import org.jetbrains.groovy.compiler.rt.GroovyRtConstants;
35 import org.jetbrains.jps.ModuleChunk;
36 import org.jetbrains.jps.ProjectPaths;
37 import org.jetbrains.jps.builders.BuildRootIndex;
38 import org.jetbrains.jps.builders.DirtyFilesHolder;
39 import org.jetbrains.jps.builders.FileProcessor;
40 import org.jetbrains.jps.builders.java.JavaBuilderUtil;
41 import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
42 import org.jetbrains.jps.builders.java.dependencyView.Callbacks;
43 import org.jetbrains.jps.builders.java.dependencyView.Mappings;
44 import org.jetbrains.jps.builders.storage.SourceToOutputMapping;
45 import org.jetbrains.jps.cmdline.ClasspathBootstrap;
46 import org.jetbrains.jps.cmdline.ProjectDescriptor;
47 import org.jetbrains.jps.incremental.*;
48 import org.jetbrains.jps.incremental.java.ClassPostProcessor;
49 import org.jetbrains.jps.incremental.java.JavaBuilder;
50 import org.jetbrains.jps.incremental.messages.BuildMessage;
51 import org.jetbrains.jps.incremental.messages.CompilerMessage;
52 import org.jetbrains.jps.incremental.messages.ProgressMessage;
53 import org.jetbrains.jps.javac.OutputFileObject;
54 import org.jetbrains.jps.model.JpsDummyElement;
55 import org.jetbrains.jps.model.java.JpsJavaExtensionService;
56 import org.jetbrains.jps.model.java.JpsJavaSdkType;
57 import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration;
58 import org.jetbrains.jps.model.library.sdk.JpsSdk;
59 import org.jetbrains.jps.service.JpsServiceManager;
60 import org.jetbrains.jps.service.SharedThreadPool;
61 import org.jetbrains.org.objectweb.asm.ClassReader;
62 import org.jetbrains.org.objectweb.asm.ClassVisitor;
63 import org.jetbrains.org.objectweb.asm.Opcodes;
66 import java.io.IOException;
68 import java.util.concurrent.Future;
71 * @author Eugene Zhuravlev
74 public class GroovyBuilder extends ModuleLevelBuilder {
75 private static final int ourOptimizeThreshold = Integer.parseInt(System.getProperty("groovyc.optimized.class.loading.threshold", "10"));
76 private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.groovy.GroovyBuilder");
77 private static final Key<Boolean> CHUNK_REBUILD_ORDERED = Key.create("CHUNK_REBUILD_ORDERED");
78 private static final Key<Map<String, String>> STUB_TO_SRC = Key.create("STUB_TO_SRC");
79 private static final Key<Boolean> FILES_MARKED_DIRTY_FOR_NEXT_ROUND = Key.create("SRC_MARKED_DIRTY");
80 private static final String GROOVY_EXTENSION = "groovy";
81 private static final String GPP_EXTENSION = "gpp";
82 private final boolean myForStubs;
83 private final String myBuilderName;
85 public GroovyBuilder(boolean forStubs) {
86 super(forStubs ? BuilderCategory.SOURCE_GENERATOR : BuilderCategory.OVERWRITING_TRANSLATOR);
87 myForStubs = forStubs;
88 myBuilderName = "Groovy " + (forStubs ? "stub generator" : "compiler");
92 JavaBuilder.registerClassPostProcessor(new RecompileStubSources());
95 public ModuleLevelBuilder.ExitCode build(final CompileContext context,
97 DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
98 OutputConsumer outputConsumer) throws ProjectBuildException {
101 JpsGroovySettings settings = JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject());
103 final List<File> toCompile = collectChangedFiles(context, dirtyFilesHolder, myForStubs, false);
104 if (toCompile.isEmpty()) {
105 return hasFilesToCompileForNextRound(context) ? ExitCode.ADDITIONAL_PASS_REQUIRED : ExitCode.NOTHING_DONE;
107 if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
108 LOG.info("forStubs=" + myForStubs);
111 Map<ModuleBuildTarget, String> finalOutputs = getCanonicalModuleOutputs(context, chunk, this);
112 if (finalOutputs == null) {
113 return ExitCode.ABORT;
116 start = System.currentTimeMillis();
117 final Set<String> toCompilePaths = getPathsToCompile(toCompile);
119 JpsSdk<JpsDummyElement> jdk = getJdk(chunk);
120 String version = jdk == null ? SystemInfo.JAVA_RUNTIME_VERSION : jdk.getVersionString();
121 boolean mayDependOnUtilJar = version != null && StringUtil.compareVersionNumbers(version, "1.6") >= 0;
122 boolean optimizeClassLoading = mayDependOnUtilJar && ourOptimizeThreshold != 0 && toCompilePaths.size() >= ourOptimizeThreshold;
124 Map<String, String> class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, finalOutputs);
126 final String encoding = context.getProjectDescriptor().getEncodingConfiguration().getPreferredModuleChunkEncoding(chunk);
127 List<String> patchers = new ArrayList<String>();
129 for (GroovyBuilderExtension extension : JpsServiceManager.getInstance().getExtensions(GroovyBuilderExtension.class)) {
130 patchers.addAll(extension.getCompilationUnitPatchers(context, chunk));
133 Map<ModuleBuildTarget, String> generationOutputs = myForStubs ? getStubGenerationOutputs(chunk, context) : finalOutputs;
134 String compilerOutput = generationOutputs.get(chunk.representativeTarget());
136 Collection<String> classpath = generateClasspath(context, chunk);
137 if (LOG.isDebugEnabled()) {
138 LOG.debug("Optimized class loading: " + optimizeClassLoading);
139 LOG.debug("Groovyc classpath: " + classpath);
142 final File tempFile = GroovycOSProcessHandler.fillFileWithGroovycParameters(
143 compilerOutput, toCompilePaths, finalOutputs.values(), class2Src, encoding, patchers,
144 optimizeClassLoading ? StringUtil.join(classpath, File.pathSeparator) : ""
146 final GroovycOSProcessHandler handler = runGroovyc(context, chunk, tempFile, settings, classpath, optimizeClassLoading);
148 Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>>
149 compiled = processCompiledFiles(context, chunk, generationOutputs, compilerOutput, handler.getSuccessfullyCompiled());
151 if (checkChunkRebuildNeeded(context, handler)) {
152 return ExitCode.CHUNK_REBUILD_REQUIRED;
156 addStubRootsToJavacSourcePath(context, generationOutputs);
157 rememberStubSources(context, compiled);
160 for (CompilerMessage message : handler.getCompilerMessages(chunk.representativeTarget().getModule().getName())) {
161 context.processMessage(message);
164 if (!myForStubs && updateDependencies(context, chunk, dirtyFilesHolder, toCompile, compiled, outputConsumer, this)) {
165 return ExitCode.ADDITIONAL_PASS_REQUIRED;
167 return hasFilesToCompileForNextRound(context) ? ExitCode.ADDITIONAL_PASS_REQUIRED : ExitCode.OK;
169 catch (Exception e) {
170 throw new ProjectBuildException(e);
173 if (start > 0 && LOG.isDebugEnabled()) {
174 LOG.debug(myBuilderName + " took " + (System.currentTimeMillis() - start) + " on " + chunk.getName());
177 FILES_MARKED_DIRTY_FOR_NEXT_ROUND.set(context, null);
182 private Boolean hasFilesToCompileForNextRound(CompileContext context) {
183 return !myForStubs && FILES_MARKED_DIRTY_FOR_NEXT_ROUND.get(context, Boolean.FALSE);
186 private static Set<String> getPathsToCompile(List<File> toCompile) {
187 final Set<String> toCompilePaths = new LinkedHashSet<String>();
188 for (File file : toCompile) {
189 if (LOG.isDebugEnabled()) {
190 LOG.debug("Path to compile: " + file.getPath());
192 toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
194 return toCompilePaths;
197 private GroovycOSProcessHandler runGroovyc(final CompileContext context,
198 final ModuleChunk chunk,
200 final JpsGroovySettings settings,
201 Collection<String> compilationClassPath,
202 boolean optimizeClassLoading) throws IOException {
203 List<String> classpath = new ArrayList<String>();
204 if (optimizeClassLoading) {
205 classpath.add(getGroovyRtRoot().getPath());
206 classpath.add(ClasspathBootstrap.getResourcePath(Function.class));
207 classpath.add(ClasspathBootstrap.getResourcePath(UrlClassLoader.class));
208 classpath.add(ClasspathBootstrap.getResourceFile(THashMap.class).getPath());
210 classpath.addAll(compilationClassPath);
213 List<String> programParams = ContainerUtilRt.newArrayList(optimizeClassLoading ? GroovyRtConstants.OPTIMIZE : "do_not_optimize",
214 myForStubs ? "stubs" : "groovyc",
216 if (settings.invokeDynamic) {
217 programParams.add("--indy");
220 List<String> vmParams = ContainerUtilRt.newArrayList();
221 vmParams.add("-Xmx" + settings.heapSize + "m");
222 vmParams.add("-Dfile.encoding=" + System.getProperty("file.encoding"));
223 //vmParams.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239");
225 String grapeRoot = System.getProperty(GroovycOSProcessHandler.GRAPE_ROOT);
226 if (grapeRoot != null) {
227 vmParams.add("-D" + GroovycOSProcessHandler.GRAPE_ROOT + "=" + grapeRoot);
230 final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
231 getJavaExecutable(chunk),
232 "org.jetbrains.groovy.compiler.rt.GroovycRunner",
233 Collections.<String>emptyList(), classpath,
238 final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd));
239 final Consumer<String> updater = new Consumer<String>() {
240 public void consume(String s) {
241 context.processMessage(new ProgressMessage(s + " [" + chunk.getPresentableShortName() + "]"));
244 final GroovycOSProcessHandler handler = new GroovycOSProcessHandler(process, updater) {
246 protected Future<?> executeOnPooledThread(Runnable task) {
247 return SharedThreadPool.getInstance().executeOnPooledThread(task);
251 handler.startNotify();
256 private static boolean checkChunkRebuildNeeded(CompileContext context, GroovycOSProcessHandler handler) {
257 if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) || !handler.shouldRetry()) {
261 if (CHUNK_REBUILD_ORDERED.get(context) != null) {
262 CHUNK_REBUILD_ORDERED.set(context, null);
266 CHUNK_REBUILD_ORDERED.set(context, Boolean.TRUE);
267 LOG.info("Order chunk rebuild");
271 private static void rememberStubSources(CompileContext context, Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> compiled) {
272 Map<String, String> stubToSrc = STUB_TO_SRC.get(context);
273 if (stubToSrc == null) {
274 STUB_TO_SRC.set(context, stubToSrc = new HashMap<String, String>());
276 for (Collection<GroovycOSProcessHandler.OutputItem> items : compiled.values()) {
277 for (GroovycOSProcessHandler.OutputItem item : items) {
278 stubToSrc.put(FileUtil.toSystemIndependentName(item.outputPath), item.sourcePath);
283 private static void addStubRootsToJavacSourcePath(CompileContext context, Map<ModuleBuildTarget, String> generationOutputs) {
284 final BuildRootIndex rootsIndex = context.getProjectDescriptor().getBuildRootIndex();
285 for (ModuleBuildTarget target : generationOutputs.keySet()) {
286 File root = new File(generationOutputs.get(target));
287 rootsIndex.associateTempRoot(context, target, new JavaSourceRootDescriptor(root, target, true, true, "", Collections.<File>emptySet()));
291 public static Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> processCompiledFiles(CompileContext context,
293 Map<ModuleBuildTarget, String> generationOutputs,
294 String compilerOutput,
295 List<GroovycOSProcessHandler.OutputItem> successfullyCompiled)
297 ProjectDescriptor pd = context.getProjectDescriptor();
299 final Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> compiled = new THashMap<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>>();
300 for (final GroovycOSProcessHandler.OutputItem item : successfullyCompiled) {
301 if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
302 LOG.info("compiled=" + item);
304 final JavaSourceRootDescriptor rd = pd.getBuildRootIndex().findJavaRootDescriptor(context, new File(item.sourcePath));
306 final String outputPath = ensureCorrectOutput(chunk, item, generationOutputs, compilerOutput, rd.target);
308 Collection<GroovycOSProcessHandler.OutputItem> items = compiled.get(rd.target);
310 items = new ArrayList<GroovycOSProcessHandler.OutputItem>();
311 compiled.put(rd.target, items);
314 items.add(new GroovycOSProcessHandler.OutputItem(outputPath, item.sourcePath));
317 if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
318 LOG.info("No java source root descriptor for the item found =" + item);
322 if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
323 LOG.info("Chunk " + chunk + " compilation finished");
329 public void chunkBuildFinished(CompileContext context, ModuleChunk chunk) {
330 JavaBuilderUtil.cleanupChunkResources(context);
331 STUB_TO_SRC.set(context, null);
334 private static Map<ModuleBuildTarget, String> getStubGenerationOutputs(ModuleChunk chunk, CompileContext context) throws IOException {
335 Map<ModuleBuildTarget, String> generationOutputs = new HashMap<ModuleBuildTarget, String>();
336 File commonRoot = new File(context.getProjectDescriptor().dataManager.getDataPaths().getDataStorageRoot(), "groovyStubs");
337 for (ModuleBuildTarget target : chunk.getTargets()) {
338 File targetRoot = new File(commonRoot, target.getModule().getName() + File.separator + target.getTargetType().getTypeId());
339 if (!FileUtil.delete(targetRoot)) {
340 throw new IOException("External make cannot clean " + targetRoot.getPath());
342 if (!targetRoot.mkdirs()) {
343 throw new IOException("External make cannot create " + targetRoot.getPath());
345 generationOutputs.put(target, targetRoot.getPath());
347 return generationOutputs;
351 public static Map<ModuleBuildTarget, String> getCanonicalModuleOutputs(CompileContext context, ModuleChunk chunk, Builder builder) {
352 Map<ModuleBuildTarget, String> finalOutputs = new HashMap<ModuleBuildTarget, String>();
353 for (ModuleBuildTarget target : chunk.getTargets()) {
354 File moduleOutputDir = target.getOutputDir();
355 if (moduleOutputDir == null) {
356 context.processMessage(new CompilerMessage(builder.getPresentableName(), BuildMessage.Kind.ERROR, "Output directory not specified for module " + target.getModule().getName()));
359 //noinspection ResultOfMethodCallIgnored
360 moduleOutputDir.mkdirs();
361 String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
362 assert moduleOutputPath != null;
363 finalOutputs.put(target, moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/");
368 private static String ensureCorrectOutput(ModuleChunk chunk,
369 GroovycOSProcessHandler.OutputItem item,
370 Map<ModuleBuildTarget, String> generationOutputs,
371 String compilerOutput,
372 @NotNull ModuleBuildTarget srcTarget) throws IOException {
373 if (chunk.getModules().size() > 1 && !srcTarget.equals(chunk.representativeTarget())) {
374 File output = new File(item.outputPath);
376 String srcTargetOutput = generationOutputs.get(srcTarget);
377 if (srcTargetOutput == null) {
378 LOG.info("No output for " + srcTarget + "; outputs=" + generationOutputs + "; targets = " + chunk.getTargets());
379 return item.outputPath;
382 //todo honor package prefixes
383 File correctRoot = new File(srcTargetOutput);
384 File correctOutput = new File(correctRoot, FileUtil.getRelativePath(new File(compilerOutput), output));
386 FileUtil.rename(output, correctOutput);
387 return correctOutput.getPath();
389 return item.outputPath;
392 private static String getJavaExecutable(ModuleChunk chunk) {
393 JpsSdk<?> sdk = getJdk(chunk);
394 return sdk != null ? JpsJavaSdkType.getJavaExecutable(sdk) : SystemProperties.getJavaHome() + "/bin/java";
397 private static JpsSdk<JpsDummyElement> getJdk(ModuleChunk chunk) {
398 return chunk.getModules().iterator().next().getSdk(JpsJavaSdkType.INSTANCE);
401 static List<File> collectChangedFiles(CompileContext context,
402 DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
403 final boolean forStubs, final boolean forEclipse)
406 final JpsJavaCompilerConfiguration configuration =
407 JpsJavaExtensionService.getInstance().getCompilerConfiguration(context.getProjectDescriptor().getProject());
408 assert configuration != null;
410 final JpsGroovySettings settings = JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject());
412 final List<File> toCompile = new ArrayList<File>();
413 dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
414 public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor sourceRoot) throws IOException {
415 final String path = file.getPath();
416 //todo file type check
417 if ((isGroovyFile(path) || forEclipse && path.endsWith(".java")) &&
418 !configuration.isResourceFile(file, sourceRoot.root)) {
419 if (forStubs && settings.isExcludedFromStubGeneration(file)) {
431 public static boolean updateDependencies(CompileContext context,
433 DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
434 List<File> toCompile,
435 Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> successfullyCompiled,
436 OutputConsumer outputConsumer, Builder builder) throws IOException {
437 final Mappings delta = context.getProjectDescriptor().dataManager.getMappings().createDelta();
438 final List<File> successfullyCompiledFiles = new ArrayList<File>();
439 if (!successfullyCompiled.isEmpty()) {
441 final Callbacks.Backend callback = delta.getCallback();
443 for (Map.Entry<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> entry : successfullyCompiled.entrySet()) {
444 final ModuleBuildTarget target = entry.getKey();
445 final Collection<GroovycOSProcessHandler.OutputItem> compiled = entry.getValue();
446 for (GroovycOSProcessHandler.OutputItem item : compiled) {
447 final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath);
448 final String outputPath = FileUtil.toSystemIndependentName(item.outputPath);
449 final File outputFile = new File(outputPath);
450 final File srcFile = new File(sourcePath);
452 final byte[] bytes = FileUtil.loadFileBytes(outputFile);
453 outputConsumer.registerCompiledClass(
455 new CompiledClass(outputFile, srcFile, readClassName(bytes), new BinaryContent(bytes))
457 callback.associate(outputPath, sourcePath, new ClassReader(bytes));
459 catch (Throwable e) {
460 // need this to make sure that unexpected errors in, for example, ASM will not ruin the compilation
461 final String message = "Class dependency information may be incomplete! Error parsing generated class " + item.outputPath;
462 LOG.info(message, e);
463 context.processMessage(new CompilerMessage(
464 builder.getPresentableName(), BuildMessage.Kind.WARNING, message + "\n" + CompilerMessage.getTextFromThrowable(e), sourcePath)
467 successfullyCompiledFiles.add(srcFile);
472 return JavaBuilderUtil.updateMappings(context, delta, dirtyFilesHolder, chunk, toCompile, successfullyCompiledFiles);
475 private static String readClassName(byte[] classBytes) throws IOException{
476 final Ref<String> nameRef = Ref.create(null);
477 new ClassReader(classBytes).accept(new ClassVisitor(Opcodes.ASM5) {
478 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
479 nameRef.set(name.replace('/', '.'));
481 }, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
482 return nameRef.get();
485 private static Collection<String> generateClasspath(CompileContext context, ModuleChunk chunk) {
486 final Set<String> cp = new LinkedHashSet<String>();
488 // IMPORTANT! must be the first in classpath
489 cp.add(getGroovyRtRoot().getPath());
491 for (File file : ProjectPaths.getCompilationClasspathFiles(chunk, chunk.containsTests(), false, false)) {
492 cp.add(FileUtil.toCanonicalPath(file.getPath()));
495 for (GroovyBuilderExtension extension : JpsServiceManager.getInstance().getExtensions(GroovyBuilderExtension.class)) {
496 cp.addAll(extension.getCompilationClassPath(context, chunk));
502 private static File getGroovyRtRoot() {
503 File root = ClasspathBootstrap.getResourceFile(GroovyBuilder.class);
505 return new File(root.getParentFile(), "groovy_rt.jar");
507 return new File(root.getParentFile(), "groovy_rt");
510 public static boolean isGroovyFile(String path) {
511 return path.endsWith("." + GROOVY_EXTENSION) || path.endsWith("." + GPP_EXTENSION);
515 public List<String> getCompilableFileExtensions() {
516 return Arrays.asList(GROOVY_EXTENSION, GPP_EXTENSION);
519 private static Map<String, String> buildClassToSourceMap(ModuleChunk chunk, CompileContext context, Set<String> toCompilePaths, Map<ModuleBuildTarget, String> finalOutputs) throws IOException {
520 final Map<String, String> class2Src = new HashMap<String, String>();
521 JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(
522 context.getProjectDescriptor().getProject());
523 for (ModuleBuildTarget target : chunk.getTargets()) {
524 String moduleOutputPath = finalOutputs.get(target);
525 final SourceToOutputMapping srcToOut = context.getProjectDescriptor().dataManager.getSourceToOutputMap(target);
526 for (String src : srcToOut.getSources()) {
527 if (!toCompilePaths.contains(src) && isGroovyFile(src) &&
528 !configuration.getCompilerExcludes().isExcluded(new File(src))) {
529 final Collection<String> outs = srcToOut.getOutputs(src);
531 for (String out : outs) {
532 if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) {
533 final String className = out.substring(moduleOutputPath.length(), out.length() - ".class".length()).replace('/', '.');
534 class2Src.put(className, src);
545 public String toString() {
546 return myBuilderName;
550 public String getPresentableName() {
551 return myBuilderName;
554 private static class RecompileStubSources implements ClassPostProcessor {
556 public void process(CompileContext context, OutputFileObject out) {
557 Map<String, String> stubToSrc = STUB_TO_SRC.get(context);
558 if (stubToSrc == null) {
561 File src = out.getSourceFile();
565 String groovy = stubToSrc.get(FileUtil.toSystemIndependentName(src.getPath()));
566 if (groovy == null) {
570 final File groovyFile = new File(groovy);
571 if (!FSOperations.isMarkedDirty(context, groovyFile)) {
572 FSOperations.markDirty(context, groovyFile);
573 FILES_MARKED_DIRTY_FOR_NEXT_ROUND.set(context, Boolean.TRUE);
576 catch (IOException e) {