1 package org.jetbrains.jps.incremental.groovy;
4 import com.intellij.openapi.util.Comparing;
5 import com.intellij.openapi.util.io.FileUtil;
6 import com.intellij.util.ArrayUtil;
7 import com.intellij.util.Consumer;
8 import com.intellij.util.SystemProperties;
9 import groovy.util.CharsetToolkit;
10 import org.jetbrains.ether.dependencyView.Callbacks;
11 import org.jetbrains.ether.dependencyView.Mappings;
12 import org.jetbrains.groovy.compiler.rt.GroovyCompilerWrapper;
13 import org.jetbrains.jps.ClasspathKind;
14 import org.jetbrains.jps.Module;
15 import org.jetbrains.jps.ModuleChunk;
16 import org.jetbrains.jps.incremental.*;
17 import org.jetbrains.jps.incremental.java.JavaBuilder;
18 import org.jetbrains.jps.incremental.messages.CompilerMessage;
19 import org.jetbrains.jps.incremental.messages.FileGeneratedEvent;
20 import org.jetbrains.jps.incremental.messages.ProgressMessage;
21 import org.jetbrains.jps.incremental.storage.SourceToOutputMapping;
22 import org.jetbrains.jps.server.ClasspathBootstrap;
23 import org.objectweb.asm.ClassReader;
26 import java.io.IOException;
30 * @author Eugene Zhuravlev
33 public class GroovyBuilder extends ModuleLevelBuilder {
34 public static final String BUILDER_NAME = "groovy";
35 private final boolean myForStubs;
36 private final String myBuilderName;
38 public GroovyBuilder(boolean forStubs) {
39 myForStubs = forStubs;
40 myBuilderName = BUILDER_NAME + (forStubs ? "-stubs" : "-classes");
43 public String getName() {
47 public ModuleLevelBuilder.ExitCode build(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException {
48 ExitCode exitCode = ExitCode.OK;
50 final List<File> toCompile = collectChangedFiles(context, chunk);
51 if (toCompile.isEmpty()) {
55 String moduleOutput = getModuleOutput(context, chunk);
56 String compilerOutput = getCompilerOutput(context, moduleOutput);
58 final Set<String> toCompilePaths = new LinkedHashSet<String>();
59 for (File file : toCompile) {
60 toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
63 Map<String, String> class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutput);
65 String ideCharset = chunk.getProject().getProjectCharset();
66 String encoding = !Comparing.equal(CharsetToolkit.getDefaultSystemCharset().name(), ideCharset) ? ideCharset : null;
67 List<String> patchers = Collections.emptyList(); //todo patchers
68 final File tempFile = GroovycOSProcessHandler.fillFileWithGroovycParameters(
69 compilerOutput, toCompilePaths, FileUtil.toSystemDependentName(moduleOutput), class2Src, encoding, patchers
72 // todo CompilerUtil.addLocaleOptions()
73 //todo different outputs in a chunk
75 //todo module jdk path
76 final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
77 SystemProperties.getJavaHome() + "/bin/java",
78 "org.jetbrains.groovy.compiler.rt.GroovycRunner",
79 Collections.<String>emptyList(), new ArrayList<String>(generateClasspath(context, chunk)),
80 Arrays.asList("-Xmx384m"/*, "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239"*/),
81 Arrays.<String>asList(myForStubs ? "stubs" : "groovyc", tempFile.getPath())
84 List<GroovycOSProcessHandler.OutputItem> successfullyCompiled = Collections.emptyList();
86 final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd));
87 GroovycOSProcessHandler handler = GroovycOSProcessHandler.runGroovyc(process, new Consumer<String>() {
89 public void consume(String s) {
90 context.processMessage(new ProgressMessage(s));
94 if (myForStubs && handler.shouldRetry()) {
95 File marker = new File(moduleOutput, "groovy_stubs_retry");
96 if (marker.exists()) {
97 FileUtil.delete(marker);
99 FileUtil.createIfDoesntExist(marker);
100 exitCode = ExitCode.CHUNK_REBUILD_REQUIRED;
105 successfullyCompiled = handler.getSuccessfullyCompiled();
107 for (CompilerMessage message : handler.getCompilerMessages()) {
108 context.processMessage(message);
113 if (updateDependencies(context, chunk, toCompile, moduleOutput, successfullyCompiled)) {
114 exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED;
121 catch (Exception e) {
122 throw new ProjectBuildException(e);
126 private static String getModuleOutput(CompileContext context, ModuleChunk chunk) {
127 final Module representativeModule = chunk.getModules().iterator().next();
128 File moduleOutputDir = context.getProjectPaths().getModuleOutputDir(representativeModule, context.isCompilingTests());
129 assert moduleOutputDir != null;
130 String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
131 return moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/";
134 private String getCompilerOutput(CompileContext context, String moduleOutputDir) throws IOException {
135 final File dir = myForStubs ? FileUtil.createTempDirectory("groovyStubs", null) : new File(moduleOutputDir);
137 JavaBuilder.addTempSourcePathRoot(context, dir);
139 return FileUtil.toCanonicalPath(dir.getPath());
142 private static List<File> collectChangedFiles(CompileContext context, ModuleChunk chunk) throws Exception {
143 final List<File> toCompile = new ArrayList<File>();
144 context.processFilesToRecompile(chunk, new FileProcessor() {
146 public boolean apply(Module module, File file, String sourceRoot) throws Exception {
147 final String path = file.getPath();
148 if (isGroovyFile(path)) { //todo file type check
157 private boolean updateDependencies(CompileContext context,
159 List<File> toCompile,
160 String moduleOutputPath,
161 List<GroovycOSProcessHandler.OutputItem> successfullyCompiled) throws Exception {
162 final Mappings delta = context.createDelta();
163 final List<File> successfullyCompiledFiles = new ArrayList<File>();
164 if (!successfullyCompiled.isEmpty()) {
166 final Callbacks.Backend callback = delta.getCallback();
167 final FileGeneratedEvent generatedEvent = new FileGeneratedEvent();
169 for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) {
170 final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath);
171 final String outputPath = FileUtil.toSystemIndependentName(item.outputPath);
172 final RootDescriptor moduleAndRoot = context.getModuleAndRoot(new File(sourcePath));
173 if (moduleAndRoot != null) {
174 final String moduleName = moduleAndRoot.module.getName().toLowerCase(Locale.US);
175 context.getDataManager().getSourceToOutputMap(moduleName, moduleAndRoot.isTestRoot).appendData(sourcePath, outputPath);
177 callback.associate(outputPath, Callbacks.getDefaultLookup(sourcePath), new ClassReader(FileUtil.loadFileBytes(new File(outputPath))));
178 successfullyCompiledFiles.add(new File(sourcePath));
180 generatedEvent.add(moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/'));
183 context.processMessage(generatedEvent);
187 return updateMappings(context, delta, chunk, toCompile, successfullyCompiledFiles);
190 private static List<String> generateClasspath(CompileContext context, ModuleChunk chunk) {
191 final Set<String> cp = new LinkedHashSet<String>();
193 // IMPORTANT! must be the first in classpath
194 cp.add(ClasspathBootstrap.getResourcePath(GroovyCompilerWrapper.class).getPath());
196 for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.compile(context.isCompilingTests()), false)) {
197 cp.add(FileUtil.toCanonicalPath(file.getPath()));
199 for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.runtime(context.isCompilingTests()), false)) {
200 cp.add(FileUtil.toCanonicalPath(file.getPath()));
202 return new ArrayList<String>(cp);
205 private static boolean isGroovyFile(String path) {
206 return path.endsWith(".groovy") || path.endsWith(".gpp");
209 private static Map<String, String> buildClassToSourceMap(ModuleChunk chunk, CompileContext context, Set<String> toCompilePaths, String moduleOutputPath) throws Exception {
210 final Map<String, String> class2Src = new HashMap<String, String>();
211 for (Module module : chunk.getModules()) {
212 final String moduleName = module.getName().toLowerCase(Locale.US);
213 final SourceToOutputMapping srcToOut = context.getDataManager().getSourceToOutputMap(moduleName, context.isCompilingTests());
214 for (String src : srcToOut.getKeys()) {
215 if (!toCompilePaths.contains(src) && isGroovyFile(src)) {
216 final Collection<String> outs = srcToOut.getState(src);
218 for (String out : outs) {
219 if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) {
220 final String className = out.substring(moduleOutputPath.length(), out.length() - ".class".length()).replace('/', '.');
221 class2Src.put(className, src);
231 public String getDescription() {
232 return "Groovy builder";