blob: e1b6d5eb0de8222920973bc0db10bef4a067cf20 [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jps.incremental.groovy;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.SystemProperties;
import com.intellij.util.containers.ContainerUtilRt;
import com.intellij.util.lang.UrlClassLoader;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.groovy.compiler.rt.GroovyRtConstants;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.ProjectPaths;
import org.jetbrains.jps.builders.BuildRootIndex;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.builders.FileProcessor;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor;
import org.jetbrains.jps.builders.java.dependencyView.Callbacks;
import org.jetbrains.jps.builders.storage.SourceToOutputMapping;
import org.jetbrains.jps.cmdline.ClasspathBootstrap;
import org.jetbrains.jps.cmdline.ProjectDescriptor;
import org.jetbrains.jps.incremental.*;
import org.jetbrains.jps.incremental.fs.CompilationRound;
import org.jetbrains.jps.incremental.java.ClassPostProcessor;
import org.jetbrains.jps.incremental.java.JavaBuilder;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.jetbrains.jps.javac.OutputFileObject;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.java.JpsJavaSdkType;
import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration;
import org.jetbrains.jps.model.library.sdk.JpsSdk;
import org.jetbrains.jps.service.JpsServiceManager;
import org.jetbrains.jps.service.SharedThreadPool;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Future;
/**
* @author Eugene Zhuravlev
* Date: 10/25/11
*/
public class GroovyBuilder extends ModuleLevelBuilder {
private static final int ourOptimizeThreshold = Integer.parseInt(System.getProperty("groovyc.optimized.class.loading.threshold", "10"));
private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.incremental.groovy.GroovyBuilder");
private static final Key<Boolean> CHUNK_REBUILD_ORDERED = Key.create("CHUNK_REBUILD_ORDERED");
private static final Key<Map<String, String>> STUB_TO_SRC = Key.create("STUB_TO_SRC");
private static final Key<Boolean> FILES_MARKED_DIRTY_FOR_NEXT_ROUND = Key.create("SRC_MARKED_DIRTY");
private static final String GROOVY_EXTENSION = "groovy";
private static final String GPP_EXTENSION = "gpp";
private final boolean myForStubs;
private final String myBuilderName;
public GroovyBuilder(boolean forStubs) {
super(forStubs ? BuilderCategory.SOURCE_GENERATOR : BuilderCategory.OVERWRITING_TRANSLATOR);
myForStubs = forStubs;
myBuilderName = "Groovy " + (forStubs ? "stub generator" : "compiler");
}
static {
JavaBuilder.registerClassPostProcessor(new RecompileStubSources());
}
public ModuleLevelBuilder.ExitCode build(final CompileContext context,
ModuleChunk chunk,
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
OutputConsumer outputConsumer) throws ProjectBuildException {
long start = 0;
try {
JpsGroovySettings settings = JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject());
final List<File> toCompile = collectChangedFiles(context, dirtyFilesHolder, myForStubs, false);
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();
final Set<String> toCompilePaths = getPathsToCompile(toCompile);
JpsSdk<JpsDummyElement> jdk = getJdk(chunk);
String version = jdk == null ? SystemInfo.JAVA_RUNTIME_VERSION : jdk.getVersionString();
boolean mayDependOnUtilJar = version != null && StringUtil.compareVersionNumbers(version, "1.6") >= 0;
boolean optimizeClassLoading = 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));
}
Map<ModuleBuildTarget, String> generationOutputs = myForStubs ? getStubGenerationOutputs(chunk, context) : finalOutputs;
String compilerOutput = generationOutputs.get(chunk.representativeTarget());
Collection<String> classpath = generateClasspath(context, chunk);
if (LOG.isDebugEnabled()) {
LOG.debug("Optimized class loading: " + optimizeClassLoading);
LOG.debug("Groovyc classpath: " + classpath);
}
final File tempFile = GroovycOSProcessHandler.fillFileWithGroovycParameters(
compilerOutput, toCompilePaths, finalOutputs.values(), class2Src, encoding, patchers,
optimizeClassLoading ? StringUtil.join(classpath, File.pathSeparator) : ""
);
final GroovycOSProcessHandler handler = runGroovyc(context, chunk, tempFile, settings, classpath, optimizeClassLoading);
Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>>
compiled = processCompiledFiles(context, chunk, generationOutputs, compilerOutput, handler.getSuccessfullyCompiled());
if (checkChunkRebuildNeeded(context, handler)) {
return ExitCode.CHUNK_REBUILD_REQUIRED;
}
if (myForStubs) {
addStubRootsToJavacSourcePath(context, generationOutputs);
rememberStubSources(context, compiled);
}
for (CompilerMessage message : handler.getCompilerMessages(chunk.representativeTarget().getModule().getName())) {
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);
}
}
}
private Boolean hasFilesToCompileForNextRound(CompileContext context) {
return !myForStubs && FILES_MARKED_DIRTY_FOR_NEXT_ROUND.get(context, Boolean.FALSE);
}
private static Set<String> getPathsToCompile(List<File> toCompile) {
final Set<String> toCompilePaths = new LinkedHashSet<String>();
for (File file : toCompile) {
if (LOG.isDebugEnabled()) {
LOG.debug("Path to compile: " + file.getPath());
}
toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
}
return toCompilePaths;
}
private GroovycOSProcessHandler runGroovyc(final CompileContext context,
final ModuleChunk chunk,
File tempFile,
final JpsGroovySettings settings,
Collection<String> compilationClassPath,
boolean optimizeClassLoading) throws IOException {
List<String> classpath = new ArrayList<String>();
if (optimizeClassLoading) {
classpath.add(getGroovyRtRoot().getPath());
classpath.add(ClasspathBootstrap.getResourcePath(Function.class));
classpath.add(ClasspathBootstrap.getResourcePath(UrlClassLoader.class));
classpath.add(ClasspathBootstrap.getResourceFile(THashMap.class).getPath());
} else {
classpath.addAll(compilationClassPath);
}
List<String> programParams = ContainerUtilRt.newArrayList(optimizeClassLoading ? GroovyRtConstants.OPTIMIZE : "do_not_optimize",
myForStubs ? "stubs" : "groovyc",
tempFile.getPath());
if (settings.invokeDynamic) {
programParams.add("--indy");
}
List<String> vmParams = ContainerUtilRt.newArrayList();
vmParams.add("-Xmx" + settings.heapSize + "m");
vmParams.add("-Dfile.encoding=" + System.getProperty("file.encoding"));
//vmParams.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239");
String grapeRoot = System.getProperty(GroovycOSProcessHandler.GRAPE_ROOT);
if (grapeRoot != null) {
vmParams.add("-D" + GroovycOSProcessHandler.GRAPE_ROOT + "=" + grapeRoot);
}
final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
getJavaExecutable(chunk),
"org.jetbrains.groovy.compiler.rt.GroovycRunner",
Collections.<String>emptyList(), classpath,
vmParams,
programParams
);
final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd));
final Consumer<String> updater = new Consumer<String>() {
public void consume(String s) {
context.processMessage(new ProgressMessage(s + " [" + chunk.getPresentableShortName() + "]"));
}
};
final GroovycOSProcessHandler handler = new GroovycOSProcessHandler(process, updater) {
@Override
protected Future<?> executeOnPooledThread(Runnable task) {
return SharedThreadPool.getInstance().executeOnPooledThread(task);
}
};
handler.startNotify();
handler.waitFor();
return handler;
}
private static boolean checkChunkRebuildNeeded(CompileContext context, GroovycOSProcessHandler handler) {
if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) || !handler.shouldRetry()) {
return false;
}
if (CHUNK_REBUILD_ORDERED.get(context) != null) {
CHUNK_REBUILD_ORDERED.set(context, null);
return false;
}
CHUNK_REBUILD_ORDERED.set(context, Boolean.TRUE);
LOG.info("Order chunk rebuild");
return true;
}
private static void rememberStubSources(CompileContext context, Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.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<GroovycOSProcessHandler.OutputItem> items : compiled.values()) {
for (GroovycOSProcessHandler.OutputItem item : items) {
stubToSrc.put(FileUtil.toSystemIndependentName(item.outputPath), item.sourcePath);
}
}
}
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 static Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> processCompiledFiles(CompileContext context,
ModuleChunk chunk,
Map<ModuleBuildTarget, String> generationOutputs,
String compilerOutput,
List<GroovycOSProcessHandler.OutputItem> successfullyCompiled)
throws IOException {
ProjectDescriptor pd = context.getProjectDescriptor();
final Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> compiled = new THashMap<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>>();
for (final GroovycOSProcessHandler.OutputItem item : successfullyCompiled) {
if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
LOG.info("compiled=" + item);
}
final JavaSourceRootDescriptor rd = pd.getBuildRootIndex().findJavaRootDescriptor(context, new File(item.sourcePath));
if (rd != null) {
final String outputPath = ensureCorrectOutput(chunk, item, generationOutputs, compilerOutput, rd.target);
Collection<GroovycOSProcessHandler.OutputItem> items = compiled.get(rd.target);
if (items == null) {
items = new ArrayList<GroovycOSProcessHandler.OutputItem>();
compiled.put(rd.target, items);
}
items.add(new GroovycOSProcessHandler.OutputItem(outputPath, item.sourcePath));
}
else {
if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
LOG.info("No java source root descriptor for the item found =" + item);
}
}
}
if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
LOG.info("Chunk " + chunk + " compilation finished");
}
return compiled;
}
@Override
public void chunkBuildFinished(CompileContext context, ModuleChunk chunk) {
JavaBuilderUtil.cleanupChunkResources(context);
STUB_TO_SRC.set(context, null);
}
private static Map<ModuleBuildTarget, String> getStubGenerationOutputs(ModuleChunk chunk, CompileContext context) throws IOException {
Map<ModuleBuildTarget, String> generationOutputs = new HashMap<ModuleBuildTarget, String>();
File commonRoot = new File(context.getProjectDescriptor().dataManager.getDataPaths().getDataStorageRoot(), "groovyStubs");
for (ModuleBuildTarget target : chunk.getTargets()) {
File targetRoot = new File(commonRoot, target.getModule().getName() + File.separator + target.getTargetType().getTypeId());
if (!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;
}
@Nullable
public static Map<ModuleBuildTarget, String> getCanonicalModuleOutputs(CompileContext context, ModuleChunk chunk, Builder builder) {
Map<ModuleBuildTarget, String> finalOutputs = new HashMap<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;
}
private static String ensureCorrectOutput(ModuleChunk chunk,
GroovycOSProcessHandler.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 String getJavaExecutable(ModuleChunk chunk) {
JpsSdk<?> sdk = getJdk(chunk);
return sdk != null ? JpsJavaSdkType.getJavaExecutable(sdk) : SystemProperties.getJavaHome() + "/bin/java";
}
private static JpsSdk<JpsDummyElement> getJdk(ModuleChunk chunk) {
return chunk.getModules().iterator().next().getSdk(JpsJavaSdkType.INSTANCE);
}
static List<File> collectChangedFiles(CompileContext context,
DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
final boolean forStubs, final boolean forEclipse)
throws IOException {
final JpsJavaCompilerConfiguration configuration =
JpsJavaExtensionService.getInstance().getCompilerConfiguration(context.getProjectDescriptor().getProject());
assert configuration != null;
final JpsGroovySettings settings = JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject());
final List<File> toCompile = new ArrayList<File>();
dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
public boolean apply(ModuleBuildTarget target, File file, JavaSourceRootDescriptor sourceRoot) throws IOException {
final String path = file.getPath();
//todo file type check
if ((isGroovyFile(path) || forEclipse && path.endsWith(".java")) &&
!configuration.isResourceFile(file, sourceRoot.root)) {
if (forStubs && settings.isExcludedFromStubGeneration(file)) {
return true;
}
toCompile.add(file);
}
return true;
}
});
return toCompile;
}
public static void updateDependencies(CompileContext context,
List<File> toCompile,
Map<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> successfullyCompiled,
OutputConsumer outputConsumer, Builder builder) throws IOException {
JavaBuilderUtil.registerFilesToCompile(context, toCompile);
if (!successfullyCompiled.isEmpty()) {
final Callbacks.Backend callback = JavaBuilderUtil.getDependenciesRegistrar(context);
for (Map.Entry<ModuleBuildTarget, Collection<GroovycOSProcessHandler.OutputItem>> entry : successfullyCompiled.entrySet()) {
final ModuleBuildTarget target = entry.getKey();
final Collection<GroovycOSProcessHandler.OutputItem> compiled = entry.getValue();
for (GroovycOSProcessHandler.OutputItem item : compiled) {
final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath);
final String outputPath = FileUtil.toSystemIndependentName(item.outputPath);
final File outputFile = new File(outputPath);
final File srcFile = new File(sourcePath);
try {
final byte[] bytes = FileUtil.loadFileBytes(outputFile);
outputConsumer.registerCompiledClass(
target,
new CompiledClass(outputFile, srcFile, readClassName(bytes), new BinaryContent(bytes))
);
callback.associate(outputPath, sourcePath, new ClassReader(bytes));
}
catch (Throwable e) {
// need this to make sure that unexpected errors in, for example, ASM will not ruin the compilation
final String message = "Class dependency information may be incomplete! Error parsing generated class " + item.outputPath;
LOG.info(message, e);
context.processMessage(new CompilerMessage(
builder.getPresentableName(), BuildMessage.Kind.WARNING, message + "\n" + CompilerMessage.getTextFromThrowable(e), sourcePath)
);
}
JavaBuilderUtil.registerSuccessfullyCompiled(context, srcFile);
}
}
}
}
private static String readClassName(byte[] classBytes) throws IOException{
final Ref<String> nameRef = Ref.create(null);
new ClassReader(classBytes).accept(new ClassVisitor(Opcodes.ASM5) {
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
nameRef.set(name.replace('/', '.'));
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return nameRef.get();
}
private static Collection<String> generateClasspath(CompileContext context, ModuleChunk chunk) {
final Set<String> cp = new LinkedHashSet<String>();
//groovy_rt.jar
// IMPORTANT! must be the first in classpath
cp.add(getGroovyRtRoot().getPath());
for (File file : ProjectPaths.getCompilationClasspathFiles(chunk, chunk.containsTests(), false, false)) {
cp.add(FileUtil.toCanonicalPath(file.getPath()));
}
for (GroovyBuilderExtension extension : JpsServiceManager.getInstance().getExtensions(GroovyBuilderExtension.class)) {
cp.addAll(extension.getCompilationClassPath(context, chunk));
}
return cp;
}
private static File getGroovyRtRoot() {
File root = ClasspathBootstrap.getResourceFile(GroovyBuilder.class);
if (root.isFile()) {
return new File(root.getParentFile(), "groovy_rt.jar");
}
return new File(root.getParentFile(), "groovy_rt");
}
public static boolean isGroovyFile(String path) {
return path.endsWith("." + GROOVY_EXTENSION) || path.endsWith("." + GPP_EXTENSION);
}
@Override
public List<String> getCompilableFileExtensions() {
return Arrays.asList(GROOVY_EXTENSION, GPP_EXTENSION);
}
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;
}
@Override
public String toString() {
return myBuilderName;
}
@NotNull
public String getPresentableName() {
return myBuilderName;
}
private static class RecompileStubSources implements ClassPostProcessor {
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);
}
}
}
}