a5dc5e2ffc24886d1460f93e43e9232697e52ed6
[idea/community.git] / java / compiler / impl / src / com / intellij / compiler / impl / NewCompilerRunner.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.compiler.impl;
17
18 import com.intellij.compiler.impl.newApi.*;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.compiler.*;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.progress.ProcessCanceledException;
23 import com.intellij.openapi.project.DumbService;
24 import com.intellij.openapi.project.Project;
25 import com.intellij.openapi.util.Pair;
26 import com.intellij.openapi.util.Ref;
27 import com.intellij.util.CommonProcessors;
28 import com.intellij.util.Processor;
29 import com.intellij.util.io.KeyDescriptor;
30 import gnu.trove.THashSet;
31 import gnu.trove.TObjectHashingStrategy;
32 import org.jetbrains.annotations.NotNull;
33
34 import java.io.File;
35 import java.io.IOException;
36 import java.util.*;
37
38 /**
39  * @author nik
40  */
41 public class NewCompilerRunner {
42   private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.impl.NewCompilerRunner");
43   private CompileContext myContext;
44   private final boolean myForceCompile;
45   private final boolean myOnlyCheckStatus;
46   private final NewCompiler<?,?>[] myCompilers;
47   private final Project myProject;
48
49   public NewCompilerRunner(CompileContext context, CompilerManager compilerManager, boolean forceCompile, boolean onlyCheckStatus) {
50     myContext = context;
51     myForceCompile = forceCompile;
52     myOnlyCheckStatus = onlyCheckStatus;
53     myCompilers = compilerManager.getCompilers(NewCompiler.class);
54     myProject = myContext.getProject();
55   }
56
57   public boolean invokeCompilers(NewCompiler.CompileOrderPlace place) throws CompileDriver.ExitException {
58     boolean didSomething = false;
59     try {
60       for (NewCompiler<?, ?> compiler : myCompilers) {
61         if (compiler.getOrderPlace().equals(place)) {
62           didSomething = invokeCompiler(compiler);
63         }
64       }
65     }
66     catch (IOException e) {
67       LOG.info(e);
68       myContext.requestRebuildNextTime(e.getMessage());
69       throw new CompileDriver.ExitException(CompileDriver.ExitStatus.ERRORS);
70     }
71     catch (CompileDriver.ExitException e) {
72       throw e;
73     }
74     catch (ProcessCanceledException e) {
75       throw e;
76     }
77     catch (Exception e) {
78       LOG.info(e);
79       myContext.addMessage(CompilerMessageCategory.ERROR, CompilerBundle.message("compiler.error.exception", e.getMessage()), null, -1, -1);
80     }
81     return didSomething;
82   }
83
84   private <T extends BuildTarget, Key, State> boolean invokeCompiler(NewCompiler<Key, State> compiler) throws IOException, CompileDriver.ExitException {
85     return invokeCompiler(compiler, compiler.createInstance(myContext));
86   }
87
88   private <T extends BuildTarget, Item extends CompileItem<Key, State>, Key, State>
89   boolean invokeCompiler(NewCompiler<Key, State> compiler, CompilerInstance<T, Item, Key, State> instance) throws IOException, CompileDriver.ExitException {
90     NewCompilerCache<Key, State> cache = CompilerCacheManager.getInstance(myProject).getNewCompilerCache(compiler);
91     NewCompilerPersistentData data = new NewCompilerPersistentData(getNewCompilerCacheDir(myProject, compiler), compiler.getVersion());
92     if (data.isVersionChanged()) {
93       LOG.info("Clearing cache for " + compiler.getDescription());
94       cache.wipe();
95     }
96
97     Set<String> targetsToRemove = new HashSet<String>(data.getAllTargets());
98     for (T target : instance.getAllTargets()) {
99       targetsToRemove.remove(target.getId());
100     }
101     if (!myOnlyCheckStatus) {
102       for (String target : targetsToRemove) {
103         int id = data.removeId(target);
104         if (LOG.isDebugEnabled()) {
105           LOG.debug("Removing obsolete target '" + target + "' (id=" + id + ")");
106         }
107         List<Key> keys = new ArrayList<Key>();
108         cache.processSources(id, new CommonProcessors.CollectProcessor<Key>(keys));
109         List<Pair<Key, State>> obsoleteSources = new ArrayList<Pair<Key, State>>();
110         for (Key key : keys) {
111           final State state = cache.getState(id, key);
112           obsoleteSources.add(Pair.create(key, state));
113         }
114         instance.processObsoleteTarget(target, obsoleteSources);
115         if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
116           return true;
117         }
118         for (Key key : keys) {
119           cache.remove(id, key);
120         }
121       }
122     }
123
124     boolean didSomething = false;
125     for (T target : instance.getSelectedTargets()) {
126       int id = data.getId(target.getId());
127       didSomething |= processTarget(target, id, compiler, instance, cache);
128     }
129
130     data.save();
131     return didSomething;
132   }
133
134   public static File getNewCompilerCacheDir(Project project, NewCompiler<?, ?> compiler) {
135     return new File(CompilerPaths.getCacheStoreDirectory(project), compiler.getId());
136   }
137
138   private <T extends BuildTarget, Item extends CompileItem<Key, State>, Key, State>
139   boolean processTarget(T target, final int targetId, final NewCompiler<Key, State> compiler, final CompilerInstance<T, Item, Key, State> instance,
140                         final NewCompilerCache<Key, State> cache) throws IOException, CompileDriver.ExitException {
141     if (LOG.isDebugEnabled()) {
142       LOG.debug("Processing target '" + target + "' (id=" + targetId + ")");
143     }
144     final List<Item> items = instance.getItems(target);
145     if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) return true;
146
147     final List<Pair<Item, State>> toProcess = new ArrayList<Pair<Item, State>>();
148     final THashSet<Key> keySet = new THashSet<Key>(new SourceItemHashingStrategy<Key>(compiler));
149     final Ref<IOException> exception = Ref.create(null);
150     DumbService.getInstance(myProject).waitForSmartMode();
151     ApplicationManager.getApplication().runReadAction(new Runnable() {
152       @Override
153       public void run() {
154         try {
155           for (Item item : items) {
156             final Key key = item.getKey();
157             keySet.add(key);
158             State output = cache.getState(targetId, key);
159             if (myForceCompile || output == null || !item.isUpToDate(output)) {
160               toProcess.add(Pair.create(item, output));
161             }
162           }
163         }
164         catch (IOException e) {
165           exception.set(e);
166         }
167       }
168     });
169     if (!exception.isNull()) {
170       throw exception.get();
171     }
172
173     final List<Key> toRemove = new ArrayList<Key>();
174     cache.processSources(targetId, new Processor<Key>() {
175       @Override
176       public boolean process(Key key) {
177         if (!keySet.contains(key)) {
178           toRemove.add(key);
179         }
180         return true;
181       }
182     });
183
184     if (LOG.isDebugEnabled()) {
185       LOG.debug(toProcess.size() + " items will be processed, " + toRemove.size() + " items will be removed");
186     }
187
188     if (toProcess.isEmpty() && toRemove.isEmpty()) {
189       return false;
190     }
191
192     if (myOnlyCheckStatus) {
193       throw new CompileDriver.ExitException(CompileDriver.ExitStatus.CANCELLED);
194     }
195
196     List<Pair<Key, State>> obsoleteItems = new ArrayList<Pair<Key, State>>();
197     for (Key key : toRemove) {
198       obsoleteItems.add(Pair.create(key, cache.getState(targetId, key)));
199     }
200
201     final List<Item> processedItems = new ArrayList<Item>();
202     final List<File> toRefresh = new ArrayList<File>();
203     instance.processItems(target, toProcess, obsoleteItems, new CompilerInstance.OutputConsumer<Item>() {
204       @Override
205       public void addFileToRefresh(@NotNull File file) {
206         toRefresh.add(file);
207       }
208       @Override
209       public void addProcessedItem(@NotNull Item sourceItem) {
210         processedItems.add(sourceItem);
211       }
212     });
213     if (myContext.getMessageCount(CompilerMessageCategory.ERROR) > 0) {
214       return true;
215     }
216
217     for (Key key : toRemove) {
218       cache.remove(targetId, key);
219     }
220     CompilerUtil.refreshIOFiles(toRefresh);
221     for (Item item : processedItems) {
222       cache.putOutput(targetId, item.getKey(), item.computeState());
223     }
224
225     return true;
226
227   }
228
229   private class SourceItemHashingStrategy<S> implements TObjectHashingStrategy<S> {
230     private KeyDescriptor<S> myKeyDescriptor;
231
232     public SourceItemHashingStrategy(NewCompiler<S, ?> compiler) {
233       myKeyDescriptor = compiler.getItemKeyDescriptor();
234     }
235
236     @Override
237     public int computeHashCode(S object) {
238       return myKeyDescriptor.getHashCode(object);
239     }
240
241     @Override
242     public boolean equals(S o1, S o2) {
243       return myKeyDescriptor.isEqual(o1, o2);
244     }
245   }
246 }