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.SystemProperties;
7 import groovy.util.CharsetToolkit;
8 import org.jetbrains.annotations.Nullable;
9 import org.jetbrains.ether.dependencyView.Callbacks;
10 import org.jetbrains.ether.dependencyView.Mappings;
11 import org.jetbrains.groovy.compiler.rt.GroovyCompilerWrapper;
12 import org.jetbrains.jps.ClasspathKind;
13 import org.jetbrains.jps.Module;
14 import org.jetbrains.jps.ModuleChunk;
15 import org.jetbrains.jps.incremental.*;
16 import org.jetbrains.jps.incremental.java.JavaBuilder;
17 import org.jetbrains.jps.incremental.messages.CompilerMessage;
18 import org.jetbrains.jps.incremental.messages.FileGeneratedEvent;
19 import org.jetbrains.jps.incremental.messages.ProgressMessage;
20 import org.jetbrains.jps.incremental.storage.SourceToOutputMapping;
21 import org.jetbrains.jps.server.ClasspathBootstrap;
22 import org.objectweb.asm.ClassReader;
28 * @author Eugene Zhuravlev
31 public class GroovyBuilder extends ModuleLevelBuilder {
32 public static final String BUILDER_NAME = "groovy";
33 private final boolean myForStubs;
34 private final String myBuilderName;
36 public GroovyBuilder(boolean forStubs) {
37 myForStubs = forStubs;
38 myBuilderName = BUILDER_NAME + (forStubs ? "-stubs" : "-classes");
41 public String getName() {
45 public ModuleLevelBuilder.ExitCode build(final CompileContext context, ModuleChunk chunk) throws ProjectBuildException {
46 ExitCode exitCode = ExitCode.OK;
47 final List<File> toCompile = new ArrayList<File>();
49 context.processFilesToRecompile(chunk, new FileProcessor() {
51 public boolean apply(Module module, File file, String sourceRoot) throws Exception {
52 final String path = file.getPath();
53 if (isGroovyFile(path)) { //todo file type check
60 if (toCompile.isEmpty()) {
64 final Set<String> cp = new LinkedHashSet<String>();
66 // IMPORTANT! must be the first in classpath
67 cp.add(ClasspathBootstrap.getResourcePath(GroovyCompilerWrapper.class).getPath());
69 for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.compile(context.isCompilingTests()), false)) {
70 cp.add(FileUtil.toCanonicalPath(file.getPath()));
72 for (File file : context.getProjectPaths().getClasspathFiles(chunk, ClasspathKind.runtime(context.isCompilingTests()), false)) {
73 cp.add(FileUtil.toCanonicalPath(file.getPath()));
76 final File tempFile = FileUtil.createTempFile("ideaGroovyToCompile", ".txt", true);
77 final Module representativeModule = chunk.getModules().iterator().next();
78 File moduleOutputDir = context.getProjectPaths().getModuleOutputDir(representativeModule, context.isCompilingTests());
79 final File dir = myForStubs ? FileUtil.createTempDirectory(/*new File("/tmp/stubs/"), */"groovyStubs", null) : moduleOutputDir;
82 final Set<String> toCompilePaths = new LinkedHashSet<String>();
83 for (File file : toCompile) {
84 toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
87 String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
88 if (!moduleOutputPath.endsWith("/")) {
89 moduleOutputPath += "/";
91 Map<String, String> class2Src = buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutputPath);
93 String ideCharset = chunk.getProject().getProjectCharset();
94 String encoding = !Comparing.equal(CharsetToolkit.getDefaultSystemCharset().name(), ideCharset) ? ideCharset : null;
95 List<String> patchers = Collections.emptyList(); //todo patchers
96 GroovycOSProcessHandler.fillFileWithGroovycParameters(
97 tempFile, FileUtil.toCanonicalPath(dir.getPath()), toCompilePaths, FileUtil.toSystemDependentName(moduleOutputPath), class2Src, encoding, patchers
101 JavaBuilder.addTempSourcePathRoot(context, dir);
104 // todo CompilerUtil.addLocaleOptions()
105 //todo different outputs in a chunk
107 //todo module jdk path
108 final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
109 SystemProperties.getJavaHome() + "/bin/java",
110 "org.jetbrains.groovy.compiler.rt.GroovycRunner",
111 Collections.<String>emptyList(), new ArrayList<String>(cp),
112 Arrays.asList("-Xmx384m"/*, "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239"*/),
113 Arrays.<String>asList(myForStubs ? "stubs" : "groovyc", tempFile.getPath())
116 List<GroovycOSProcessHandler.OutputItem> successfullyCompiled = Collections.emptyList();
118 final Process process = Runtime.getRuntime().exec(cmd.toArray(new String[cmd.size()]));
119 GroovycOSProcessHandler handler = new GroovycOSProcessHandler(process, null) {
121 protected void updateStatus(@Nullable String status) {
122 context.processMessage(new ProgressMessage(status == null ? GROOVY_COMPILER_IN_OPERATION : status));
125 handler.startNotify();
128 successfullyCompiled = handler.getSuccessfullyCompiled();
130 for (CompilerMessage message : handler.getCompilerMessages()) {
131 context.processMessage(message);
137 final Mappings delta = context.createDelta();
138 final List<File> successfullyCompiledFiles = new ArrayList<File>();
139 if (!successfullyCompiled.isEmpty()) {
141 final Callbacks.Backend callback = delta.getCallback();
142 final FileGeneratedEvent generatedEvent = new FileGeneratedEvent();
144 for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) {
145 final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath);
146 final String outputPath = FileUtil.toSystemIndependentName(item.outputPath);
147 final RootDescriptor moduleAndRoot = context.getModuleAndRoot(new File(sourcePath));
148 if (moduleAndRoot != null) {
149 final String moduleName = moduleAndRoot.module.getName().toLowerCase(Locale.US);
150 context.getDataManager().getSourceToOutputMap(moduleName, moduleAndRoot.isTestRoot).appendData(sourcePath, outputPath);
152 callback.associate(outputPath, Callbacks.getDefaultLookup(sourcePath), new ClassReader(FileUtil.loadFileBytes(new File(outputPath))));
153 successfullyCompiledFiles.add(new File(sourcePath));
155 generatedEvent.add(moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/'));
158 context.processMessage(generatedEvent);
162 final boolean needSecondPass = updateMappings(context, delta, chunk, toCompile, successfullyCompiledFiles);
163 if (needSecondPass) {
164 exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED;
171 catch (Exception e) {
172 throw new ProjectBuildException(e);
176 private static boolean isGroovyFile(String path) {
177 return path.endsWith(".groovy") || path.endsWith(".gpp");
180 private static Map<String, String> buildClassToSourceMap(ModuleChunk chunk, CompileContext context, Set<String> toCompilePaths, String moduleOutputPath) throws Exception {
181 final Map<String, String> class2Src = new HashMap<String, String>();
182 for (Module module : chunk.getModules()) {
183 final String moduleName = module.getName().toLowerCase(Locale.US);
184 final SourceToOutputMapping srcToOut = context.getDataManager().getSourceToOutputMap(moduleName, context.isCompilingTests());
185 for (String src : srcToOut.getKeys()) {
186 if (!toCompilePaths.contains(src) && isGroovyFile(src)) {
187 final Collection<String> outs = srcToOut.getState(src);
189 for (String out : outs) {
190 if (out.endsWith(".class") && out.startsWith(moduleOutputPath)) {
191 final String className = out.substring(moduleOutputPath.length(), out.length() - ".class".length()).replace('/', '.');
192 class2Src.put(className, src);
202 public String getDescription() {
203 return "Groovy builder";