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.
18 * @author: Eugene Zhuravlev
22 package com
.intellij
.compiler
.impl
;
24 import com
.intellij
.CommonBundle
;
25 import com
.intellij
.analysis
.AnalysisScope
;
26 import com
.intellij
.compiler
.*;
27 import com
.intellij
.compiler
.make
.CacheCorruptedException
;
28 import com
.intellij
.compiler
.make
.CacheUtils
;
29 import com
.intellij
.compiler
.make
.DependencyCache
;
30 import com
.intellij
.compiler
.progress
.CompilerTask
;
31 import com
.intellij
.diagnostic
.IdeErrorsDialog
;
32 import com
.intellij
.diagnostic
.PluginException
;
33 import com
.intellij
.openapi
.application
.ApplicationManager
;
34 import com
.intellij
.openapi
.application
.ModalityState
;
35 import com
.intellij
.openapi
.compiler
.*;
36 import com
.intellij
.openapi
.compiler
.Compiler
;
37 import com
.intellij
.openapi
.compiler
.ex
.CompileContextEx
;
38 import com
.intellij
.openapi
.compiler
.ex
.CompilerPathsEx
;
39 import com
.intellij
.openapi
.diagnostic
.Logger
;
40 import com
.intellij
.openapi
.extensions
.Extensions
;
41 import com
.intellij
.openapi
.extensions
.PluginId
;
42 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
43 import com
.intellij
.openapi
.fileTypes
.FileType
;
44 import com
.intellij
.openapi
.fileTypes
.FileTypeManager
;
45 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
46 import com
.intellij
.openapi
.module
.LanguageLevelUtil
;
47 import com
.intellij
.openapi
.module
.Module
;
48 import com
.intellij
.openapi
.module
.ModuleManager
;
49 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
50 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
51 import com
.intellij
.openapi
.progress
.ProgressManager
;
52 import com
.intellij
.openapi
.project
.DumbService
;
53 import com
.intellij
.openapi
.project
.Project
;
54 import com
.intellij
.openapi
.project
.ProjectBundle
;
55 import com
.intellij
.openapi
.projectRoots
.Sdk
;
56 import com
.intellij
.openapi
.roots
.ContentEntry
;
57 import com
.intellij
.openapi
.roots
.ModuleRootManager
;
58 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
59 import com
.intellij
.openapi
.roots
.SourceFolder
;
60 import com
.intellij
.openapi
.roots
.ex
.ProjectRootManagerEx
;
61 import com
.intellij
.openapi
.roots
.ui
.configuration
.CommonContentEntriesEditor
;
62 import com
.intellij
.openapi
.roots
.ui
.configuration
.ProjectSettingsService
;
63 import com
.intellij
.openapi
.ui
.MessageType
;
64 import com
.intellij
.openapi
.ui
.Messages
;
65 import com
.intellij
.openapi
.util
.*;
66 import com
.intellij
.openapi
.util
.io
.FileUtil
;
67 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
68 import com
.intellij
.openapi
.vfs
.VfsUtil
;
69 import com
.intellij
.openapi
.vfs
.VirtualFile
;
70 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
71 import com
.intellij
.openapi
.vfs
.newvfs
.RefreshQueue
;
72 import com
.intellij
.openapi
.wm
.StatusBar
;
73 import com
.intellij
.openapi
.wm
.ToolWindowId
;
74 import com
.intellij
.openapi
.wm
.ToolWindowManager
;
75 import com
.intellij
.openapi
.wm
.WindowManager
;
76 import com
.intellij
.packageDependencies
.DependenciesBuilder
;
77 import com
.intellij
.packageDependencies
.ForwardDependenciesBuilder
;
78 import com
.intellij
.pom
.java
.LanguageLevel
;
79 import com
.intellij
.psi
.PsiCompiledElement
;
80 import com
.intellij
.psi
.PsiDocumentManager
;
81 import com
.intellij
.psi
.PsiFile
;
82 import com
.intellij
.psi
.PsiManager
;
83 import com
.intellij
.util
.Chunk
;
84 import com
.intellij
.util
.LocalTimeCounter
;
85 import com
.intellij
.util
.StringBuilderSpinAllocator
;
86 import com
.intellij
.util
.ThrowableRunnable
;
87 import com
.intellij
.util
.containers
.ContainerUtil
;
88 import com
.intellij
.util
.containers
.HashMap
;
89 import com
.intellij
.util
.containers
.OrderedSet
;
90 import gnu
.trove
.TObjectHashingStrategy
;
91 import org
.jetbrains
.annotations
.NonNls
;
92 import org
.jetbrains
.annotations
.NotNull
;
96 import java
.util
.concurrent
.CountDownLatch
;
98 public class CompileDriver
{
99 private static final Logger LOG
= Logger
.getInstance("#com.intellij.compiler.impl.CompileDriver");
101 private final Project myProject
;
102 private final Map
<Pair
<IntermediateOutputCompiler
, Module
>, Pair
<VirtualFile
, VirtualFile
>> myGenerationCompilerModuleToOutputDirMap
; // [IntermediateOutputCompiler, Module] -> [ProductionSources, TestSources]
103 private final String myCachesDirectoryPath
;
104 private boolean myShouldClearOutputDirectory
;
106 private final Map
<Module
, String
> myModuleOutputPaths
= new HashMap
<Module
, String
>();
107 private final Map
<Module
, String
> myModuleTestOutputPaths
= new HashMap
<Module
, String
>();
109 @NonNls private static final String VERSION_FILE_NAME
= "version.dat";
110 @NonNls private static final String LOCK_FILE_NAME
= "in_progress.dat";
112 @NonNls private static final boolean GENERATE_CLASSPATH_INDEX
= "true".equals(System
.getProperty("generate.classpath.index"));
114 private static final FileProcessingCompilerAdapterFactory FILE_PROCESSING_COMPILER_ADAPTER_FACTORY
= new FileProcessingCompilerAdapterFactory() {
115 public FileProcessingCompilerAdapter
create(CompileContext context
, FileProcessingCompiler compiler
) {
116 return new FileProcessingCompilerAdapter(context
, compiler
);
119 private static final FileProcessingCompilerAdapterFactory FILE_PACKAGING_COMPILER_ADAPTER_FACTORY
= new FileProcessingCompilerAdapterFactory() {
120 public FileProcessingCompilerAdapter
create(CompileContext context
, FileProcessingCompiler compiler
) {
121 return new PackagingCompilerAdapter(context
, (PackagingCompiler
)compiler
);
124 private CompilerFilter myCompilerFilter
= CompilerFilter
.ALL
;
125 private static CompilerFilter SOURCE_PROCESSING_ONLY
= new CompilerFilter() {
126 public boolean acceptCompiler(Compiler compiler
) {
127 return compiler
instanceof SourceProcessingCompiler
;
130 private static CompilerFilter ALL_EXCEPT_SOURCE_PROCESSING
= new CompilerFilter() {
131 public boolean acceptCompiler(Compiler compiler
) {
132 return !SOURCE_PROCESSING_ONLY
.acceptCompiler(compiler
);
136 private OutputPathFinder myOutputFinder
; // need this for updating zip archives (experimental feature)
138 private Set
<File
> myAllOutputDirectories
;
139 private static final long ONE_MINUTE_MS
= 60L /*sec*/ * 1000L /*millisec*/;
141 public CompileDriver(Project project
) {
143 myCachesDirectoryPath
= CompilerPaths
.getCacheStoreDirectory(myProject
).getPath().replace('/', File
.separatorChar
);
144 myShouldClearOutputDirectory
= CompilerWorkspaceConfiguration
.getInstance(myProject
).CLEAR_OUTPUT_DIRECTORY
;
146 myGenerationCompilerModuleToOutputDirMap
= new HashMap
<Pair
<IntermediateOutputCompiler
, Module
>, Pair
<VirtualFile
, VirtualFile
>>();
148 final IntermediateOutputCompiler
[] generatingCompilers
= CompilerManager
.getInstance(myProject
).getCompilers(IntermediateOutputCompiler
.class, myCompilerFilter
);
149 if (generatingCompilers
.length
> 0) {
150 final Module
[] allModules
= ModuleManager
.getInstance(myProject
).getModules();
151 for (IntermediateOutputCompiler compiler
: generatingCompilers
) {
152 for (final Module module
: allModules
) {
153 final VirtualFile productionOutput
= lookupVFile(compiler
, module
, false);
154 final VirtualFile testOutput
= lookupVFile(compiler
, module
, true);
155 final Pair
<IntermediateOutputCompiler
, Module
> pair
= new Pair
<IntermediateOutputCompiler
, Module
>(compiler
, module
);
156 final Pair
<VirtualFile
, VirtualFile
> outputs
= new Pair
<VirtualFile
, VirtualFile
>(productionOutput
, testOutput
);
157 myGenerationCompilerModuleToOutputDirMap
.put(pair
, outputs
);
163 public void setCompilerFilter(CompilerFilter compilerFilter
) {
164 myCompilerFilter
= compilerFilter
== null? CompilerFilter
.ALL
: compilerFilter
;
167 public void rebuild(CompileStatusNotification callback
) {
168 doRebuild(callback
, null, true, addAdditionalRoots(new ProjectCompileScope(myProject
), ALL_EXCEPT_SOURCE_PROCESSING
));
171 public void make(CompileScope scope
, CompileStatusNotification callback
) {
172 scope
= addAdditionalRoots(scope
, ALL_EXCEPT_SOURCE_PROCESSING
);
173 if (validateCompilerConfiguration(scope
, false)) {
174 startup(scope
, false, false, callback
, null, true, false);
178 public boolean isUpToDate(CompileScope scope
) {
179 if (LOG
.isDebugEnabled()) {
180 LOG
.debug("isUpToDate operation started");
182 scope
= addAdditionalRoots(scope
, ALL_EXCEPT_SOURCE_PROCESSING
);
184 final CompilerTask task
= new CompilerTask(myProject
, true, "", true);
185 final CompileContextImpl compileContext
=
186 new CompileContextImpl(myProject
, task
, scope
, createDependencyCache(), true, false);
188 checkCachesVersion(compileContext
);
189 if (compileContext
.isRebuildRequested()) {
190 if (LOG
.isDebugEnabled()) {
191 LOG
.debug("Rebuild requested, up-to-date=false");
196 for (Map
.Entry
<Pair
<IntermediateOutputCompiler
, Module
>, Pair
<VirtualFile
, VirtualFile
>> entry
: myGenerationCompilerModuleToOutputDirMap
.entrySet()) {
197 final Pair
<VirtualFile
, VirtualFile
> outputs
= entry
.getValue();
198 Module module
= entry
.getKey().getSecond();
199 compileContext
.assignModule(outputs
.getFirst(), module
, false);
200 compileContext
.assignModule(outputs
.getSecond(), module
, true);
203 final Ref
<ExitStatus
> status
= new Ref
<ExitStatus
>();
205 task
.start(new Runnable() {
208 myAllOutputDirectories
= getAllOutputDirectories();
209 // need this for updating zip archives experiment, uncomment if the feature is turned on
210 //myOutputFinder = new OutputPathFinder(myAllOutputDirectories);
211 status
.set(doCompile(compileContext
, false, false, false, true));
214 compileContext
.commitZipFiles(); // just to be on the safe side; normally should do nothing if called in isUpToDate()
219 if (LOG
.isDebugEnabled()) {
220 LOG
.debug("isUpToDate operation finished");
223 return ExitStatus
.UP_TO_DATE
.equals(status
.get());
226 private DependencyCache
createDependencyCache() {
227 return new DependencyCache(myCachesDirectoryPath
+ File
.separator
+ ".dependency-info");
230 public void compile(CompileScope scope
, CompileStatusNotification callback
, boolean trackDependencies
) {
231 if (trackDependencies
) {
232 scope
= new TrackDependenciesScope(scope
);
234 if (validateCompilerConfiguration(scope
, false)) {
235 startup(scope
, false, true, callback
, null, true, trackDependencies
);
239 private static class CompileStatus
{
240 final int CACHE_FORMAT_VERSION
;
241 final boolean COMPILATION_IN_PROGRESS
;
243 private CompileStatus(int cacheVersion
, boolean isCompilationInProgress
) {
244 CACHE_FORMAT_VERSION
= cacheVersion
;
245 COMPILATION_IN_PROGRESS
= isCompilationInProgress
;
249 private CompileStatus
readStatus() {
250 final boolean isInProgress
= getLockFile().exists();
253 final File versionFile
= new File(myCachesDirectoryPath
, VERSION_FILE_NAME
);
254 DataInputStream in
= new DataInputStream(new FileInputStream(versionFile
));
256 version
= in
.readInt();
262 catch (FileNotFoundException e
) {
265 catch (IOException e
) {
266 LOG
.info(e
); // may happen in case of IDEA crashed and the file is not written properly
269 return new CompileStatus(version
, isInProgress
);
272 private void writeStatus(CompileStatus status
, CompileContext context
) {
273 final File statusFile
= new File(myCachesDirectoryPath
, VERSION_FILE_NAME
);
275 final File lockFile
= getLockFile();
277 FileUtil
.createIfDoesntExist(statusFile
);
278 DataOutputStream out
= new DataOutputStream(new FileOutputStream(statusFile
));
280 out
.writeInt(status
.CACHE_FORMAT_VERSION
);
285 if (status
.COMPILATION_IN_PROGRESS
) {
286 FileUtil
.createIfDoesntExist(lockFile
);
289 deleteFile(lockFile
);
292 catch (IOException e
) {
293 context
.addMessage(CompilerMessageCategory
.ERROR
, CompilerBundle
.message("compiler.error.exception", e
.getMessage()), null, -1, -1);
297 private File
getLockFile() {
298 return new File(CompilerPaths
.getCompilerSystemDirectory(myProject
), LOCK_FILE_NAME
);
301 private void doRebuild(CompileStatusNotification callback
,
302 CompilerMessage message
,
303 final boolean checkCachesVersion
,
304 final CompileScope compileScope
) {
305 if (validateCompilerConfiguration(compileScope
, true)) {
306 startup(compileScope
, true, false, callback
, message
, checkCachesVersion
, false);
310 private CompileScope
addAdditionalRoots(CompileScope originalScope
, final CompilerFilter filter
) {
311 CompileScope scope
= attachIntermediateOutputDirectories(originalScope
, filter
);
313 final AdditionalCompileScopeProvider
[] scopeProviders
= Extensions
.getExtensions(AdditionalCompileScopeProvider
.EXTENSION_POINT_NAME
);
314 CompileScope baseScope
= scope
;
315 for (AdditionalCompileScopeProvider scopeProvider
: scopeProviders
) {
316 final CompileScope additionalScope
= scopeProvider
.getAdditionalScope(baseScope
);
317 if (additionalScope
!= null) {
318 scope
= new CompositeScope(scope
, additionalScope
);
324 private CompileScope
attachIntermediateOutputDirectories(CompileScope originalScope
, CompilerFilter filter
) {
325 CompileScope scope
= originalScope
;
326 final Set
<Module
> affected
= new HashSet
<Module
>(Arrays
.asList(originalScope
.getAffectedModules()));
327 for (Map
.Entry
<Pair
<IntermediateOutputCompiler
, Module
>, Pair
<VirtualFile
, VirtualFile
>> entry
: myGenerationCompilerModuleToOutputDirMap
.entrySet()) {
328 final Module module
= entry
.getKey().getSecond();
329 if (affected
.contains(module
) && filter
.acceptCompiler(entry
.getKey().getFirst())) {
330 final Pair
<VirtualFile
, VirtualFile
> outputs
= entry
.getValue();
331 scope
= new CompositeScope(scope
, new FileSetCompileScope(Arrays
.asList(outputs
.getFirst(), outputs
.getSecond()), new Module
[]{module
}));
337 public static final Key
<Long
> COMPILATION_START_TIMESTAMP
= Key
.create("COMPILATION_START_TIMESTAMP");
339 private void startup(final CompileScope scope
,
340 final boolean isRebuild
,
341 final boolean forceCompile
,
342 final CompileStatusNotification callback
,
343 final CompilerMessage message
,
344 final boolean checkCachesVersion
,
345 final boolean trackDependencies
) {
346 final CompilerTask compileTask
= new CompilerTask(myProject
, CompilerWorkspaceConfiguration
.getInstance(myProject
).COMPILE_IN_BACKGROUND
,
348 ? CompilerBundle
.message("compiler.content.name.compile")
349 : CompilerBundle
.message("compiler.content.name.make"), false);
350 final WindowManager windowManager
= WindowManager
.getInstance();
351 if (windowManager
!= null) {
352 windowManager
.getStatusBar(myProject
).setInfo("");
355 PsiDocumentManager
.getInstance(myProject
).commitAllDocuments();
356 FileDocumentManager
.getInstance().saveAllDocuments();
358 final DependencyCache dependencyCache
= createDependencyCache();
359 final CompileContextImpl compileContext
=
360 new CompileContextImpl(myProject
, compileTask
, scope
, dependencyCache
, !isRebuild
&& !forceCompile
, isRebuild
);
361 compileContext
.putUserData(COMPILATION_START_TIMESTAMP
, LocalTimeCounter
.currentTime());
362 for (Map
.Entry
<Pair
<IntermediateOutputCompiler
, Module
>, Pair
<VirtualFile
, VirtualFile
>> entry
: myGenerationCompilerModuleToOutputDirMap
.entrySet()) {
363 final Pair
<VirtualFile
, VirtualFile
> outputs
= entry
.getValue();
364 final Module module
= entry
.getKey().getSecond();
365 compileContext
.assignModule(outputs
.getFirst(), module
, false);
366 compileContext
.assignModule(outputs
.getSecond(), module
, true);
369 compileTask
.start(new Runnable() {
371 long start
= System
.currentTimeMillis();
373 if (myProject
.isDisposed()) {
376 LOG
.info("COMPILATION STARTED");
377 if (message
!= null) {
378 compileContext
.addMessage(message
);
380 TranslatingCompilerFilesMonitor
.getInstance().ensureInitializationCompleted(myProject
);
381 doCompile(compileContext
, isRebuild
, forceCompile
, callback
, checkCachesVersion
, trackDependencies
);
384 compileContext
.commitZipFiles();
385 CompilerUtil
.logDuration("Refreshing VFS in total", CompilerUtil
.ourRefreshTime
);
386 CompilerUtil
.ourRefreshTime
= 0L;
387 final long finish
= System
.currentTimeMillis();
388 CompilerUtil
.logDuration(
389 "\tCOMPILATION FINISHED; Errors: " +
390 compileContext
.getMessageCount(CompilerMessageCategory
.ERROR
) +
392 compileContext
.getMessageCount(CompilerMessageCategory
.WARNING
),
395 //if (LOG.isDebugEnabled()) {
396 // LOG.debug("COMPILATION FINISHED");
403 final int rv
= Messages
.showDialog(
404 myProject
, "You are about to rebuild the whole project.\nRun 'Make Project' instead?", "Confirm Project Rebuild",
405 new String
[]{"Make", "Rebuild"}, 0, Messages
.getQuestionIcon()
407 if (rv
== 0 /*yes, please, do run make*/) {
408 startup(scope
, false, false, callback
, null, checkCachesVersion
, trackDependencies
);
412 startup(scope
, isRebuild
, forceCompile
, callback
, message
, checkCachesVersion
, trackDependencies
);
417 private void doCompile(final CompileContextImpl compileContext
,
418 final boolean isRebuild
,
419 final boolean forceCompile
,
420 final CompileStatusNotification callback
,
421 final boolean checkCachesVersion
,
422 final boolean trackDependencies
) {
423 ExitStatus status
= ExitStatus
.ERRORS
;
424 boolean wereExceptions
= false;
426 if (checkCachesVersion
) {
427 checkCachesVersion(compileContext
);
428 if (compileContext
.isRebuildRequested()) {
432 writeStatus(new CompileStatus(CompilerConfigurationImpl
.DEPENDENCY_FORMAT_VERSION
, true), compileContext
);
433 if (compileContext
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
437 myAllOutputDirectories
= getAllOutputDirectories();
438 // need this for updating zip archives experiment, uncomment if the feature is turned on
439 //myOutputFinder = new OutputPathFinder(myAllOutputDirectories);
440 status
= doCompile(compileContext
, isRebuild
, forceCompile
, trackDependencies
, false);
442 catch (Throwable ex
) {
443 wereExceptions
= true;
444 final PluginId pluginId
= IdeErrorsDialog
.findPluginId(ex
);
446 final StringBuffer message
= new StringBuffer();
447 message
.append("Internal error");
448 if (pluginId
!= null) {
449 message
.append(" (Plugin: ").append(pluginId
).append(")");
451 message
.append(": ").append(ex
.getMessage());
452 compileContext
.addMessage(CompilerMessageCategory
.ERROR
, message
.toString(), null, -1, -1);
454 if (pluginId
!= null) {
455 throw new PluginException(ex
, pluginId
);
457 throw new RuntimeException(ex
);
460 dropDependencyCache(compileContext
);
461 final ExitStatus _status
= status
;
462 if (compileContext
.isRebuildRequested()) {
463 ApplicationManager
.getApplication().invokeLater(new Runnable() {
465 doRebuild(callback
, new CompilerMessageImpl(myProject
, CompilerMessageCategory
.INFORMATION
, compileContext
.getRebuildReason(),
466 null, -1, -1, null), false, compileContext
.getCompileScope());
468 }, ModalityState
.NON_MODAL
);
471 final long duration
= System
.currentTimeMillis() - compileContext
.getStartCompilationStamp();
472 writeStatus(new CompileStatus(CompilerConfigurationImpl
.DEPENDENCY_FORMAT_VERSION
, wereExceptions
), compileContext
);
473 ApplicationManager
.getApplication().invokeLater(new Runnable() {
475 final int errorCount
= compileContext
.getMessageCount(CompilerMessageCategory
.ERROR
);
476 final int warningCount
= compileContext
.getMessageCount(CompilerMessageCategory
.WARNING
);
477 final String statusMessage
= createStatusMessage(_status
, warningCount
, errorCount
);
478 final StatusBar statusBar
= WindowManager
.getInstance().getStatusBar(myProject
);
479 if (statusBar
!= null) { // because this code is in invoke later, the code may work for already closed project
480 // in case another project was opened in the frame while the compiler was working (See SCR# 28591)
481 statusBar
.setInfo(statusMessage
);
482 if (duration
> ONE_MINUTE_MS
) {
483 final MessageType messageType
= errorCount
> 0 ? MessageType
.ERROR
: warningCount
> 0 ? MessageType
.WARNING
: MessageType
.INFO
;
484 ToolWindowManager
.getInstance(myProject
).notifyByBalloon(ToolWindowId
.MESSAGES_WINDOW
, messageType
, statusMessage
);
487 if (_status
!= ExitStatus
.UP_TO_DATE
&& compileContext
.getMessageCount(null) > 0) {
488 compileContext
.addMessage(CompilerMessageCategory
.INFORMATION
, statusMessage
, null, -1, -1);
490 if (callback
!= null) {
491 callback
.finished(_status
== ExitStatus
.CANCELLED
, errorCount
, warningCount
, compileContext
);
494 }, ModalityState
.NON_MODAL
);
499 private void checkCachesVersion(final CompileContextImpl compileContext
) {
500 final CompileStatus compileStatus
= readStatus();
501 if (compileStatus
== null) {
502 compileContext
.requestRebuildNextTime(CompilerBundle
.message("error.compiler.caches.corrupted"));
504 else if (compileStatus
.CACHE_FORMAT_VERSION
!= -1 &&
505 compileStatus
.CACHE_FORMAT_VERSION
!= CompilerConfigurationImpl
.DEPENDENCY_FORMAT_VERSION
) {
506 compileContext
.requestRebuildNextTime(CompilerBundle
.message("error.caches.old.format"));
508 else if (compileStatus
.COMPILATION_IN_PROGRESS
) {
509 compileContext
.requestRebuildNextTime(CompilerBundle
.message("error.previous.compilation.failed"));
513 private static String
createStatusMessage(final ExitStatus status
, final int warningCount
, final int errorCount
) {
514 if (status
== ExitStatus
.CANCELLED
) {
515 return CompilerBundle
.message("status.compilation.aborted");
517 if (status
== ExitStatus
.UP_TO_DATE
) {
518 return CompilerBundle
.message("status.all.up.to.date");
520 if (status
== ExitStatus
.SUCCESS
) {
521 return warningCount
> 0
522 ? CompilerBundle
.message("status.compilation.completed.successfully.with.warnings", warningCount
)
523 : CompilerBundle
.message("status.compilation.completed.successfully");
525 return CompilerBundle
.message("status.compilation.completed.successfully.with.warnings.and.errors", errorCount
, warningCount
);
528 private static class ExitStatus
{
529 private final String myName
;
531 private ExitStatus(@NonNls String name
) {
535 public String
toString() {
539 public static final ExitStatus CANCELLED
= new ExitStatus("CANCELLED");
540 public static final ExitStatus ERRORS
= new ExitStatus("ERRORS");
541 public static final ExitStatus SUCCESS
= new ExitStatus("SUCCESS");
542 public static final ExitStatus UP_TO_DATE
= new ExitStatus("UP_TO_DATE");
545 private static class ExitException
extends Exception
{
546 private final ExitStatus myStatus
;
548 private ExitException(ExitStatus status
) {
552 public ExitStatus
getExitStatus() {
557 private ExitStatus
doCompile(final CompileContextEx context
,
559 final boolean forceCompile
,
560 final boolean trackDependencies
, final boolean onlyCheckStatus
) {
564 if (context
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
565 if (LOG
.isDebugEnabled()) {
566 logErrorMessages(context
);
568 return ExitStatus
.ERRORS
;
572 if (!onlyCheckStatus
) {
573 if (!executeCompileTasks(context
, true)) {
574 if (LOG
.isDebugEnabled()) {
575 LOG
.debug("Compilation cancelled");
577 return ExitStatus
.CANCELLED
;
581 if (context
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
582 if (LOG
.isDebugEnabled()) {
583 logErrorMessages(context
);
585 return ExitStatus
.ERRORS
;
588 final long refreshStart
= System
.currentTimeMillis();
590 // need this to make sure the VFS is built
591 boolean needRecalcOutputDirs
= false;
592 //final List<VirtualFile> outputsToRefresh = new ArrayList<VirtualFile>();
594 final VirtualFile
[] all
= context
.getAllOutputDirectories();
596 final ProgressIndicator progressIndicator
= context
.getProgressIndicator();
598 final int totalCount
= all
.length
+ myGenerationCompilerModuleToOutputDirMap
.size() * 2;
599 final CountDownLatch latch
= new CountDownLatch(totalCount
);
600 final Runnable decCount
= new Runnable() {
603 progressIndicator
.setFraction(((double)(totalCount
- latch
.getCount())) / totalCount
);
606 progressIndicator
.pushState();
607 progressIndicator
.setText("Inspecting output directories...");
608 final boolean asyncMode
= !ApplicationManager
.getApplication().isDispatchThread(); // must not lock awt thread with latch.await()
610 for (VirtualFile output
: all
) {
611 if (output
.isValid()) {
612 walkChildren(output
, context
);
615 needRecalcOutputDirs
= true;
616 final File file
= new File(output
.getPath());
617 if (!file
.exists()) {
618 final boolean created
= file
.mkdirs();
620 context
.addMessage(CompilerMessageCategory
.ERROR
, "Failed to create output directory " + file
.getPath(), null, 0, 0);
621 return ExitStatus
.ERRORS
;
624 output
= LocalFileSystem
.getInstance().refreshAndFindFileByIoFile(file
);
625 if (output
== null) {
626 context
.addMessage(CompilerMessageCategory
.ERROR
, "Failed to locate output directory " + file
.getPath(), null, 0, 0);
627 return ExitStatus
.ERRORS
;
630 output
.refresh(asyncMode
, true, decCount
);
631 //outputsToRefresh.add(output);
633 for (Pair
<IntermediateOutputCompiler
, Module
> pair
: myGenerationCompilerModuleToOutputDirMap
.keySet()) {
634 final Pair
<VirtualFile
, VirtualFile
> generated
= myGenerationCompilerModuleToOutputDirMap
.get(pair
);
635 walkChildren(generated
.getFirst(), context
);
636 //outputsToRefresh.add(generated.getFirst());
637 generated
.getFirst().refresh(asyncMode
, true, decCount
);
638 walkChildren(generated
.getSecond(), context
);
639 //outputsToRefresh.add(generated.getSecond());
640 generated
.getSecond().refresh(asyncMode
, true, decCount
);
643 //RefreshQueue.getInstance().refresh(false, true, null, outputsToRefresh.toArray(new VirtualFile[outputsToRefresh.size()]));
645 latch
.await(); // wait until all threads are refreshed
647 catch (InterruptedException e
) {
652 progressIndicator
.popState();
655 final long initialRefreshTime
= System
.currentTimeMillis() - refreshStart
;
656 CompilerUtil
.logDuration("Initial VFS refresh", initialRefreshTime
);
657 CompilerUtil
.ourRefreshTime
+= initialRefreshTime
;
659 DumbService
.getInstance(myProject
).waitForSmartMode();
661 if (needRecalcOutputDirs
) {
662 context
.recalculateOutputDirs();
665 boolean didSomething
= false;
667 final CompilerManager compilerManager
= CompilerManager
.getInstance(myProject
);
670 didSomething
|= generateSources(compilerManager
, context
, forceCompile
, onlyCheckStatus
);
672 didSomething
|= invokeFileProcessingCompilers(compilerManager
, context
, SourceInstrumentingCompiler
.class,
673 FILE_PROCESSING_COMPILER_ADAPTER_FACTORY
, forceCompile
, true, onlyCheckStatus
);
675 didSomething
|= invokeFileProcessingCompilers(compilerManager
, context
, SourceProcessingCompiler
.class,
676 FILE_PROCESSING_COMPILER_ADAPTER_FACTORY
, forceCompile
, true, onlyCheckStatus
);
678 final CompileScope intermediateSources
= attachIntermediateOutputDirectories(new CompositeScope(CompileScope
.EMPTY_ARRAY
) {
680 public Module
[] getAffectedModules() {
681 return context
.getCompileScope().getAffectedModules();
683 }, SOURCE_PROCESSING_ONLY
);
684 context
.addScope(intermediateSources
);
686 didSomething
|= translate(context
, compilerManager
, forceCompile
, isRebuild
, trackDependencies
, onlyCheckStatus
);
688 didSomething
|= invokeFileProcessingCompilers(compilerManager
, context
, ClassInstrumentingCompiler
.class,
689 FILE_PROCESSING_COMPILER_ADAPTER_FACTORY
, isRebuild
, false, onlyCheckStatus
);
691 // explicitly passing forceCompile = false because in scopes that is narrower than ProjectScope it is impossible
692 // to understand whether the class to be processed is in scope or not. Otherwise compiler may process its items even if
693 // there were changes in completely independent files.
694 didSomething
|= invokeFileProcessingCompilers(compilerManager
, context
, ClassPostProcessingCompiler
.class,
695 FILE_PROCESSING_COMPILER_ADAPTER_FACTORY
, isRebuild
, false, onlyCheckStatus
);
697 didSomething
|= invokeFileProcessingCompilers(compilerManager
, context
, PackagingCompiler
.class,
698 FILE_PACKAGING_COMPILER_ADAPTER_FACTORY
,
699 isRebuild
, false, onlyCheckStatus
);
701 didSomething
|= invokeFileProcessingCompilers(compilerManager
, context
, Validator
.class, FILE_PROCESSING_COMPILER_ADAPTER_FACTORY
,
702 forceCompile
, true, onlyCheckStatus
);
704 catch (ExitException e
) {
705 if (LOG
.isDebugEnabled()) {
707 logErrorMessages(context
);
709 return e
.getExitStatus();
712 // drop in case it has not been dropped yet.
713 dropDependencyCache(context
);
715 final VirtualFile
[] allOutputDirs
= context
.getAllOutputDirectories();
717 if (didSomething
&& GENERATE_CLASSPATH_INDEX
) {
718 CompilerUtil
.runInContext(context
, "Generating classpath index...", new ThrowableRunnable
<RuntimeException
>(){
721 for (VirtualFile file
: allOutputDirs
) {
722 context
.getProgressIndicator().setFraction((double)++count
/ allOutputDirs
.length
);
723 createClasspathIndex(file
);
729 if (!context
.getProgressIndicator().isCanceled() && context
.getMessageCount(CompilerMessageCategory
.ERROR
) == 0) {
730 RefreshQueue
.getInstance().refresh(true, true, new Runnable() {
732 CompilerDirectoryTimestamp
.updateTimestamp(Arrays
.asList(allOutputDirs
));
738 if (!onlyCheckStatus
) {
739 if (!executeCompileTasks(context
, false)) {
740 return ExitStatus
.CANCELLED
;
744 if (context
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
745 if (LOG
.isDebugEnabled()) {
746 logErrorMessages(context
);
748 return ExitStatus
.ERRORS
;
751 return ExitStatus
.UP_TO_DATE
;
753 return ExitStatus
.SUCCESS
;
755 catch (ProcessCanceledException e
) {
756 return ExitStatus
.CANCELLED
;
760 private static void logErrorMessages(final CompileContext context
) {
761 final CompilerMessage
[] errors
= context
.getMessages(CompilerMessageCategory
.ERROR
);
762 if (errors
.length
> 0) {
763 LOG
.debug("Errors reported: ");
764 for (CompilerMessage error
: errors
) {
765 LOG
.debug("\t" + error
.getMessage());
770 private static void walkChildren(VirtualFile from
, final CompileContext context
) {
771 final VirtualFile
[] files
= from
.getChildren();
772 if (files
!= null && files
.length
> 0) {
773 context
.getProgressIndicator().checkCanceled();
774 context
.getProgressIndicator().setText2(from
.getPresentableUrl());
775 for (VirtualFile file
: files
) {
776 walkChildren(file
, context
);
781 private static void createClasspathIndex(final VirtualFile file
) {
783 BufferedWriter writer
= new BufferedWriter(new FileWriter(new File(VfsUtil
.virtualToIoFile(file
), "classpath.index")));
785 writeIndex(writer
, file
, file
);
791 catch (IOException e
) {
792 // Ignore. Failed to create optional classpath index
796 private static void writeIndex(final BufferedWriter writer
, final VirtualFile root
, final VirtualFile file
) throws IOException
{
797 writer
.write(VfsUtil
.getRelativePath(file
, root
, '/'));
800 for (VirtualFile child
: file
.getChildren()) {
801 writeIndex(writer
, root
, child
);
805 private static void dropDependencyCache(final CompileContextEx context
) {
806 CompilerUtil
.runInContext(context
, CompilerBundle
.message("progress.saving.caches"), new ThrowableRunnable
<RuntimeException
>(){
808 context
.getDependencyCache().resetState();
813 private boolean generateSources(final CompilerManager compilerManager
,
814 CompileContextEx context
,
815 final boolean forceCompile
,
816 final boolean onlyCheckStatus
) throws ExitException
{
817 boolean didSomething
= false;
819 final SourceGeneratingCompiler
[] sourceGenerators
= compilerManager
.getCompilers(SourceGeneratingCompiler
.class, myCompilerFilter
);
820 for (final SourceGeneratingCompiler sourceGenerator
: sourceGenerators
) {
821 if (context
.getProgressIndicator().isCanceled()) {
822 throw new ExitException(ExitStatus
.CANCELLED
);
825 final boolean generatedSomething
= generateOutput(context
, sourceGenerator
, forceCompile
, onlyCheckStatus
);
827 if (context
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
828 throw new ExitException(ExitStatus
.ERRORS
);
830 didSomething
|= generatedSomething
;
835 private boolean translate(final CompileContextEx context
,
836 final CompilerManager compilerManager
,
837 final boolean forceCompile
,
839 final boolean trackDependencies
, final boolean onlyCheckStatus
) throws ExitException
{
841 boolean didSomething
= false;
843 final TranslatingCompiler
[] translators
= compilerManager
.getCompilers(TranslatingCompiler
.class, myCompilerFilter
);
846 final List
<Chunk
<Module
>> sortedChunks
= Collections
.unmodifiableList(ApplicationManager
.getApplication().runReadAction(new Computable
<List
<Chunk
<Module
>>>() {
847 public List
<Chunk
<Module
>> compute() {
848 final ModuleManager moduleManager
= ModuleManager
.getInstance(myProject
);
849 return ModuleCompilerUtil
.getSortedModuleChunks(myProject
, Arrays
.asList(moduleManager
.getModules()));
854 VirtualFile
[] snapshot
= null;
855 final Map
<Chunk
<Module
>, Collection
<VirtualFile
>> chunkMap
= new HashMap
<Chunk
<Module
>, Collection
<VirtualFile
>>();
858 for (final Chunk
<Module
> currentChunk
: sortedChunks
) {
859 final TranslatorsOutputSink sink
= new TranslatorsOutputSink(context
, translators
);
860 final Set
<FileType
> generatedTypes
= new HashSet
<FileType
>();
861 Collection
<VirtualFile
> chunkFiles
= chunkMap
.get(currentChunk
);
863 for (int currentCompiler
= 0, translatorsLength
= translators
.length
; currentCompiler
< translatorsLength
; currentCompiler
++) {
864 sink
.setCurrentCompilerIndex(currentCompiler
);
865 final TranslatingCompiler compiler
= translators
[currentCompiler
];
866 if (context
.getProgressIndicator().isCanceled()) {
867 throw new ExitException(ExitStatus
.CANCELLED
);
870 DumbService
.getInstance(myProject
).waitForSmartMode();
872 if (snapshot
== null || ContainerUtil
.intersects(generatedTypes
, compilerManager
.getRegisteredInputTypes(compiler
))) {
873 // rescan snapshot if previously generated files may influence the input of this compiler
874 snapshot
= ApplicationManager
.getApplication().runReadAction(new Computable
<VirtualFile
[]>() {
875 public VirtualFile
[] compute() {
876 return context
.getCompileScope().getFiles(null, true);
879 final Map
<Module
, List
<VirtualFile
>> moduleToFilesMap
= CompilerUtil
.buildModuleToFilesMap(context
, snapshot
);
880 for (Chunk
<Module
> moduleChunk
: sortedChunks
) {
881 List
<VirtualFile
> files
= Collections
.emptyList();
882 for (Module module
: moduleChunk
.getNodes()) {
883 final List
<VirtualFile
> moduleFiles
= moduleToFilesMap
.get(module
);
884 if (moduleFiles
!= null) {
885 files
= ContainerUtil
.concat(files
, moduleFiles
);
888 chunkMap
.put(moduleChunk
, files
);
890 total
= snapshot
.length
* translatorsLength
;
891 chunkFiles
= chunkMap
.get(currentChunk
);
894 final CompileContextEx _context
;
895 if (compiler
instanceof IntermediateOutputCompiler
) {
896 // wrap compile context so that output goes into intermediate directories
897 final IntermediateOutputCompiler _compiler
= (IntermediateOutputCompiler
)compiler
;
898 _context
= new CompileContextExProxy(context
) {
899 public VirtualFile
getModuleOutputDirectory(final Module module
) {
900 return getGenerationOutputDir(_compiler
, module
, false);
903 public VirtualFile
getModuleOutputDirectoryForTests(final Module module
) {
904 return getGenerationOutputDir(_compiler
, module
, true);
911 final boolean compiledSomething
=
912 compileSources(_context
, currentChunk
, compiler
, chunkFiles
, forceCompile
, isRebuild
, trackDependencies
, onlyCheckStatus
, sink
);
914 processed
+= chunkFiles
.size();
915 _context
.getProgressIndicator().setFraction(((double)processed
) / total
);
917 if (compiledSomething
) {
918 generatedTypes
.addAll(compilerManager
.getRegisteredOutputTypes(compiler
));
921 if (_context
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
922 throw new ExitException(ExitStatus
.ERRORS
);
925 didSomething
|= compiledSomething
;
929 if (context
.getMessageCount(CompilerMessageCategory
.ERROR
) == 0) {
930 // perform update only if there were no errors, so it is guaranteed that the file was processd by all neccesary compilers
931 sink
.flushPostponedItems();
936 catch (ProcessCanceledException e
) {
937 ProgressManager
.getInstance().executeNonCancelableSection(new Runnable() {
940 final Collection
<VirtualFile
> deps
= CacheUtils
.findDependentFiles(context
, Collections
.<VirtualFile
>emptySet(), null, null);
941 if (deps
.size() > 0) {
942 TranslatingCompilerFilesMonitor
.getInstance().update(context
, null, Collections
.<TranslatingCompiler
.OutputItem
>emptyList(), deps
.toArray(new VirtualFile
[deps
.size()]));
945 catch (IOException ignored
) {
948 catch (CacheCorruptedException ignored
) {
956 dropDependencyCache(context
);
958 TranslatingCompilerFilesMonitor
.getInstance().updateOutputRootsLayout(myProject
);
964 private interface FileProcessingCompilerAdapterFactory
{
965 FileProcessingCompilerAdapter
create(CompileContext context
, FileProcessingCompiler compiler
);
968 private boolean invokeFileProcessingCompilers(final CompilerManager compilerManager
,
969 CompileContextEx context
,
970 Class
<?
extends FileProcessingCompiler
> fileProcessingCompilerClass
,
971 FileProcessingCompilerAdapterFactory factory
,
972 boolean forceCompile
,
973 final boolean checkScope
,
974 final boolean onlyCheckStatus
) throws ExitException
{
975 boolean didSomething
= false;
976 final FileProcessingCompiler
[] compilers
= compilerManager
.getCompilers(fileProcessingCompilerClass
, myCompilerFilter
);
977 if (compilers
.length
> 0) {
979 CacheDeferredUpdater cacheUpdater
= new CacheDeferredUpdater();
981 for (final FileProcessingCompiler compiler
: compilers
) {
982 if (context
.getProgressIndicator().isCanceled()) {
983 throw new ExitException(ExitStatus
.CANCELLED
);
986 CompileContextEx _context
= context
;
987 if (compiler
instanceof IntermediateOutputCompiler
) {
988 final IntermediateOutputCompiler _compiler
= (IntermediateOutputCompiler
)compiler
;
989 _context
= new CompileContextExProxy(context
) {
990 public VirtualFile
getModuleOutputDirectory(final Module module
) {
991 return getGenerationOutputDir(_compiler
, module
, false);
994 public VirtualFile
getModuleOutputDirectoryForTests(final Module module
) {
995 return getGenerationOutputDir(_compiler
, module
, true);
1000 final boolean processedSomething
= processFiles(factory
.create(_context
, compiler
), forceCompile
, checkScope
, onlyCheckStatus
, cacheUpdater
);
1002 if (context
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
1003 throw new ExitException(ExitStatus
.ERRORS
);
1006 didSomething
|= processedSomething
;
1010 cacheUpdater
.doUpdate();
1013 catch (IOException e
) {
1015 context
.requestRebuildNextTime(e
.getMessage());
1016 throw new ExitException(ExitStatus
.ERRORS
);
1018 catch (ProcessCanceledException e
) {
1021 catch (ExitException e
) {
1024 catch (Exception e
) {
1025 context
.addMessage(CompilerMessageCategory
.ERROR
, CompilerBundle
.message("compiler.error.exception", e
.getMessage()), null, -1, -1);
1030 return didSomething
;
1033 private static Map
<Module
, Set
<GeneratingCompiler
.GenerationItem
>> buildModuleToGenerationItemMap(GeneratingCompiler
.GenerationItem
[] items
) {
1034 final Map
<Module
, Set
<GeneratingCompiler
.GenerationItem
>> map
= new HashMap
<Module
, Set
<GeneratingCompiler
.GenerationItem
>>();
1035 for (GeneratingCompiler
.GenerationItem item
: items
) {
1036 Module module
= item
.getModule();
1037 LOG
.assertTrue(module
!= null);
1038 Set
<GeneratingCompiler
.GenerationItem
> itemSet
= map
.get(module
);
1039 if (itemSet
== null) {
1040 itemSet
= new HashSet
<GeneratingCompiler
.GenerationItem
>();
1041 map
.put(module
, itemSet
);
1048 private void deleteAll(final CompileContextEx context
) {
1049 CompilerUtil
.runInContext(context
, CompilerBundle
.message("progress.clearing.output"), new ThrowableRunnable
<RuntimeException
>() {
1051 final boolean isTestMode
= ApplicationManager
.getApplication().isUnitTestMode();
1052 final VirtualFile
[] allSources
= context
.getProjectCompileScope().getFiles(null, true);
1053 if (myShouldClearOutputDirectory
) {
1054 clearOutputDirectories(myAllOutputDirectories
);
1056 else { // refresh is still required
1058 for (final Compiler compiler
: CompilerManager
.getInstance(myProject
).getCompilers(Compiler
.class)) {
1060 if (compiler
instanceof GeneratingCompiler
) {
1061 final StateCache
<ValidityState
> cache
= getGeneratingCompilerCache((GeneratingCompiler
)compiler
);
1062 final Iterator
<String
> urlIterator
= cache
.getUrlsIterator();
1063 while (urlIterator
.hasNext()) {
1064 context
.getProgressIndicator().checkCanceled();
1065 deleteFile(new File(VirtualFileManager
.extractPath(urlIterator
.next())));
1068 else if (compiler
instanceof TranslatingCompiler
) {
1069 final ArrayList
<Trinity
<File
, String
, Boolean
>> toDelete
= new ArrayList
<Trinity
<File
, String
, Boolean
>>();
1070 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1072 TranslatingCompilerFilesMonitor
.getInstance()
1073 .collectFiles(context
, (TranslatingCompiler
)compiler
, Arrays
.<VirtualFile
>asList(allSources
).iterator(), true
1074 /*pass true to make sure that every source in scope file is processed*/, false
1075 /*important! should pass false to enable collection of files to delete*/,
1076 new ArrayList
<VirtualFile
>(), toDelete
);
1079 for (Trinity
<File
, String
, Boolean
> trinity
: toDelete
) {
1080 context
.getProgressIndicator().checkCanceled();
1081 final File file
= trinity
.getFirst();
1084 CompilerManagerImpl
.addDeletedPath(file
.getPath());
1089 catch (IOException e
) {
1093 pruneEmptyDirectories(context
.getProgressIndicator(), myAllOutputDirectories
); // to avoid too much files deleted events
1096 CompilerUtil
.refreshIODirectories(myAllOutputDirectories
);
1101 clearCompilerSystemDirectory(context
);
1106 private void dropScopesCaches() {
1107 // hack to be sure the classpath will include the output directories
1108 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1110 ((ProjectRootManagerEx
)ProjectRootManager
.getInstance(myProject
)).clearScopesCachesForModules();
1115 private static void pruneEmptyDirectories(ProgressIndicator progress
, final Set
<File
> directories
) {
1116 for (File directory
: directories
) {
1117 doPrune(progress
, directory
, directories
);
1121 private static boolean doPrune(ProgressIndicator progress
, final File directory
, final Set
<File
> outPutDirectories
) {
1122 progress
.checkCanceled();
1123 final File
[] files
= directory
.listFiles();
1124 boolean isEmpty
= true;
1125 if (files
!= null) {
1126 for (File file
: files
) {
1127 if (!outPutDirectories
.contains(file
)) {
1128 if (doPrune(progress
, file
, outPutDirectories
)) {
1147 private Set
<File
> getAllOutputDirectories() {
1148 final Set
<File
> outputDirs
= new OrderedSet
<File
>((TObjectHashingStrategy
<File
>)TObjectHashingStrategy
.CANONICAL
);
1149 for (final String path
: CompilerPathsEx
.getOutputPaths(ModuleManager
.getInstance(myProject
).getModules())) {
1150 outputDirs
.add(new File(path
));
1155 private void clearOutputDirectories(final Set
<File
> _outputDirectories
) {
1156 final long start
= System
.currentTimeMillis();
1157 // do not delete directories themselves, or we'll get rootsChanged() otherwise
1158 final List
<File
> outputDirectories
= new ArrayList
<File
>(_outputDirectories
);
1159 for (Pair
<IntermediateOutputCompiler
, Module
> pair
: myGenerationCompilerModuleToOutputDirMap
.keySet()) {
1160 outputDirectories
.add(new File(CompilerPaths
.getGenerationOutputPath(pair
.getFirst(), pair
.getSecond(), false)));
1161 outputDirectories
.add(new File(CompilerPaths
.getGenerationOutputPath(pair
.getFirst(), pair
.getSecond(), true)));
1163 Collection
<File
> filesToDelete
= new ArrayList
<File
>(outputDirectories
.size() * 2);
1164 for (File outputDirectory
: outputDirectories
) {
1165 File
[] files
= outputDirectory
.listFiles();
1166 if (files
!= null) {
1167 filesToDelete
.addAll(Arrays
.asList(files
));
1170 FileUtil
.asyncDelete(filesToDelete
);
1172 // ensure output directories exist
1173 for (final File file
: outputDirectories
) {
1176 final long clearStop
= System
.currentTimeMillis();
1178 CompilerUtil
.refreshIODirectories(outputDirectories
);
1180 final long refreshStop
= System
.currentTimeMillis();
1182 CompilerUtil
.logDuration("Clearing output dirs", clearStop
- start
);
1183 CompilerUtil
.logDuration("Refreshing output directories", refreshStop
- clearStop
);
1186 private void clearCompilerSystemDirectory(final CompileContextEx context
) {
1187 CompilerCacheManager
.getInstance(myProject
).clearCaches(context
);
1188 FileUtil
.delete(CompilerPathsEx
.getZipStoreDirectory(myProject
));
1189 dropDependencyCache(context
);
1191 for (Pair
<IntermediateOutputCompiler
, Module
> pair
: myGenerationCompilerModuleToOutputDirMap
.keySet()) {
1192 final File
[] outputs
= {
1193 new File(CompilerPaths
.getGenerationOutputPath(pair
.getFirst(), pair
.getSecond(), false)),
1194 new File(CompilerPaths
.getGenerationOutputPath(pair
.getFirst(), pair
.getSecond(), true))
1196 for (File output
: outputs
) {
1197 final File
[] files
= output
.listFiles();
1198 if (files
!= null) {
1199 for (final File file
: files
) {
1200 final boolean deleteOk
= deleteFile(file
);
1202 context
.addMessage(CompilerMessageCategory
.ERROR
, CompilerBundle
.message("compiler.error.failed.to.delete", file
.getPath()),
1212 * @param file a file to delete
1213 * @return true if and only if the file existed and was successfully deleted
1214 * Note: the behaviour is different from FileUtil.delete() which returns true if the file absent on the disk
1216 private static boolean deleteFile(final File file
) {
1217 File
[] files
= file
.listFiles();
1218 if (files
!= null) {
1219 for (File file1
: files
) {
1224 for (int i
= 0; i
< 10; i
++){
1225 if (file
.delete()) {
1228 if (!file
.exists()) {
1234 catch (InterruptedException ignored
) {
1240 private VirtualFile
getGenerationOutputDir(final IntermediateOutputCompiler compiler
, final Module module
, final boolean forTestSources
) {
1241 final Pair
<VirtualFile
, VirtualFile
> outputs
=
1242 myGenerationCompilerModuleToOutputDirMap
.get(new Pair
<IntermediateOutputCompiler
, Module
>(compiler
, module
));
1243 return forTestSources? outputs
.getSecond() : outputs
.getFirst();
1246 private boolean generateOutput(final CompileContextEx context
,
1247 final GeneratingCompiler compiler
,
1248 final boolean forceGenerate
,
1249 final boolean onlyCheckStatus
) throws ExitException
{
1250 final GeneratingCompiler
.GenerationItem
[] allItems
= compiler
.getGenerationItems(context
);
1251 final List
<GeneratingCompiler
.GenerationItem
> toGenerate
= new ArrayList
<GeneratingCompiler
.GenerationItem
>();
1252 final List
<File
> filesToRefresh
= new ArrayList
<File
>();
1253 final List
<File
> generatedFiles
= new ArrayList
<File
>();
1254 final List
<Module
> affectedModules
= new ArrayList
<Module
>();
1256 final StateCache
<ValidityState
> cache
= getGeneratingCompilerCache(compiler
);
1257 final Set
<String
> pathsToRemove
= new HashSet
<String
>(cache
.getUrls());
1259 final Map
<GeneratingCompiler
.GenerationItem
, String
> itemToOutputPathMap
= new HashMap
<GeneratingCompiler
.GenerationItem
, String
>();
1260 final IOException
[] ex
= {null};
1261 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1263 for (final GeneratingCompiler
.GenerationItem item
: allItems
) {
1264 final Module itemModule
= item
.getModule();
1265 final String outputDirPath
= CompilerPaths
.getGenerationOutputPath(compiler
, itemModule
, item
.isTestSource());
1266 final String outputPath
= outputDirPath
+ "/" + item
.getPath();
1267 itemToOutputPathMap
.put(item
, outputPath
);
1270 final ValidityState savedState
= cache
.getState(outputPath
);
1272 if (forceGenerate
|| savedState
== null || !savedState
.equalsTo(item
.getValidityState())) {
1273 final String outputPathUrl
= VirtualFileManager
.constructUrl(LocalFileSystem
.PROTOCOL
, outputPath
);
1274 if (context
.getCompileScope().belongs(outputPathUrl
)) {
1275 toGenerate
.add(item
);
1278 pathsToRemove
.remove(outputPath
);
1282 pathsToRemove
.remove(outputPath
);
1285 catch (IOException e
) {
1291 if (ex
[0] != null) {
1295 if (onlyCheckStatus
) {
1296 if (toGenerate
.isEmpty() && pathsToRemove
.isEmpty()) {
1299 if (LOG
.isDebugEnabled()) {
1300 if (!toGenerate
.isEmpty()) {
1301 LOG
.debug("Found items to generate, compiler " + compiler
.getDescription());
1303 if (!pathsToRemove
.isEmpty()) {
1304 LOG
.debug("Found paths to remove, compiler " + compiler
.getDescription());
1307 throw new ExitException(ExitStatus
.CANCELLED
);
1310 if (!pathsToRemove
.isEmpty()) {
1311 CompilerUtil
.runInContext(context
, CompilerBundle
.message("progress.synchronizing.output.directory"), new ThrowableRunnable
<IOException
>(){
1312 public void run() throws IOException
{
1313 for (final String path
: pathsToRemove
) {
1314 final File file
= new File(path
);
1315 final boolean deleted
= deleteFile(file
);
1318 filesToRefresh
.add(file
);
1325 final Map
<Module
, Set
<GeneratingCompiler
.GenerationItem
>> moduleToItemMap
=
1326 buildModuleToGenerationItemMap(toGenerate
.toArray(new GeneratingCompiler
.GenerationItem
[toGenerate
.size()]));
1327 List
<Module
> modules
= new ArrayList
<Module
>(moduleToItemMap
.size());
1328 for (final Module module
: moduleToItemMap
.keySet()) {
1329 modules
.add(module
);
1331 ModuleCompilerUtil
.sortModules(myProject
, modules
);
1333 for (final Module module
: modules
) {
1334 CompilerUtil
.runInContext(context
, "Generating output from "+compiler
.getDescription(),new ThrowableRunnable
<IOException
>(){
1335 public void run() throws IOException
{
1336 final Set
<GeneratingCompiler
.GenerationItem
> items
= moduleToItemMap
.get(module
);
1337 if (items
!= null && !items
.isEmpty()) {
1338 final GeneratingCompiler
.GenerationItem
[][] productionAndTestItems
= splitGenerationItems(items
);
1339 for (GeneratingCompiler
.GenerationItem
[] _items
: productionAndTestItems
) {
1340 if (_items
.length
== 0) continue;
1341 final VirtualFile outputDir
= getGenerationOutputDir(compiler
, module
, _items
[0].isTestSource());
1342 final GeneratingCompiler
.GenerationItem
[] successfullyGenerated
= compiler
.generate(context
, _items
, outputDir
);
1344 CompilerUtil
.runInContext(context
, CompilerBundle
.message("progress.updating.caches"), new ThrowableRunnable
<IOException
>() {
1345 public void run() throws IOException
{
1346 if (successfullyGenerated
.length
> 0) {
1347 affectedModules
.add(module
);
1349 for (final GeneratingCompiler
.GenerationItem item
: successfullyGenerated
) {
1350 final String fullOutputPath
= itemToOutputPathMap
.get(item
);
1351 cache
.update(fullOutputPath
, item
.getValidityState());
1352 final File file
= new File(fullOutputPath
);
1353 filesToRefresh
.add(file
);
1354 generatedFiles
.add(file
);
1355 context
.getProgressIndicator().setText2(file
.getPath());
1365 catch (IOException e
) {
1367 context
.requestRebuildNextTime(e
.getMessage());
1368 throw new ExitException(ExitStatus
.ERRORS
);
1371 CompilerUtil
.refreshIOFiles(filesToRefresh
);
1372 if (!generatedFiles
.isEmpty()) {
1373 DumbService
.getInstance(myProject
).waitForSmartMode();
1374 List
<VirtualFile
> vFiles
= ApplicationManager
.getApplication().runReadAction(new Computable
<List
<VirtualFile
>>() {
1375 public List
<VirtualFile
> compute() {
1376 final ArrayList
<VirtualFile
> vFiles
= new ArrayList
<VirtualFile
>(generatedFiles
.size());
1377 for (File generatedFile
: generatedFiles
) {
1378 final VirtualFile vFile
= LocalFileSystem
.getInstance().findFileByIoFile(generatedFile
);
1379 if (vFile
!= null) {
1386 if (forceGenerate
) {
1387 context
.addScope(new FileSetCompileScope(vFiles
, affectedModules
.toArray(new Module
[affectedModules
.size()])));
1389 context
.markGenerated(vFiles
);
1392 return !toGenerate
.isEmpty() || !filesToRefresh
.isEmpty();
1395 private static GeneratingCompiler
.GenerationItem
[][] splitGenerationItems(final Set
<GeneratingCompiler
.GenerationItem
> items
) {
1396 final List
<GeneratingCompiler
.GenerationItem
> production
= new ArrayList
<GeneratingCompiler
.GenerationItem
>();
1397 final List
<GeneratingCompiler
.GenerationItem
> tests
= new ArrayList
<GeneratingCompiler
.GenerationItem
>();
1398 for (GeneratingCompiler
.GenerationItem item
: items
) {
1399 if (item
.isTestSource()) {
1403 production
.add(item
);
1406 return new GeneratingCompiler
.GenerationItem
[][]{
1407 production
.toArray(new GeneratingCompiler
.GenerationItem
[production
.size()]),
1408 tests
.toArray(new GeneratingCompiler
.GenerationItem
[tests
.size()])
1412 private boolean compileSources(final CompileContextEx context
, final Chunk
<Module
> moduleChunk
, final TranslatingCompiler compiler
, final Collection
<VirtualFile
> srcSnapshot
,
1413 final boolean forceCompile
,
1414 final boolean isRebuild
,
1415 final boolean trackDependencies
,
1416 final boolean onlyCheckStatus
,
1417 TranslatingCompiler
.OutputSink sink
) throws ExitException
{
1419 final Set
<VirtualFile
> toCompile
= new HashSet
<VirtualFile
>();
1420 final List
<Trinity
<File
, String
, Boolean
>> toDelete
= new ArrayList
<Trinity
<File
, String
, Boolean
>>();
1421 context
.getProgressIndicator().pushState();
1423 final boolean[] wereFilesDeleted
= new boolean[]{false};
1425 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1428 TranslatingCompilerFilesMonitor
.getInstance().collectFiles(
1429 context
, compiler
, srcSnapshot
.iterator(), forceCompile
, isRebuild
, toCompile
, toDelete
1431 if (trackDependencies
&& !toCompile
.isEmpty()) { // should add dependent files
1433 final FileTypeManager fileTypeManager
= FileTypeManager
.getInstance();
1434 final PsiManager psiManager
= PsiManager
.getInstance(myProject
);
1435 for (final VirtualFile file
: toCompile
.toArray(new VirtualFile
[toCompile
.size()])) {
1436 if (fileTypeManager
.getFileTypeByFile(file
) == StdFileTypes
.JAVA
) {
1437 final PsiFile psiFile
= psiManager
.findFile(file
);
1438 if (psiFile
!= null) {
1439 addDependentFiles(psiFile
, toCompile
, compiler
, context
);
1447 if (onlyCheckStatus
) {
1448 if (toDelete
.isEmpty() && toCompile
.isEmpty()) {
1451 if (LOG
.isDebugEnabled()) {
1452 if (!toDelete
.isEmpty()) {
1453 LOG
.debug("Found items to delete, compiler " + compiler
.getDescription());
1455 if (!toCompile
.isEmpty()) {
1456 LOG
.debug("Found items to compile, compiler " + compiler
.getDescription());
1459 throw new ExitException(ExitStatus
.CANCELLED
);
1462 if (!toDelete
.isEmpty()) {
1464 wereFilesDeleted
[0] = syncOutputDir(context
, toDelete
);
1466 catch (CacheCorruptedException e
) {
1468 context
.requestRebuildNextTime(e
.getMessage());
1472 if ((wereFilesDeleted
[0] || !toCompile
.isEmpty()) && context
.getMessageCount(CompilerMessageCategory
.ERROR
) == 0) {
1473 compiler
.compile(context
, moduleChunk
, toCompile
.toArray(new VirtualFile
[toCompile
.size()]), sink
);
1477 context
.getProgressIndicator().popState();
1479 return !toCompile
.isEmpty() || wereFilesDeleted
[0];
1482 private static boolean syncOutputDir(final CompileContextEx context
, final Collection
<Trinity
<File
, String
, Boolean
>> toDelete
) throws CacheCorruptedException
{
1483 final int total
= toDelete
.size();
1484 final DependencyCache dependencyCache
= context
.getDependencyCache();
1485 final boolean isTestMode
= ApplicationManager
.getApplication().isUnitTestMode();
1487 final List
<File
> filesToRefresh
= new ArrayList
<File
>();
1488 final boolean[] wereFilesDeleted
= {false};
1489 CompilerUtil
.runInContext(context
, CompilerBundle
.message("progress.synchronizing.output.directory"), new ThrowableRunnable
<CacheCorruptedException
>(){
1490 public void run() throws CacheCorruptedException
{
1491 final long start
= System
.currentTimeMillis();
1494 for (final Trinity
<File
, String
, Boolean
> trinity
: toDelete
) {
1495 final File outputPath
= trinity
.getFirst();
1496 context
.getProgressIndicator().checkCanceled();
1497 context
.getProgressIndicator().setFraction((double)++current
/ total
);
1498 context
.getProgressIndicator().setText2(outputPath
.getPath());
1499 filesToRefresh
.add(outputPath
);
1501 LOG
.assertTrue(outputPath
.exists());
1503 if (!deleteFile(outputPath
)) {
1504 if (isTestMode
&& outputPath
.exists()) {
1505 LOG
.error("Was not able to delete output file: " + outputPath
.getPath());
1509 wereFilesDeleted
[0] = true;
1512 //final String outputDir = myOutputFinder.lookupOutputPath(outputPath);
1513 //if (outputDir != null) {
1515 // context.updateZippedOuput(outputDir, FileUtil.toSystemIndependentName(outputPath.getPath()).substring(outputDir.length() + 1));
1517 // catch (IOException e) {
1522 final String className
= trinity
.getSecond();
1523 if (className
!= null) {
1524 final int id
= dependencyCache
.getSymbolTable().getId(className
);
1525 dependencyCache
.addTraverseRoot(id
);
1526 final boolean sourcePresent
= trinity
.getThird().booleanValue();
1527 if (!sourcePresent
) {
1528 dependencyCache
.markSourceRemoved(id
);
1532 CompilerManagerImpl
.addDeletedPath(outputPath
.getPath());
1537 CompilerUtil
.logDuration("Sync output directory", System
.currentTimeMillis() - start
);
1538 CompilerUtil
.refreshIOFiles(filesToRefresh
);
1542 return wereFilesDeleted
[0];
1545 private void addDependentFiles(final PsiFile psiFile
, Set
<VirtualFile
> toCompile
, TranslatingCompiler compiler
, CompileContext context
) {
1546 final DependenciesBuilder builder
= new ForwardDependenciesBuilder(myProject
, new AnalysisScope(psiFile
));
1548 final Map
<PsiFile
, Set
<PsiFile
>> dependencies
= builder
.getDependencies();
1549 final Set
<PsiFile
> dependentFiles
= dependencies
.get(psiFile
);
1550 if (dependentFiles
!= null && !dependentFiles
.isEmpty()) {
1551 final TranslatingCompilerFilesMonitor monitor
= TranslatingCompilerFilesMonitor
.getInstance();
1552 for (final PsiFile dependentFile
: dependentFiles
) {
1553 if (dependentFile
instanceof PsiCompiledElement
) {
1556 final VirtualFile vFile
= dependentFile
.getVirtualFile();
1557 if (vFile
== null || toCompile
.contains(vFile
)) {
1560 if (!compiler
.isCompilableFile(vFile
, context
)) {
1563 if (!monitor
.isMarkedForCompilation(myProject
, vFile
)) {
1564 continue; // no need to compile since already compiled
1566 toCompile
.add(vFile
);
1567 addDependentFiles(dependentFile
, toCompile
, compiler
, context
);
1572 // [mike] performance optimization - this method is accessed > 15,000 times in Aurora
1573 private String
getModuleOutputPath(final Module module
, boolean inTestSourceContent
) {
1574 final Map
<Module
, String
> map
= inTestSourceContent ? myModuleTestOutputPaths
: myModuleOutputPaths
;
1575 String path
= map
.get(module
);
1577 path
= CompilerPaths
.getModuleOutputPath(module
, inTestSourceContent
);
1578 map
.put(module
, path
);
1584 private boolean processFiles(final FileProcessingCompilerAdapter adapter
,
1585 final boolean forceCompile
,
1586 final boolean checkScope
,
1587 final boolean onlyCheckStatus
, final CacheDeferredUpdater cacheUpdater
) throws ExitException
, IOException
{
1588 final CompileContextEx context
= (CompileContextEx
)adapter
.getCompileContext();
1589 final FileProcessingCompilerStateCache cache
= getFileProcessingCompilerCache(adapter
.getCompiler());
1590 final FileProcessingCompiler
.ProcessingItem
[] items
= adapter
.getProcessingItems();
1591 if (context
.getMessageCount(CompilerMessageCategory
.ERROR
) > 0) {
1594 final CompileScope scope
= context
.getCompileScope();
1595 final List
<FileProcessingCompiler
.ProcessingItem
> toProcess
= new ArrayList
<FileProcessingCompiler
.ProcessingItem
>();
1596 final Set
<String
> allUrls
= new HashSet
<String
>();
1597 final IOException
[] ex
= {null};
1598 DumbService
.getInstance(myProject
).waitForSmartMode();
1599 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1602 for (FileProcessingCompiler
.ProcessingItem item
: items
) {
1603 final VirtualFile file
= item
.getFile();
1605 LOG
.error("FileProcessingCompiler.ProcessingItem.getFile() must not return null: compiler " + adapter
.getCompiler().getDescription());
1608 final String url
= file
.getUrl();
1610 if (!forceCompile
&& cache
.getTimestamp(url
) == file
.getTimeStamp()) {
1611 final ValidityState state
= cache
.getExtState(url
);
1612 final ValidityState itemState
= item
.getValidityState();
1613 if (state
!= null ? state
.equalsTo(itemState
) : itemState
== null) {
1617 if (LOG
.isDebugEnabled()) {
1618 LOG
.debug("Adding item to process: " + url
+ "; saved ts= " + cache
.getTimestamp(url
) + "; VFS ts=" + file
.getTimeStamp());
1620 toProcess
.add(item
);
1623 catch (IOException e
) {
1629 if (ex
[0] != null) {
1633 final Collection
<String
> urls
= cache
.getUrls();
1634 final List
<String
> urlsToRemove
= new ArrayList
<String
>();
1635 if (!urls
.isEmpty()) {
1636 CompilerUtil
.runInContext(context
, CompilerBundle
.message("progress.processing.outdated.files"), new ThrowableRunnable
<IOException
>(){
1637 public void run() throws IOException
{
1638 ApplicationManager
.getApplication().runReadAction(new Runnable() {
1640 for (final String url
: urls
) {
1641 if (!allUrls
.contains(url
)) {
1642 if (!checkScope
|| scope
.belongs(url
)) {
1643 urlsToRemove
.add(url
);
1649 if (!onlyCheckStatus
&& !urlsToRemove
.isEmpty()) {
1650 for (final String url
: urlsToRemove
) {
1651 adapter
.processOutdatedItem(context
, url
, cache
.getExtState(url
));
1659 if (onlyCheckStatus
) {
1660 if (urlsToRemove
.isEmpty() && toProcess
.isEmpty()) {
1663 if (LOG
.isDebugEnabled()) {
1664 if (!urlsToRemove
.isEmpty()) {
1665 LOG
.debug("Found urls to remove, compiler " + adapter
.getCompiler().getDescription());
1666 for (String url
: urlsToRemove
) {
1667 LOG
.debug("\t" + url
);
1670 if (!toProcess
.isEmpty()) {
1671 LOG
.debug("Found items to compile, compiler " + adapter
.getCompiler().getDescription());
1672 for (FileProcessingCompiler
.ProcessingItem item
: toProcess
) {
1673 LOG
.debug("\t" + item
.getFile().getPresentableUrl());
1677 throw new ExitException(ExitStatus
.CANCELLED
);
1680 if (toProcess
.isEmpty()) {
1684 final FileProcessingCompiler
.ProcessingItem
[] processed
=
1685 adapter
.process(toProcess
.toArray(new FileProcessingCompiler
.ProcessingItem
[toProcess
.size()]));
1687 if (processed
.length
== 0) {
1690 CompilerUtil
.runInContext(context
, CompilerBundle
.message("progress.updating.caches"), new ThrowableRunnable
<IOException
>() {
1691 public void run() throws IOException
{
1692 final List
<VirtualFile
> vFiles
= new ArrayList
<VirtualFile
>(processed
.length
);
1693 for (FileProcessingCompiler
.ProcessingItem aProcessed
: processed
) {
1694 final VirtualFile file
= aProcessed
.getFile();
1696 if (LOG
.isDebugEnabled()) {
1697 LOG
.debug("File processed by " + adapter
.getCompiler().getDescription());
1698 LOG
.debug("\tFile processed " + file
.getPresentableUrl() + "; ts=" + file
.getTimeStamp());
1701 //final String path = file.getPath();
1702 //final String outputDir = myOutputFinder.lookupOutputPath(path);
1703 //if (outputDir != null) {
1704 // context.updateZippedOuput(outputDir, path.substring(outputDir.length() + 1));
1707 LocalFileSystem
.getInstance().refreshFiles(vFiles
);
1708 if (LOG
.isDebugEnabled()) {
1709 LOG
.debug("Files after VFS refresh:");
1710 for (VirtualFile file
: vFiles
) {
1711 LOG
.debug("\t" + file
.getPresentableUrl() + "; ts=" + file
.getTimeStamp());
1714 for (FileProcessingCompiler
.ProcessingItem item
: processed
) {
1715 cacheUpdater
.addFileForUpdate(item
, cache
);
1722 private FileProcessingCompilerStateCache
getFileProcessingCompilerCache(FileProcessingCompiler compiler
) throws IOException
{
1723 return CompilerCacheManager
.getInstance(myProject
).getFileProcessingCompilerCache(compiler
);
1726 private StateCache
<ValidityState
> getGeneratingCompilerCache(final GeneratingCompiler compiler
) throws IOException
{
1727 return CompilerCacheManager
.getInstance(myProject
).getGeneratingCompilerCache(compiler
);
1730 public void executeCompileTask(final CompileTask task
, final CompileScope scope
, final String contentName
, final Runnable onTaskFinished
) {
1731 final CompilerTask progressManagerTask
=
1732 new CompilerTask(myProject
, CompilerWorkspaceConfiguration
.getInstance(myProject
).COMPILE_IN_BACKGROUND
, contentName
, false);
1733 final CompileContextImpl compileContext
= new CompileContextImpl(myProject
, progressManagerTask
, scope
, null, false, false);
1735 FileDocumentManager
.getInstance().saveAllDocuments();
1737 progressManagerTask
.start(new Runnable() {
1740 task
.execute(compileContext
);
1742 catch (ProcessCanceledException ex
) {
1746 compileContext
.commitZipFiles();
1747 if (onTaskFinished
!= null) {
1748 onTaskFinished
.run();
1755 private boolean executeCompileTasks(final CompileContext context
, final boolean beforeTasks
) {
1756 final CompilerManager manager
= CompilerManager
.getInstance(myProject
);
1757 final ProgressIndicator progressIndicator
= context
.getProgressIndicator();
1758 progressIndicator
.pushState();
1760 CompileTask
[] tasks
= beforeTasks ? manager
.getBeforeTasks() : manager
.getAfterTasks();
1761 if (tasks
.length
> 0) {
1762 progressIndicator
.setText(beforeTasks
1763 ? CompilerBundle
.message("progress.executing.precompile.tasks")
1764 : CompilerBundle
.message("progress.executing.postcompile.tasks"));
1765 for (CompileTask task
: tasks
) {
1766 if (!task
.execute(context
)) {
1773 progressIndicator
.popState();
1774 WindowManager
.getInstance().getStatusBar(myProject
).setInfo("");
1775 if (progressIndicator
instanceof CompilerTask
) {
1776 ApplicationManager
.getApplication().invokeLater(new Runnable() {
1778 ((CompilerTask
)progressIndicator
).showCompilerContent();
1786 private boolean validateCompilerConfiguration(final CompileScope scope
, boolean checkOutputAndSourceIntersection
) {
1787 final Module
[] scopeModules
= scope
.getAffectedModules()/*ModuleManager.getInstance(myProject).getModules()*/;
1788 final List
<String
> modulesWithoutOutputPathSpecified
= new ArrayList
<String
>();
1789 final List
<String
> modulesWithoutJdkAssigned
= new ArrayList
<String
>();
1790 final Set
<File
> nonExistingOutputPaths
= new HashSet
<File
>();
1792 for (final Module module
: scopeModules
) {
1793 final boolean hasSources
= hasSources(module
, false);
1794 final boolean hasTestSources
= hasSources(module
, true);
1795 if (!hasSources
&& !hasTestSources
) {
1796 // If module contains no sources, shouldn't have to select JDK or output directory (SCR #19333)
1797 // todo still there may be problems with this approach if some generated files are attributed by this module
1800 final Sdk jdk
= ModuleRootManager
.getInstance(module
).getSdk();
1802 modulesWithoutJdkAssigned
.add(module
.getName());
1804 final String outputPath
= getModuleOutputPath(module
, false);
1805 final String testsOutputPath
= getModuleOutputPath(module
, true);
1806 if (outputPath
== null && testsOutputPath
== null) {
1807 modulesWithoutOutputPathSpecified
.add(module
.getName());
1810 if (outputPath
!= null) {
1811 final File file
= new File(outputPath
.replace('/', File
.separatorChar
));
1812 if (!file
.exists()) {
1813 nonExistingOutputPaths
.add(file
);
1818 modulesWithoutOutputPathSpecified
.add(module
.getName());
1821 if (testsOutputPath
!= null) {
1822 final File f
= new File(testsOutputPath
.replace('/', File
.separatorChar
));
1824 nonExistingOutputPaths
.add(f
);
1828 if (hasTestSources
) {
1829 modulesWithoutOutputPathSpecified
.add(module
.getName());
1834 if (!modulesWithoutJdkAssigned
.isEmpty()) {
1835 showNotSpecifiedError("error.jdk.not.specified", modulesWithoutJdkAssigned
, ProjectBundle
.message("modules.classpath.title"));
1839 if (!modulesWithoutOutputPathSpecified
.isEmpty()) {
1840 showNotSpecifiedError("error.output.not.specified", modulesWithoutOutputPathSpecified
, CommonContentEntriesEditor
.NAME
);
1844 if (!nonExistingOutputPaths
.isEmpty()) {
1845 for (File file
: nonExistingOutputPaths
) {
1846 final boolean succeeded
= file
.mkdirs();
1848 if (file
.exists()) {
1849 // for overlapping paths, this one might have been created as an intermediate path on a previous iteration
1852 Messages
.showMessageDialog(myProject
, CompilerBundle
.message("error.failed.to.create.directory", file
.getPath()),
1853 CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
1857 final Boolean refreshSuccess
= ApplicationManager
.getApplication().runWriteAction(new Computable
<Boolean
>() {
1858 public Boolean
compute() {
1859 LocalFileSystem
.getInstance().refreshIoFiles(nonExistingOutputPaths
);
1860 for (File file
: nonExistingOutputPaths
) {
1861 if (LocalFileSystem
.getInstance().findFileByIoFile(file
) == null) {
1862 return Boolean
.FALSE
;
1865 return Boolean
.TRUE
;
1868 if (!refreshSuccess
.booleanValue()) {
1874 if (checkOutputAndSourceIntersection
) {
1875 if (myShouldClearOutputDirectory
) {
1876 if (!validateOutputAndSourcePathsIntersection()) {
1881 final List
<Chunk
<Module
>> chunks
= ModuleCompilerUtil
.getSortedModuleChunks(myProject
, Arrays
.asList(scopeModules
));
1882 final CompilerConfiguration config
= CompilerConfiguration
.getInstance(myProject
);
1883 for (final Chunk
<Module
> chunk
: chunks
) {
1884 final Set
<Module
> chunkModules
= chunk
.getNodes();
1885 if (chunkModules
.size() <= 1) {
1886 continue; // no need to check one-module chunks
1888 if (config
.isAnnotationProcessorsEnabled()) {
1889 final Set
<Module
> excluded
= config
.getExcludedModules();
1890 for (Module chunkModule
: chunkModules
) {
1891 if (!excluded
.contains(chunkModule
)) {
1892 showCyclesNotSupportedForAnnotationProcessors(chunkModules
.toArray(new Module
[chunkModules
.size()]));
1898 LanguageLevel languageLevel
= null;
1899 for (final Module module
: chunkModules
) {
1900 final Sdk moduleJdk
= ModuleRootManager
.getInstance(module
).getSdk();
1905 if (!jdk
.equals(moduleJdk
)) {
1906 showCyclicModulesHaveDifferentJdksError(chunkModules
.toArray(new Module
[chunkModules
.size()]));
1911 LanguageLevel moduleLanguageLevel
= LanguageLevelUtil
.getEffectiveLanguageLevel(module
);
1912 if (languageLevel
== null) {
1913 languageLevel
= moduleLanguageLevel
;
1916 if (!languageLevel
.equals(moduleLanguageLevel
)) {
1917 showCyclicModulesHaveDifferentLanguageLevel(chunkModules
.toArray(new Module
[chunkModules
.size()]));
1923 final Compiler
[] allCompilers
= CompilerManager
.getInstance(myProject
).getCompilers(Compiler
.class);
1924 for (Compiler compiler
: allCompilers
) {
1925 if (!compiler
.validateConfiguration(scope
)) {
1932 private void showCyclicModulesHaveDifferentLanguageLevel(Module
[] modulesInChunk
) {
1933 LOG
.assertTrue(modulesInChunk
.length
> 0);
1934 String moduleNameToSelect
= modulesInChunk
[0].getName();
1935 final String moduleNames
= getModulesString(modulesInChunk
);
1936 Messages
.showMessageDialog(myProject
, CompilerBundle
.message("error.chunk.modules.must.have.same.language.level", moduleNames
),
1937 CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
1938 showConfigurationDialog(moduleNameToSelect
, null);
1941 private void showCyclicModulesHaveDifferentJdksError(Module
[] modulesInChunk
) {
1942 LOG
.assertTrue(modulesInChunk
.length
> 0);
1943 String moduleNameToSelect
= modulesInChunk
[0].getName();
1944 final String moduleNames
= getModulesString(modulesInChunk
);
1945 Messages
.showMessageDialog(myProject
, CompilerBundle
.message("error.chunk.modules.must.have.same.jdk", moduleNames
),
1946 CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
1947 showConfigurationDialog(moduleNameToSelect
, null);
1950 private void showCyclesNotSupportedForAnnotationProcessors(Module
[] modulesInChunk
) {
1951 LOG
.assertTrue(modulesInChunk
.length
> 0);
1952 String moduleNameToSelect
= modulesInChunk
[0].getName();
1953 final String moduleNames
= getModulesString(modulesInChunk
);
1954 Messages
.showMessageDialog(myProject
, CompilerBundle
.message("error.annotation.processing.not.supported.for.module.cycles", moduleNames
),
1955 CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
1956 showConfigurationDialog(moduleNameToSelect
, null);
1959 private static String
getModulesString(Module
[] modulesInChunk
) {
1960 final StringBuilder moduleNames
= StringBuilderSpinAllocator
.alloc();
1962 for (Module module
: modulesInChunk
) {
1963 if (moduleNames
.length() > 0) {
1964 moduleNames
.append("\n");
1966 moduleNames
.append("\"").append(module
.getName()).append("\"");
1968 return moduleNames
.toString();
1971 StringBuilderSpinAllocator
.dispose(moduleNames
);
1975 private static boolean hasSources(Module module
, boolean checkTestSources
) {
1976 final ContentEntry
[] contentEntries
= ModuleRootManager
.getInstance(module
).getContentEntries();
1977 for (final ContentEntry contentEntry
: contentEntries
) {
1978 final SourceFolder
[] sourceFolders
= contentEntry
.getSourceFolders();
1979 for (final SourceFolder sourceFolder
: sourceFolders
) {
1980 if (sourceFolder
.getFile() == null) {
1981 continue; // skip invalid source folders
1983 if (checkTestSources
) {
1984 if (sourceFolder
.isTestSource()) {
1989 if (!sourceFolder
.isTestSource()) {
1998 private void showNotSpecifiedError(@NonNls final String resourceId
, List
<String
> modules
, String tabNameToSelect
) {
1999 String nameToSelect
= null;
2000 final StringBuilder names
= StringBuilderSpinAllocator
.alloc();
2001 final String message
;
2003 final int maxModulesToShow
= 10;
2004 for (String name
: modules
.size() > maxModulesToShow ? modules
.subList(0, maxModulesToShow
) : modules
) {
2005 if (nameToSelect
== null) {
2006 nameToSelect
= name
;
2008 if (names
.length() > 0) {
2009 names
.append(",\n");
2015 if (modules
.size() > maxModulesToShow
) {
2016 names
.append(",\n...");
2018 message
= CompilerBundle
.message(resourceId
, modules
.size(), names
.toString());
2021 StringBuilderSpinAllocator
.dispose(names
);
2024 if (ApplicationManager
.getApplication().isUnitTestMode()) {
2028 Messages
.showMessageDialog(myProject
, message
, CommonBundle
.getErrorTitle(), Messages
.getErrorIcon());
2029 showConfigurationDialog(nameToSelect
, tabNameToSelect
);
2032 private boolean validateOutputAndSourcePathsIntersection() {
2033 final Module
[] allModules
= ModuleManager
.getInstance(myProject
).getModules();
2034 final VirtualFile
[] outputPaths
= CompilerPathsEx
.getOutputDirectories(allModules
);
2035 final Set
<VirtualFile
> affectedOutputPaths
= new HashSet
<VirtualFile
>();
2036 for (Module allModule
: allModules
) {
2037 final ModuleRootManager rootManager
= ModuleRootManager
.getInstance(allModule
);
2038 final VirtualFile
[] sourceRoots
= rootManager
.getSourceRoots();
2039 for (final VirtualFile outputPath
: outputPaths
) {
2040 for (VirtualFile sourceRoot
: sourceRoots
) {
2041 if (VfsUtil
.isAncestor(outputPath
, sourceRoot
, true) || VfsUtil
.isAncestor(sourceRoot
, outputPath
, false)) {
2042 affectedOutputPaths
.add(outputPath
);
2047 if (!affectedOutputPaths
.isEmpty()) {
2048 final StringBuilder paths
= new StringBuilder();
2049 for (final VirtualFile affectedOutputPath
: affectedOutputPaths
) {
2050 if (paths
.length() < 0) {
2053 paths
.append(affectedOutputPath
.getPath().replace('/', File
.separatorChar
));
2055 final int answer
= Messages
.showOkCancelDialog(myProject
,
2056 CompilerBundle
.message("warning.sources.under.output.paths", paths
.toString()),
2057 CommonBundle
.getErrorTitle(), Messages
.getWarningIcon());
2058 if (answer
== 0) { // ok
2059 myShouldClearOutputDirectory
= false;
2069 private void showConfigurationDialog(String moduleNameToSelect
, String tabNameToSelect
) {
2070 ProjectSettingsService
.getInstance(myProject
).showModuleConfigurationDialog(moduleNameToSelect
, tabNameToSelect
, false);
2073 private static VirtualFile
lookupVFile(final IntermediateOutputCompiler compiler
, final Module module
, final boolean forTestSources
) {
2074 final File file
= new File(CompilerPaths
.getGenerationOutputPath(compiler
, module
, forTestSources
));
2075 final LocalFileSystem lfs
= LocalFileSystem
.getInstance();
2077 VirtualFile vFile
= lfs
.findFileByIoFile(file
);
2078 if (vFile
!= null) {
2082 final boolean justCreated
= file
.mkdirs();
2083 vFile
= lfs
.refreshAndFindFileByIoFile(file
);
2085 assert vFile
!= null: "Virtual file not found for " + file
.getPath() + "; mkdirs() exit code is " + justCreated
;
2090 private static class CacheDeferredUpdater
{
2091 private final Map
<VirtualFile
, List
<Pair
<FileProcessingCompilerStateCache
, FileProcessingCompiler
.ProcessingItem
>>> myData
= new java
.util
.HashMap
<VirtualFile
, List
<Pair
<FileProcessingCompilerStateCache
, FileProcessingCompiler
.ProcessingItem
>>>();
2093 public void addFileForUpdate(final FileProcessingCompiler
.ProcessingItem item
, FileProcessingCompilerStateCache cache
) {
2094 final VirtualFile file
= item
.getFile();
2095 List
<Pair
<FileProcessingCompilerStateCache
, FileProcessingCompiler
.ProcessingItem
>> list
= myData
.get(file
);
2097 list
= new ArrayList
<Pair
<FileProcessingCompilerStateCache
, FileProcessingCompiler
.ProcessingItem
>>();
2098 myData
.put(file
, list
);
2100 list
.add(new Pair
<FileProcessingCompilerStateCache
, FileProcessingCompiler
.ProcessingItem
>(cache
, item
));
2103 public void doUpdate() throws IOException
{
2104 final IOException
[] ex
= {null};
2105 ApplicationManager
.getApplication().runReadAction(new Runnable() {
2108 for (Map
.Entry
<VirtualFile
, List
<Pair
<FileProcessingCompilerStateCache
, FileProcessingCompiler
.ProcessingItem
>>> entry
: myData
.entrySet()) {
2109 for (Pair
<FileProcessingCompilerStateCache
, FileProcessingCompiler
.ProcessingItem
> pair
: entry
.getValue()) {
2110 final FileProcessingCompiler
.ProcessingItem item
= pair
.getSecond();
2111 pair
.getFirst().update(entry
.getKey(), item
.getValidityState());
2115 catch (IOException e
) {
2120 if (ex
[0] != null) {
2126 private static class TranslatorsOutputSink
implements TranslatingCompiler
.OutputSink
{
2127 final Map
<String
, Collection
<TranslatingCompiler
.OutputItem
>> myPostponedItems
= new HashMap
<String
, Collection
<TranslatingCompiler
.OutputItem
>>();
2128 private final CompileContextEx myContext
;
2129 private final TranslatingCompiler
[] myCompilers
;
2130 private int myCurrentCompilerIdx
;
2131 //private LinkedBlockingQueue<Future> myFutures = new LinkedBlockingQueue<Future>();
2133 private TranslatorsOutputSink(CompileContextEx context
, TranslatingCompiler
[] compilers
) {
2134 myContext
= context
;
2135 this.myCompilers
= compilers
;
2138 public void setCurrentCompilerIndex(int index
) {
2139 myCurrentCompilerIdx
= index
;
2142 public void add(final String outputRoot
, final Collection
<TranslatingCompiler
.OutputItem
> items
, final VirtualFile
[] filesToRecompile
) {
2143 final TranslatingCompiler compiler
= myCompilers
[myCurrentCompilerIdx
];
2144 if (compiler
instanceof IntermediateOutputCompiler
) {
2145 final LocalFileSystem lfs
= LocalFileSystem
.getInstance();
2146 final List
<VirtualFile
> outputs
= new ArrayList
<VirtualFile
>();
2147 for (TranslatingCompiler
.OutputItem item
: items
) {
2148 final VirtualFile vFile
= lfs
.findFileByPath(item
.getOutputPath());
2149 if (vFile
!= null) {
2153 myContext
.markGenerated(outputs
);
2155 final int nextCompilerIdx
= myCurrentCompilerIdx
+ 1;
2157 if (nextCompilerIdx
< myCompilers
.length
) {
2158 final Map
<String
, Collection
<TranslatingCompiler
.OutputItem
>> updateNow
= new java
.util
.HashMap
<String
, Collection
<TranslatingCompiler
.OutputItem
>>();
2159 // process postponed
2160 for (Map
.Entry
<String
, Collection
<TranslatingCompiler
.OutputItem
>> entry
: myPostponedItems
.entrySet()) {
2161 final String outputDir
= entry
.getKey();
2162 final Collection
<TranslatingCompiler
.OutputItem
> postponed
= entry
.getValue();
2163 for (Iterator
<TranslatingCompiler
.OutputItem
> it
= postponed
.iterator(); it
.hasNext();) {
2164 TranslatingCompiler
.OutputItem item
= it
.next();
2165 boolean shouldPostpone
= false;
2166 for (int idx
= nextCompilerIdx
; idx
< myCompilers
.length
; idx
++) {
2167 if (shouldPostpone
= myCompilers
[idx
].isCompilableFile(item
.getSourceFile(), myContext
)) {
2171 if (!shouldPostpone
) {
2172 // the file is not compilable by the rest of compilers, so it is safe to update it now
2174 addItemToMap(updateNow
, outputDir
, item
);
2178 // process items from current compilation
2179 for (TranslatingCompiler
.OutputItem item
: items
) {
2180 boolean shouldPostpone
= false;
2181 for (int idx
= nextCompilerIdx
; idx
< myCompilers
.length
; idx
++) {
2182 if (shouldPostpone
= myCompilers
[idx
].isCompilableFile(item
.getSourceFile(), myContext
)) {
2186 if (shouldPostpone
) {
2187 // the file is compilable by the next compiler in row, update should be postponed
2188 addItemToMap(myPostponedItems
, outputRoot
, item
);
2191 addItemToMap(updateNow
, outputRoot
, item
);
2195 if (updateNow
.size() == 1) {
2196 final Map
.Entry
<String
, Collection
<TranslatingCompiler
.OutputItem
>> entry
= updateNow
.entrySet().iterator().next();
2197 final String outputDir
= entry
.getKey();
2198 final Collection
<TranslatingCompiler
.OutputItem
> itemsToUpdate
= entry
.getValue();
2199 TranslatingCompilerFilesMonitor
.getInstance().update(myContext
, outputDir
, itemsToUpdate
, filesToRecompile
);
2202 for (Map
.Entry
<String
, Collection
<TranslatingCompiler
.OutputItem
>> entry
: updateNow
.entrySet()) {
2203 final String outputDir
= entry
.getKey();
2204 final Collection
<TranslatingCompiler
.OutputItem
> itemsToUpdate
= entry
.getValue();
2205 TranslatingCompilerFilesMonitor
.getInstance().update(myContext
, outputDir
, itemsToUpdate
, VirtualFile
.EMPTY_ARRAY
);
2207 if (filesToRecompile
.length
> 0) {
2208 TranslatingCompilerFilesMonitor
.getInstance().update(myContext
, null, Collections
.<TranslatingCompiler
.OutputItem
>emptyList(), filesToRecompile
);
2213 TranslatingCompilerFilesMonitor
.getInstance().update(myContext
, outputRoot
, items
, filesToRecompile
);
2216 catch (IOException e
) {
2218 myContext
.requestRebuildNextTime(e
.getMessage());
2222 private void addItemToMap(Map
<String
, Collection
<TranslatingCompiler
.OutputItem
>> map
, String outputDir
, TranslatingCompiler
.OutputItem item
) {
2223 Collection
<TranslatingCompiler
.OutputItem
> collection
= map
.get(outputDir
);
2224 if (collection
== null) {
2225 collection
= new ArrayList
<TranslatingCompiler
.OutputItem
>();
2226 map
.put(outputDir
, collection
);
2228 collection
.add(item
);
2231 public void flushPostponedItems() {
2232 final TranslatingCompilerFilesMonitor filesMonitor
= TranslatingCompilerFilesMonitor
.getInstance();
2234 for (Map
.Entry
<String
, Collection
<TranslatingCompiler
.OutputItem
>> entry
: myPostponedItems
.entrySet()) {
2235 final String outputDir
= entry
.getKey();
2236 final Collection
<TranslatingCompiler
.OutputItem
> items
= entry
.getValue();
2237 filesMonitor
.update(myContext
, outputDir
, items
, VirtualFile
.EMPTY_ARRAY
);
2240 catch (IOException e
) {
2242 myContext
.requestRebuildNextTime(e
.getMessage());