2 * Copyright 2000-2009 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.
17 package org
.jetbrains
.plugins
.groovy
.compiler
;
19 import com
.intellij
.compiler
.CompilerConfiguration
;
20 import com
.intellij
.compiler
.impl
.CompilerUtil
;
21 import com
.intellij
.compiler
.impl
.FileSetCompileScope
;
22 import com
.intellij
.compiler
.impl
.javaCompiler
.ModuleChunk
;
23 import com
.intellij
.execution
.ExecutionException
;
24 import com
.intellij
.execution
.configurations
.GeneralCommandLine
;
25 import com
.intellij
.openapi
.application
.ApplicationManager
;
26 import com
.intellij
.openapi
.application
.PathManager
;
27 import com
.intellij
.openapi
.compiler
.CompileContext
;
28 import com
.intellij
.openapi
.compiler
.CompilerMessageCategory
;
29 import com
.intellij
.openapi
.compiler
.CompilerPaths
;
30 import com
.intellij
.openapi
.compiler
.TranslatingCompiler
;
31 import com
.intellij
.openapi
.compiler
.ex
.CompileContextEx
;
32 import com
.intellij
.openapi
.diagnostic
.Logger
;
33 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
34 import com
.intellij
.openapi
.module
.JavaModuleType
;
35 import com
.intellij
.openapi
.module
.Module
;
36 import com
.intellij
.openapi
.project
.Project
;
37 import com
.intellij
.openapi
.projectRoots
.JavaSdkType
;
38 import com
.intellij
.openapi
.projectRoots
.Sdk
;
39 import com
.intellij
.openapi
.projectRoots
.SdkType
;
40 import com
.intellij
.openapi
.roots
.ModuleFileIndex
;
41 import com
.intellij
.openapi
.roots
.ModuleRootManager
;
42 import com
.intellij
.openapi
.roots
.OrderRootType
;
43 import com
.intellij
.openapi
.roots
.libraries
.Library
;
44 import com
.intellij
.openapi
.util
.Comparing
;
45 import com
.intellij
.openapi
.util
.io
.FileUtil
;
46 import com
.intellij
.openapi
.vfs
.CharsetToolkit
;
47 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
48 import com
.intellij
.openapi
.vfs
.VfsUtil
;
49 import com
.intellij
.openapi
.vfs
.VirtualFile
;
50 import com
.intellij
.openapi
.vfs
.encoding
.EncodingProjectManager
;
51 import com
.intellij
.psi
.PsiClass
;
52 import com
.intellij
.psi
.PsiFile
;
53 import com
.intellij
.psi
.PsiManager
;
54 import com
.intellij
.util
.Chunk
;
55 import com
.intellij
.util
.PathUtil
;
56 import com
.intellij
.util
.PathsList
;
57 import com
.intellij
.util
.SmartList
;
58 import com
.intellij
.util
.containers
.ContainerUtil
;
59 import org
.jetbrains
.groovy
.compiler
.rt
.CompilerMessage
;
60 import org
.jetbrains
.groovy
.compiler
.rt
.GroovycRunner
;
61 import org
.jetbrains
.plugins
.groovy
.GroovyFileType
;
62 import org
.jetbrains
.plugins
.groovy
.config
.GroovyConfigUtils
;
63 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.GroovyFileBase
;
66 import java
.nio
.charset
.Charset
;
72 public abstract class GroovyCompilerBase
implements TranslatingCompiler
{
73 private static final Logger LOG
= Logger
.getInstance("#org.jetbrains.plugins.groovy.compiler.GroovyCompilerBase");
74 protected final Project myProject
;
76 public GroovyCompilerBase(Project project
) {
80 protected void runGroovycCompiler(CompileContext compileContext
, final Module module
,
81 final List
<VirtualFile
> toCompile
,
83 VirtualFile outputDir
,
84 OutputSink sink
, boolean tests
) {
85 GeneralCommandLine commandLine
= new GeneralCommandLine();
86 final Sdk sdk
= ModuleRootManager
.getInstance(module
).getSdk();
87 assert sdk
!= null; //verified before
88 SdkType sdkType
= sdk
.getSdkType();
89 assert sdkType
instanceof JavaSdkType
;
90 commandLine
.setExePath(((JavaSdkType
)sdkType
).getVMExecutablePath(sdk
));
92 final PathsList classPathBuilder
= new PathsList();
93 classPathBuilder
.add(PathUtil
.getJarPathForClass(GroovycRunner
.class));
95 final ModuleChunk chunk
= createChunk(module
, compileContext
);
97 final Library
[] libraries
= GroovyConfigUtils
.getInstance().getSDKLibrariesByModule(module
);
98 if (libraries
.length
> 0) {
99 classPathBuilder
.addVirtualFiles(Arrays
.asList(libraries
[0].getFiles(OrderRootType
.COMPILATION_CLASSES
)));
102 classPathBuilder
.addVirtualFiles(chunk
.getCompilationClasspathFiles());
103 appendOutputPath(module
, classPathBuilder
, false);
105 appendOutputPath(module
, classPathBuilder
, true);
108 final List
<String
> patchers
= new SmartList
<String
>();
109 for (final GroovyCompilerExtension extension
: GroovyCompilerExtension
.EP_NAME
.getExtensions()) {
110 extension
.enhanceCompilationClassPath(chunk
, classPathBuilder
);
111 patchers
.addAll(extension
.getCompilationUnitPatchers(chunk
));
114 if ("true".equals(System
.getProperty("profile.groovy.compiler"))) {
115 commandLine
.addParameter("-Djava.library.path=" + PathManager
.getBinPath());
116 commandLine
.addParameter("-Dprofile.groovy.compiler=true");
117 commandLine
.addParameter("-agentlib:yjpagent=disablej2ee,disablecounts,disablealloc,sessionname=GroovyCompiler");
118 classPathBuilder
.add(PathManager
.findFileInLibDirectory("yjp-controller-api-redist.jar").getAbsolutePath());
121 commandLine
.addParameter("-cp");
122 commandLine
.addParameter(classPathBuilder
.getPathsString());
125 commandLine
.addParameter("-Xmx" + GroovyCompilerConfiguration
.getInstance(myProject
).getHeapSize() + "m");
126 commandLine
.addParameter("-XX:+HeapDumpOnOutOfMemoryError");
129 //commandLine.addParameter("-Xdebug"); commandLine.addParameter("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239");
131 // Setting up process encoding according to locale
132 final ArrayList
<String
> list
= new ArrayList
<String
>();
133 CompilerUtil
.addLocaleOptions(list
, false);
134 commandLine
.addParameters(list
);
136 commandLine
.addParameter(GroovycRunner
.class.getName());
139 File fileWithParameters
= File
.createTempFile("toCompile", "");
140 fillFileWithGroovycParameters(toCompile
, fileWithParameters
, outputDir
, patchers
, getMainOutput(compileContext
, module
, tests
));
142 commandLine
.addParameter(forStubs ?
"stubs" : "groovyc");
143 commandLine
.addParameter(fileWithParameters
.getPath());
145 catch (IOException e
) {
149 GroovycOSProcessHandler processHandler
;
152 processHandler
= new GroovycOSProcessHandler(compileContext
, commandLine
.createProcess(), commandLine
.getCommandLineString(), sink
);
154 processHandler
.startNotify();
155 processHandler
.waitFor();
157 final List
<VirtualFile
> toRecompile
= new ArrayList
<VirtualFile
>();
158 Set
<File
> toRecompileFiles
= processHandler
.getToRecompileFiles();
159 for (File toRecompileFile
: toRecompileFiles
) {
160 final VirtualFile vFile
= LocalFileSystem
.getInstance().findFileByIoFile(toRecompileFile
);
161 LOG
.assertTrue(vFile
!= null);
162 toRecompile
.add(vFile
);
165 final List
<CompilerMessage
> messages
= processHandler
.getCompilerMessages();
166 for (CompilerMessage compilerMessage
: messages
) {
167 final CompilerMessageCategory category
;
168 category
= getMessageCategory(compilerMessage
);
170 final String url
= compilerMessage
.getUrl();
172 compileContext
.addMessage(category
, compilerMessage
.getMessage(), VfsUtil
.pathToUrl(FileUtil
.toSystemIndependentName(url
)), compilerMessage
.getLineNum(),
173 compilerMessage
.getColumnNum());
176 boolean hasMessages
= !messages
.isEmpty();
178 StringBuffer unparsedBuffer
= processHandler
.getUnparsedOutput();
179 if (unparsedBuffer
.length() != 0) {
180 compileContext
.addMessage(CompilerMessageCategory
.ERROR
, unparsedBuffer
.toString(), null, -1, -1);
184 final int exitCode
= processHandler
.getProcess().exitValue();
185 if (!hasMessages
&& exitCode
!= 0) {
186 compileContext
.addMessage(CompilerMessageCategory
.ERROR
, "Internal groovyc error: code " + exitCode
, null, -1, -1);
189 List
<OutputItem
> outputItems
= processHandler
.getSuccessfullyCompiled();
191 List
<VirtualFile
> stubFiles
= new ArrayList
<VirtualFile
>();
192 for (final OutputItem outputItem
: outputItems
) {
193 final File stub
= new File(outputItem
.getOutputPath());
194 CompilerUtil
.refreshIOFile(stub
);
195 final VirtualFile file
= LocalFileSystem
.getInstance().refreshAndFindFileByIoFile(stub
);
196 ContainerUtil
.addIfNotNull(file
, stubFiles
);
198 ((CompileContextEx
)compileContext
).addScope(new FileSetCompileScope(stubFiles
, new Module
[]{module
}));
199 outputItems
= Collections
.emptyList();
202 sink
.add(outputDir
.getPath(), outputItems
, toRecompile
.toArray(new VirtualFile
[toRecompile
.size()]));
204 catch (ExecutionException e
) {
209 protected static VirtualFile
getMainOutput(CompileContext compileContext
, Module module
, boolean tests
) {
210 return tests ? compileContext
.getModuleOutputDirectoryForTests(module
) : compileContext
.getModuleOutputDirectory(module
);
213 private static CompilerMessageCategory
getMessageCategory(CompilerMessage compilerMessage
) {
215 category
= compilerMessage
.getCategory();
217 if (CompilerMessage
.ERROR
.equals(category
)) return CompilerMessageCategory
.ERROR
;
218 if (CompilerMessage
.INFORMATION
.equals(category
)) return CompilerMessageCategory
.INFORMATION
;
219 if (CompilerMessage
.STATISTICS
.equals(category
)) return CompilerMessageCategory
.STATISTICS
;
220 if (CompilerMessage
.WARNING
.equals(category
)) return CompilerMessageCategory
.WARNING
;
222 return CompilerMessageCategory
.ERROR
;
225 private void fillFileWithGroovycParameters(List
<VirtualFile
> virtualFiles
, File f
, VirtualFile outputDir
, final List
<String
> patchers
, VirtualFile finalOutputDir
) {
226 if (LOG
.isDebugEnabled()) {
227 LOG
.debug("Running groovyc on: " + virtualFiles
.toString());
230 FileOutputStream stream
;
232 stream
= new FileOutputStream(f
);
234 catch (FileNotFoundException e
) {
239 final PrintStream printer
= new PrintStream(stream
);
241 for (final VirtualFile item
: virtualFiles
) {
242 printer
.println(GroovycRunner
.SRC_FILE
);
243 printer
.println(item
.getPath());
244 ApplicationManager
.getApplication().runReadAction(new Runnable() {
246 final PsiFile file
= PsiManager
.getInstance(myProject
).findFile(item
);
247 if (file
instanceof GroovyFileBase
) {
248 for (PsiClass psiClass
: ((GroovyFileBase
)file
).getClasses()) {
249 printer
.println(psiClass
.getQualifiedName());
254 printer
.println(GroovycRunner
.END
);
257 if (!patchers
.isEmpty()) {
258 printer
.println(GroovycRunner
.PATCHERS
);
259 for (final String patcher
: patchers
) {
260 printer
.println(patcher
);
262 printer
.println(GroovycRunner
.END
);
265 final Charset ideCharset
= EncodingProjectManager
.getInstance(myProject
).getDefaultCharset();
266 if (!Comparing
.equal(CharsetToolkit
.getDefaultSystemCharset(), ideCharset
)) {
267 printer
.println(GroovycRunner
.ENCODING
);
268 printer
.println(ideCharset
.name());
271 printer
.println(GroovycRunner
.OUTPUTPATH
);
272 printer
.println(PathUtil
.getLocalPath(outputDir
));
274 printer
.println(GroovycRunner
.FINAL_OUTPUTPATH
);
275 printer
.println(PathUtil
.getLocalPath(finalOutputDir
));
281 private static void appendOutputPath(Module module
, PathsList compileClasspath
, final boolean forTestClasses
) {
282 String output
= CompilerPaths
.getModuleOutputPath(module
, forTestClasses
);
283 if (output
!= null) {
284 compileClasspath
.add(FileUtil
.toSystemDependentName(output
));
288 private static ModuleChunk
createChunk(Module module
, CompileContext context
) {
289 return new ModuleChunk((CompileContextEx
)context
, new Chunk
<Module
>(module
), Collections
.<Module
, List
<VirtualFile
>>emptyMap());
292 public void compile(final CompileContext compileContext
, Chunk
<Module
> moduleChunk
, final VirtualFile
[] virtualFiles
, OutputSink sink
) {
293 Map
<Module
, List
<VirtualFile
>> mapModulesToVirtualFiles
;
294 if (moduleChunk
.getNodes().size() == 1) {
295 mapModulesToVirtualFiles
= Collections
.singletonMap(moduleChunk
.getNodes().iterator().next(), Arrays
.asList(virtualFiles
));
298 mapModulesToVirtualFiles
= CompilerUtil
.buildModuleToFilesMap(compileContext
, virtualFiles
);
300 for (final Module module
: moduleChunk
.getNodes()) {
301 final List
<VirtualFile
> moduleFiles
= mapModulesToVirtualFiles
.get(module
);
302 if (moduleFiles
== null) {
306 final ModuleFileIndex index
= ModuleRootManager
.getInstance(module
).getFileIndex();
307 final List
<VirtualFile
> toCompile
= new ArrayList
<VirtualFile
>();
308 final List
<VirtualFile
> toCompileTests
= new ArrayList
<VirtualFile
>();
309 final CompilerConfiguration configuration
= CompilerConfiguration
.getInstance(myProject
);
311 if (module
.getModuleType() instanceof JavaModuleType
) {
312 for (final VirtualFile file
: moduleFiles
) {
313 final boolean shouldCompile
= !configuration
.isResourceFile(file
) &&
314 (file
.getFileType() == GroovyFileType
.GROOVY_FILE_TYPE
||
315 file
.getFileType() == StdFileTypes
.JAVA
);
317 (index
.isInTestSourceContent(file
) ? toCompileTests
: toCompile
).add(file
);
322 if (!toCompile
.isEmpty()) {
323 compileFiles(compileContext
, module
, toCompile
, sink
, false);
325 if (!toCompileTests
.isEmpty()) {
326 compileFiles(compileContext
, module
, toCompileTests
, sink
, true);
333 protected abstract void compileFiles(CompileContext compileContext
, Module module
,
334 List
<VirtualFile
> toCompile
, OutputSink sink
, boolean tests
);
336 public boolean isCompilableFile(VirtualFile file
, CompileContext context
) {
337 final boolean result
= GroovyFileType
.GROOVY_FILE_TYPE
.equals(file
.getFileType());
338 if (result
&& LOG
.isDebugEnabled()) {
339 LOG
.debug("compilable file: " + file
.getPath());