/plugins/groovy/src/org/jetbrains/plugins/groovy/compiler/GroovyCompilerBase.java

https://bitbucket.org/nbargnesi/idea
Java | 425 lines | 348 code | 54 blank | 23 comment | 56 complexity | 0e0317fe48b118a332407355384deb49 MD5 | raw file
  1. /*
  2. * Copyright 2000-2012 JetBrains s.r.o.
  3. *
  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
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  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.
  15. */
  16. package org.jetbrains.plugins.groovy.compiler;
  17. import com.intellij.compiler.CompilerConfiguration;
  18. import com.intellij.compiler.impl.CompilerUtil;
  19. import com.intellij.compiler.impl.FileSetCompileScope;
  20. import com.intellij.compiler.impl.TranslatingCompilerFilesMonitor;
  21. import com.intellij.compiler.impl.javaCompiler.ModuleChunk;
  22. import com.intellij.compiler.impl.javaCompiler.OutputItemImpl;
  23. import com.intellij.compiler.make.CacheCorruptedException;
  24. import com.intellij.compiler.make.DependencyCache;
  25. import com.intellij.execution.ExecutionException;
  26. import com.intellij.execution.configurations.JavaParameters;
  27. import com.intellij.openapi.application.AccessToken;
  28. import com.intellij.openapi.application.ApplicationManager;
  29. import com.intellij.openapi.application.PathManager;
  30. import com.intellij.openapi.compiler.CompileContext;
  31. import com.intellij.openapi.compiler.CompilerMessageCategory;
  32. import com.intellij.openapi.compiler.CompilerPaths;
  33. import com.intellij.openapi.compiler.TranslatingCompiler;
  34. import com.intellij.openapi.compiler.ex.CompileContextEx;
  35. import com.intellij.openapi.diagnostic.Logger;
  36. import com.intellij.openapi.fileTypes.FileType;
  37. import com.intellij.openapi.fileTypes.StdFileTypes;
  38. import com.intellij.openapi.module.Module;
  39. import com.intellij.openapi.module.ModuleType;
  40. import com.intellij.openapi.progress.ProgressIndicator;
  41. import com.intellij.openapi.progress.ProgressManager;
  42. import com.intellij.openapi.project.Project;
  43. import com.intellij.openapi.projectRoots.JavaSdkType;
  44. import com.intellij.openapi.projectRoots.JdkUtil;
  45. import com.intellij.openapi.projectRoots.Sdk;
  46. import com.intellij.openapi.projectRoots.SdkTypeId;
  47. import com.intellij.openapi.roots.ContentIterator;
  48. import com.intellij.openapi.roots.ModuleFileIndex;
  49. import com.intellij.openapi.roots.ModuleRootManager;
  50. import com.intellij.openapi.roots.OrderRootType;
  51. import com.intellij.openapi.roots.libraries.Library;
  52. import com.intellij.openapi.util.Comparing;
  53. import com.intellij.openapi.util.io.FileUtil;
  54. import com.intellij.openapi.vfs.CharsetToolkit;
  55. import com.intellij.openapi.vfs.LocalFileSystem;
  56. import com.intellij.openapi.vfs.VfsUtil;
  57. import com.intellij.openapi.vfs.VirtualFile;
  58. import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
  59. import com.intellij.psi.PsiFile;
  60. import com.intellij.psi.PsiManager;
  61. import com.intellij.util.*;
  62. import com.intellij.util.cls.ClsFormatException;
  63. import com.intellij.util.containers.ContainerUtil;
  64. import com.intellij.util.net.HttpConfigurable;
  65. import gnu.trove.THashSet;
  66. import org.jetbrains.annotations.Nullable;
  67. import org.jetbrains.groovy.compiler.rt.GroovycRunner;
  68. import org.jetbrains.jps.incremental.groovy.GroovycOSProcessHandler;
  69. import org.jetbrains.jps.incremental.messages.BuildMessage;
  70. import org.jetbrains.jps.incremental.messages.CompilerMessage;
  71. import org.jetbrains.plugins.groovy.GroovyFileType;
  72. import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
  73. import org.jetbrains.plugins.groovy.extensions.GroovyScriptType;
  74. import org.jetbrains.plugins.groovy.extensions.GroovyScriptTypeDetector;
  75. import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
  76. import org.jetbrains.plugins.groovy.util.GroovyUtils;
  77. import java.io.File;
  78. import java.io.FileNotFoundException;
  79. import java.io.IOException;
  80. import java.nio.charset.Charset;
  81. import java.util.*;
  82. /**
  83. * @author peter
  84. */
  85. public abstract class GroovyCompilerBase implements TranslatingCompiler {
  86. private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.compiler.GroovyCompilerBase");
  87. protected final Project myProject;
  88. public GroovyCompilerBase(Project project) {
  89. myProject = project;
  90. }
  91. protected void runGroovycCompiler(final CompileContext compileContext, final Module module,
  92. final List<VirtualFile> toCompile,
  93. boolean forStubs,
  94. VirtualFile outputDir,
  95. OutputSink sink, boolean tests) {
  96. //assert !ApplicationManager.getApplication().isDispatchThread();
  97. final Sdk sdk = ModuleRootManager.getInstance(module).getSdk();
  98. assert sdk != null; //verified before
  99. SdkTypeId sdkType = sdk.getSdkType();
  100. assert sdkType instanceof JavaSdkType;
  101. final String exePath = ((JavaSdkType)sdkType).getVMExecutablePath(sdk);
  102. final JavaParameters parameters = new JavaParameters();
  103. final PathsList classPathBuilder = parameters.getClassPath();
  104. // IMPORTANT: must be the first entry to avoid collisions
  105. classPathBuilder.add(PathUtil.getJarPathForClass(GroovycRunner.class));
  106. final ModuleChunk chunk = createChunk(module, compileContext);
  107. final Library[] libraries = GroovyConfigUtils.getInstance().getSDKLibrariesByModule(module);
  108. if (libraries.length > 0) {
  109. classPathBuilder.addVirtualFiles(Arrays.asList(libraries[0].getFiles(OrderRootType.CLASSES)));
  110. }
  111. classPathBuilder.addVirtualFiles(chunk.getCompilationBootClasspathFiles(false));
  112. classPathBuilder.addVirtualFiles(chunk.getCompilationClasspathFiles(false));
  113. appendOutputPath(module, classPathBuilder, false);
  114. if (tests) {
  115. appendOutputPath(module, classPathBuilder, true);
  116. }
  117. final List<String> patchers = new SmartList<String>();
  118. AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
  119. try {
  120. for (final GroovyCompilerExtension extension : GroovyCompilerExtension.EP_NAME.getExtensions()) {
  121. extension.enhanceCompilationClassPath(chunk, classPathBuilder);
  122. patchers.addAll(extension.getCompilationUnitPatchers(chunk));
  123. }
  124. }
  125. finally {
  126. accessToken.finish();
  127. }
  128. final boolean profileGroovyc = "true".equals(System.getProperty("profile.groovy.compiler"));
  129. if (profileGroovyc) {
  130. parameters.getVMParametersList().defineProperty("java.library.path", PathManager.getBinPath());
  131. parameters.getVMParametersList().defineProperty("profile.groovy.compiler", "true");
  132. parameters.getVMParametersList().add("-agentlib:yjpagent=disablej2ee,disablealloc,sessionname=GroovyCompiler");
  133. classPathBuilder.add(PathManager.findFileInLibDirectory("yjp-controller-api-redist.jar").getAbsolutePath());
  134. }
  135. final GroovyCompilerConfiguration compilerConfiguration = GroovyCompilerConfiguration.getInstance(myProject);
  136. parameters.getVMParametersList().add("-Xmx" + compilerConfiguration.getHeapSize() + "m");
  137. if (profileGroovyc) {
  138. parameters.getVMParametersList().add("-XX:+HeapDumpOnOutOfMemoryError");
  139. }
  140. parameters.getVMParametersList().addAll(HttpConfigurable.getProxyCmdLineProperties());
  141. //debug
  142. //parameters.getVMParametersList().add("-Xdebug"); parameters.getVMParametersList().add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239");
  143. // Setting up process encoding according to locale
  144. final ArrayList<String> list = new ArrayList<String>();
  145. CompilerUtil.addLocaleOptions(list, false);
  146. for (String s : list) {
  147. parameters.getVMParametersList().add(s);
  148. }
  149. parameters.setMainClass(GroovycRunner.class.getName());
  150. final VirtualFile finalOutputDir = getMainOutput(compileContext, module, tests);
  151. if (finalOutputDir == null) {
  152. compileContext.addMessage(CompilerMessageCategory.ERROR, "No output directory for module " + module.getName() + (tests ? " tests" : " production"), null, -1, -1);
  153. return;
  154. }
  155. final Charset ideCharset = EncodingProjectManager.getInstance(myProject).getDefaultCharset();
  156. String encoding = ideCharset != null && !Comparing.equal(CharsetToolkit.getDefaultSystemCharset(), ideCharset) ? ideCharset.name() : null;
  157. Set<String> paths2Compile = ContainerUtil.map2Set(toCompile, new Function<VirtualFile, String>() {
  158. @Override
  159. public String fun(VirtualFile file) {
  160. return file.getPath();
  161. }
  162. });
  163. Map<String, String> class2Src = new HashMap<String, String>();
  164. for (VirtualFile file : enumerateGroovyFiles(module)) {
  165. if (!paths2Compile.contains(file.getPath())) {
  166. for (String name : TranslatingCompilerFilesMonitor.getInstance().getCompiledClassNames(file, myProject)) {
  167. class2Src.put(name, file.getPath());
  168. }
  169. }
  170. }
  171. final File fileWithParameters;
  172. try {
  173. fileWithParameters = GroovycOSProcessHandler
  174. .fillFileWithGroovycParameters(outputDir.getPath(), paths2Compile, FileUtil.toSystemDependentName(finalOutputDir.getPath()),
  175. class2Src, encoding, patchers);
  176. }
  177. catch (IOException e) {
  178. LOG.info(e);
  179. compileContext.addMessage(CompilerMessageCategory.ERROR, "Error creating a temp file to launch Groovy compiler: " + e.getMessage(), null, -1, -1);
  180. return;
  181. }
  182. parameters.getProgramParametersList().add(forStubs ? "stubs" : "groovyc");
  183. parameters.getProgramParametersList().add(fileWithParameters.getPath());
  184. if (compilerConfiguration.isInvokeDynamic()) {
  185. parameters.getProgramParametersList().add("--indy");
  186. }
  187. try {
  188. Process process = JdkUtil.setupJVMCommandLine(exePath, parameters, true).createProcess();
  189. GroovycOSProcessHandler processHandler = GroovycOSProcessHandler.runGroovyc(process, new Consumer<String>() {
  190. @Override
  191. public void consume(String s) {
  192. compileContext.getProgressIndicator().setText(s);
  193. }
  194. });
  195. final List<VirtualFile> toRecompile = new ArrayList<VirtualFile>();
  196. for (File toRecompileFile : processHandler.getToRecompileFiles()) {
  197. final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(toRecompileFile);
  198. LOG.assertTrue(vFile != null);
  199. toRecompile.add(vFile);
  200. }
  201. for (CompilerMessage compilerMessage : processHandler.getCompilerMessages()) {
  202. final String url = compilerMessage.getSourcePath();
  203. compileContext.addMessage(getMessageCategory(compilerMessage), compilerMessage.getMessageText(),
  204. url == null ? null : VfsUtil.pathToUrl(FileUtil.toSystemIndependentName(url)),
  205. (int)compilerMessage.getLine(),
  206. (int)compilerMessage.getColumn());
  207. }
  208. List<GroovycOSProcessHandler.OutputItem> outputItems = processHandler.getSuccessfullyCompiled();
  209. ArrayList<OutputItem> items = new ArrayList<OutputItem>();
  210. if (forStubs) {
  211. List<String> outputPaths = new ArrayList<String>();
  212. for (final GroovycOSProcessHandler.OutputItem outputItem : outputItems) {
  213. outputPaths.add(outputItem.outputPath);
  214. }
  215. addStubsToCompileScope(outputPaths, compileContext, module);
  216. }
  217. else {
  218. final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
  219. if (indicator != null) {
  220. indicator.setText("Updating caches...");
  221. }
  222. final DependencyCache dependencyCache = ((CompileContextEx)compileContext).getDependencyCache();
  223. for (GroovycOSProcessHandler.OutputItem outputItem : outputItems) {
  224. final VirtualFile sourceVirtualFile = LocalFileSystem.getInstance().findFileByIoFile(new File(outputItem.sourcePath));
  225. if (sourceVirtualFile == null) {
  226. continue;
  227. }
  228. if (indicator != null) {
  229. indicator.setText2(sourceVirtualFile.getName());
  230. }
  231. LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(outputItem.outputPath));
  232. items.add(new OutputItemImpl(outputItem.outputPath, sourceVirtualFile));
  233. final File classFile = new File(outputItem.outputPath);
  234. try {
  235. dependencyCache.reparseClassFile(classFile, FileUtil.loadFileBytes(classFile));
  236. }
  237. catch (ClsFormatException e) {
  238. LOG.error(e);
  239. }
  240. catch (CacheCorruptedException e) {
  241. LOG.error(e);
  242. }
  243. catch (FileNotFoundException ignored) {
  244. }
  245. catch (IOException e) {
  246. LOG.error(e);
  247. }
  248. }
  249. }
  250. sink.add(outputDir.getPath(), items, VfsUtil.toVirtualFileArray(toRecompile));
  251. }
  252. catch (ExecutionException e) {
  253. LOG.info(e);
  254. compileContext.addMessage(CompilerMessageCategory.ERROR, "Error running Groovy compiler: " + e.getMessage(), null, -1, -1);
  255. }
  256. }
  257. protected Set<VirtualFile> enumerateGroovyFiles(final Module module) {
  258. final Set<VirtualFile> moduleClasses = new THashSet<VirtualFile>();
  259. ModuleRootManager.getInstance(module).getFileIndex().iterateContent(new ContentIterator() {
  260. public boolean processFile(final VirtualFile vfile) {
  261. if (!vfile.isDirectory() &&
  262. GroovyFileType.GROOVY_FILE_TYPE.equals(vfile.getFileType())) {
  263. AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
  264. try {
  265. if (PsiManager.getInstance(myProject).findFile(vfile) instanceof GroovyFile) {
  266. moduleClasses.add(vfile);
  267. }
  268. }
  269. finally {
  270. accessToken.finish();
  271. }
  272. }
  273. return true;
  274. }
  275. });
  276. return moduleClasses;
  277. }
  278. protected static void addStubsToCompileScope(List<String> outputPaths, CompileContext compileContext, Module module) {
  279. List<VirtualFile> stubFiles = new ArrayList<VirtualFile>();
  280. for (String outputPath : outputPaths) {
  281. final File stub = new File(outputPath);
  282. CompilerUtil.refreshIOFile(stub);
  283. final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(stub);
  284. ContainerUtil.addIfNotNull(file, stubFiles);
  285. }
  286. ((CompileContextEx)compileContext).addScope(new FileSetCompileScope(stubFiles, new Module[]{module}));
  287. }
  288. @Nullable
  289. protected static VirtualFile getMainOutput(CompileContext compileContext, Module module, boolean tests) {
  290. return tests ? compileContext.getModuleOutputDirectoryForTests(module) : compileContext.getModuleOutputDirectory(module);
  291. }
  292. private static CompilerMessageCategory getMessageCategory(CompilerMessage compilerMessage) {
  293. BuildMessage.Kind category = compilerMessage.getKind();
  294. if (BuildMessage.Kind.ERROR.equals(category)) return CompilerMessageCategory.ERROR;
  295. if (BuildMessage.Kind.INFO.equals(category)) return CompilerMessageCategory.INFORMATION;
  296. if (BuildMessage.Kind.WARNING.equals(category)) return CompilerMessageCategory.WARNING;
  297. return CompilerMessageCategory.ERROR;
  298. }
  299. private static void appendOutputPath(Module module, PathsList compileClasspath, final boolean forTestClasses) {
  300. String output = CompilerPaths.getModuleOutputPath(module, forTestClasses);
  301. if (output != null) {
  302. compileClasspath.add(FileUtil.toSystemDependentName(output));
  303. }
  304. }
  305. private static ModuleChunk createChunk(Module module, CompileContext context) {
  306. return new ModuleChunk((CompileContextEx)context, new Chunk<Module>(module), Collections.<Module, List<VirtualFile>>emptyMap());
  307. }
  308. public void compile(final CompileContext compileContext, Chunk<Module> moduleChunk, final VirtualFile[] virtualFiles, OutputSink sink) {
  309. Map<Module, List<VirtualFile>> mapModulesToVirtualFiles;
  310. if (moduleChunk.getNodes().size() == 1) {
  311. mapModulesToVirtualFiles = Collections.singletonMap(moduleChunk.getNodes().iterator().next(), Arrays.asList(virtualFiles));
  312. }
  313. else {
  314. mapModulesToVirtualFiles = CompilerUtil.buildModuleToFilesMap(compileContext, virtualFiles);
  315. }
  316. for (final Module module : moduleChunk.getNodes()) {
  317. final List<VirtualFile> moduleFiles = mapModulesToVirtualFiles.get(module);
  318. if (moduleFiles == null) {
  319. continue;
  320. }
  321. final ModuleFileIndex index = ModuleRootManager.getInstance(module).getFileIndex();
  322. final List<VirtualFile> toCompile = new ArrayList<VirtualFile>();
  323. final List<VirtualFile> toCompileTests = new ArrayList<VirtualFile>();
  324. final CompilerConfiguration configuration = CompilerConfiguration.getInstance(myProject);
  325. final PsiManager psiManager = PsiManager.getInstance(myProject);
  326. if (GroovyUtils.isAcceptableModuleType(ModuleType.get(module))) {
  327. for (final VirtualFile file : moduleFiles) {
  328. if (shouldCompile(file, configuration, psiManager)) {
  329. (index.isInTestSourceContent(file) ? toCompileTests : toCompile).add(file);
  330. }
  331. }
  332. }
  333. if (!toCompile.isEmpty()) {
  334. compileFiles(compileContext, module, toCompile, sink, false);
  335. }
  336. if (!toCompileTests.isEmpty()) {
  337. compileFiles(compileContext, module, toCompileTests, sink, true);
  338. }
  339. }
  340. }
  341. private static boolean shouldCompile(final VirtualFile file, CompilerConfiguration configuration, final PsiManager manager) {
  342. if (configuration.isResourceFile(file)) {
  343. return false;
  344. }
  345. final FileType fileType = file.getFileType();
  346. if (fileType == GroovyFileType.GROOVY_FILE_TYPE) {
  347. AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock();
  348. try {
  349. PsiFile psiFile = manager.findFile(file);
  350. if (psiFile instanceof GroovyFile && ((GroovyFile)psiFile).isScript()) {
  351. final GroovyScriptType scriptType = GroovyScriptTypeDetector.getScriptType((GroovyFile)psiFile);
  352. return scriptType.shouldBeCompiled((GroovyFile)psiFile);
  353. }
  354. return true;
  355. }
  356. finally {
  357. accessToken.finish();
  358. }
  359. }
  360. return fileType == StdFileTypes.JAVA;
  361. }
  362. protected abstract void compileFiles(CompileContext compileContext, Module module,
  363. List<VirtualFile> toCompile, OutputSink sink, boolean tests);
  364. public boolean isCompilableFile(VirtualFile file, CompileContext context) {
  365. final boolean result = GroovyFileType.GROOVY_FILE_TYPE.equals(file.getFileType());
  366. if (result && LOG.isDebugEnabled()) {
  367. LOG.debug("compilable file: " + file.getPath());
  368. }
  369. return result;
  370. }
  371. }