1 package org.jetbrains.jps.incremental;
3 import com.intellij.openapi.util.Pair;
4 import com.intellij.openapi.util.UserDataHolderBase;
5 import org.jetbrains.annotations.NotNull;
6 import org.jetbrains.annotations.Nullable;
7 import org.jetbrains.ether.dependencyView.Mappings;
8 import org.jetbrains.jps.*;
9 import org.jetbrains.jps.api.CanceledStatus;
10 import org.jetbrains.jps.incremental.messages.BuildMessage;
11 import org.jetbrains.jps.incremental.messages.ProgressMessage;
12 import org.jetbrains.jps.incremental.messages.UptoDateFilesSavedEvent;
13 import org.jetbrains.jps.incremental.storage.BuildDataManager;
14 import org.jetbrains.jps.incremental.storage.SourceToOutputMapping;
15 import org.jetbrains.jps.incremental.storage.TimestampStorage;
21 * @author Eugene Zhuravlev
24 public class CompileContext extends UserDataHolderBase implements MessageHandler{
25 private final CompileScope myScope;
26 private final boolean myIsMake;
27 private final boolean myIsProjectRebuild;
28 private final ProjectChunks myProductionChunks;
29 private final ProjectChunks myTestChunks;
30 private final FSState myFsState;
31 private final MessageHandler myDelegateMessageHandler;
32 private volatile boolean myCompilingTests = false;
33 private final BuildDataManager myDataManager;
34 private final ModuleRootsIndex myRootsIndex;
36 private final Set<Pair<Module, DirtyMarkScope>> myNonIncrementalModules = new HashSet<Pair<Module, DirtyMarkScope>>();
38 private final ProjectPaths myProjectPaths;
39 private volatile boolean myErrorsFound = false;
40 private final long myCompilationStartStamp;
41 private final TimestampStorage myTsStorage;
42 private final CanceledStatus myCancelStatus;
43 private float myDone = -1.0f;
45 public CompileContext(CompileScope scope,
47 boolean isProjectRebuild,
48 ProjectChunks productionChunks,
49 ProjectChunks testChunks,
50 FSState fsState, final BuildDataManager dataManager, TimestampStorage tsStorage, MessageHandler delegateMessageHandler, final ModuleRootsIndex rootsIndex, CanceledStatus cancelStatus) throws ProjectBuildException {
51 myTsStorage = tsStorage;
52 myCancelStatus = cancelStatus;
53 myCompilationStartStamp = System.currentTimeMillis();
55 myIsProjectRebuild = isProjectRebuild;
56 myIsMake = isProjectRebuild? false : isMake;
57 myProductionChunks = productionChunks;
58 myTestChunks = testChunks;
60 myDelegateMessageHandler = delegateMessageHandler;
61 myDataManager = dataManager;
62 final Project project = scope.getProject();
63 myProjectPaths = new ProjectPaths(project);
64 myRootsIndex = rootsIndex;
67 public Project getProject() {
68 return myScope.getProject();
71 public ProjectPaths getProjectPaths() {
72 return myProjectPaths;
75 public boolean isMake() {
79 public boolean isProjectRebuild() {
80 return myIsProjectRebuild;
83 public void markDirty(final File file) throws Exception {
84 final RootDescriptor descriptor = getModuleAndRoot(file);
85 if (descriptor != null) {
86 myFsState.markDirty(file, descriptor, myTsStorage);
90 public void markDirty(final ModuleChunk chunk) throws Exception {
91 final Set<Module> modules = chunk.getModules();
92 for (Module module : modules) {
93 markDirtyFiles(module, myTsStorage, true, isCompilingTests()? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, null);
97 public void markDirtyRecursively(ModuleChunk chunk) throws Exception {
98 final Set<Module> modules = chunk.getModules();
99 final Set<Module> dirtyModules = new HashSet<Module>(modules);
101 // now mark all modules that depend on dirty modules
102 final ClasspathKind classpathKind = ClasspathKind.compile(isCompilingTests());
103 final ProjectChunks chunks = isCompilingTests()? myTestChunks : myProductionChunks;
104 boolean found = false;
105 for (ModuleChunk moduleChunk : chunks.getChunkList()) {
107 if (moduleChunk.equals(chunk)) {
112 MODULES_LOOP: for (final Module module : moduleChunk.getModules()) {
113 for (ClasspathItem dependency : module.getClasspath(classpathKind)) {
114 if (dependency instanceof Module && dirtyModules.contains((Module)dependency)) {
115 dirtyModules.addAll(moduleChunk.getModules());
123 for (Module module : dirtyModules) {
124 markDirtyFiles(module, myTsStorage, true, isCompilingTests()? DirtyMarkScope.TESTS : DirtyMarkScope.BOTH, null);
126 if (!isCompilingTests()) {
127 myNonIncrementalModules.add(new Pair<Module, DirtyMarkScope>(module, DirtyMarkScope.PRODUCTION));
129 myNonIncrementalModules.add(new Pair<Module, DirtyMarkScope>(module, DirtyMarkScope.TESTS));
134 boolean shouldDifferentiate(ModuleChunk chunk, boolean forTests) {
136 // the check makes sense only in make mode
139 final DirtyMarkScope dirtyScope = forTests ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION;
140 for (Module module : chunk.getModules()) {
141 if (myNonIncrementalModules.contains(new Pair<Module, DirtyMarkScope>(module, dirtyScope))) {
148 public Mappings createDelta() {
149 return myDataManager.getMappings().createDelta();
152 public boolean isCompilingTests() {
153 return myCompilingTests;
156 public CanceledStatus getCancelStatus() {
157 return myCancelStatus;
160 void setCompilingTests(boolean compilingTests) {
161 myCompilingTests = compilingTests;
164 public void onChunkBuildStart(ModuleChunk chunk) {
165 myFsState.setContextChunk(chunk);
168 void beforeNextCompileRound(@NotNull ModuleChunk chunk) {
169 myFsState.beforeNextRoundStart();
172 void onChunkBuildComplete(@NotNull ModuleChunk chunk) throws Exception {
173 myDataManager.flush(true);
176 if (!myErrorsFound && !myCancelStatus.isCanceled()) {
177 final boolean compilingTests = isCompilingTests();
178 final DirtyMarkScope dirtyScope = compilingTests ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION;
179 boolean marked = false;
180 for (Module module : chunk.getModules()) {
182 // ensure non-incremental flag cleared
183 myNonIncrementalModules.remove(new Pair<Module, DirtyMarkScope>(module, dirtyScope));
185 if (isProjectRebuild()) {
186 myFsState.markInitialScanPerformed(module, compilingTests);
188 final List<RootDescriptor> roots = myRootsIndex.getModuleRoots(module);
189 for (RootDescriptor descriptor : roots) {
190 if (compilingTests? descriptor.isTestRoot : !descriptor.isTestRoot) {
191 marked |= myFsState.markAllUpToDate(getScope(), descriptor, myTsStorage, myCompilationStartStamp);
196 processMessage(UptoDateFilesSavedEvent.INSTANCE);
201 myFsState.clearContextRoundData();
205 public CompileScope getScope() {
209 public BuildDataManager getDataManager() {
210 return myDataManager;
213 public TimestampStorage getTimestampStorage() {
217 public void processMessage(BuildMessage msg) {
218 if (msg.getKind() == BuildMessage.Kind.ERROR) {
219 myErrorsFound = true;
221 if (msg instanceof ProgressMessage) {
222 ((ProgressMessage)msg).setDone(myDone);
224 myDelegateMessageHandler.processMessage(msg);
227 public void processFilesToRecompile(ModuleChunk chunk, FileProcessor processor) throws Exception {
228 for (Module module : chunk.getModules()) {
229 myFsState.processFilesToRecompile(this, module, processor);
233 final void ensureFSStateInitialized(ModuleChunk chunk) throws Exception {
234 for (Module module : chunk.getModules()) {
235 if (isProjectRebuild()) {
236 markDirtyFiles(module, myTsStorage, true, isCompilingTests() ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, null);
240 if (myFsState.markInitialScanPerformed(module, isCompilingTests())) {
241 initModuleFSState(module);
245 // forced compilation mode
246 if (getScope().isRecompilationForced(module)) {
247 markDirtyFiles(module, myTsStorage, true, isCompilingTests() ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, null);
254 private void initModuleFSState(Module module) throws Exception {
255 final HashSet<File> currentFiles = new HashSet<File>();
256 markDirtyFiles(module, myTsStorage, false, isCompilingTests() ? DirtyMarkScope.TESTS : DirtyMarkScope.PRODUCTION, currentFiles);
258 final String moduleName = module.getName().toLowerCase(Locale.US);
259 final SourceToOutputMapping sourceToOutputMap = getDataManager().getSourceToOutputMap(moduleName, isCompilingTests());
260 for (final Iterator<String> it = sourceToOutputMap.getKeysIterator(); it.hasNext();) {
261 final String path = it.next();
262 // can check if the file exists
263 final File file = new File(path);
264 if (!currentFiles.contains(file)) {
265 myFsState.registerDeleted(module, file, isCompilingTests(), myTsStorage);
270 public boolean hasRemovedSources() {
271 final Set<String> removed = Paths.CHUNK_REMOVED_SOURCES_KEY.get(this);
272 return removed != null && !removed.isEmpty();
276 public RootDescriptor getModuleAndRoot(File file) {
277 return myRootsIndex.getModuleAndRoot(file);
281 public List<RootDescriptor> getModuleRoots(Module module) {
282 return myRootsIndex.getModuleRoots(module);
285 public void setDone(float done) {
287 //processMessage(new ProgressMessage("", done));
290 public static enum DirtyMarkScope{
291 PRODUCTION, TESTS, BOTH
294 private void markDirtyFiles(Module module, final TimestampStorage tsStorage, final boolean forceMarkDirty, @NotNull final DirtyMarkScope scope, @Nullable final Set<File> currentFiles) throws Exception {
295 final Set<File> excludes = new HashSet<File>();
296 for (String excludePath : module.getExcludes()) {
297 excludes.add(new File(excludePath));
299 for (RootDescriptor rd : getModuleRoots(module)) {
300 if (scope == DirtyMarkScope.TESTS) {
301 if (!rd.isTestRoot) {
305 else if (scope == DirtyMarkScope.PRODUCTION) {
310 if (!rd.root.exists()) {
313 if (forceMarkDirty) {
314 myFsState.clearRecompile(rd);
315 myFsState.clearDeletedPaths(module, isCompilingTests());
317 traverseRecursively(rd, rd.root, excludes, tsStorage, forceMarkDirty, currentFiles);
321 private void traverseRecursively(final RootDescriptor rd, final File file, Set<File> excludes, @NotNull final TimestampStorage tsStorage, final boolean forceDirty, @Nullable Set<File> currentFiles) throws Exception {
322 if (file.isDirectory()) {
323 if (!PathUtil.isUnder(excludes, file)) {
324 final File[] children = file.listFiles();
325 if (children != null) {
326 for (File child : children) {
327 traverseRecursively(rd, child, excludes, tsStorage, forceDirty, currentFiles);
333 boolean markDirty = forceDirty;
335 markDirty = tsStorage.getStamp(file) != file.lastModified();
338 // if it is full project rebuild, all storages are already completely cleared;
339 // so passing null because there is no need to access the storage to clear non-existing data
340 final TimestampStorage _tsStorage = isProjectRebuild() ? null : tsStorage;
341 myFsState.markDirty(file, rd, _tsStorage);
343 if (currentFiles != null) {
344 currentFiles.add(file);