get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / incremental / BuildOperations.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.jps.incremental;
17
18 import com.intellij.openapi.util.io.FileUtil;
19 import gnu.trove.THashSet;
20 import gnu.trove.TObjectIntHashMap;
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.annotations.Nullable;
23 import org.jetbrains.jps.builders.*;
24 import org.jetbrains.jps.builders.impl.BuildTargetChunk;
25 import org.jetbrains.jps.builders.java.JavaBuilderUtil;
26 import org.jetbrains.jps.builders.logging.ProjectBuilderLogger;
27 import org.jetbrains.jps.builders.storage.SourceToOutputMapping;
28 import org.jetbrains.jps.cmdline.ProjectDescriptor;
29 import org.jetbrains.jps.incremental.fs.BuildFSState;
30 import org.jetbrains.jps.incremental.fs.CompilationRound;
31 import org.jetbrains.jps.incremental.messages.DoneSomethingNotification;
32 import org.jetbrains.jps.incremental.messages.FileDeletedEvent;
33 import org.jetbrains.jps.incremental.storage.BuildDataManager;
34 import org.jetbrains.jps.incremental.storage.BuildTargetConfiguration;
35 import org.jetbrains.jps.incremental.storage.Timestamps;
36
37 import java.io.File;
38 import java.io.IOException;
39 import java.nio.file.*;
40 import java.nio.file.attribute.BasicFileAttributes;
41 import java.util.*;
42
43 /**
44  * @author Eugene Zhuravlev
45  */
46 public class BuildOperations {
47   private BuildOperations() { }
48
49   public static void ensureFSStateInitialized(CompileContext context, BuildTarget<?> target, boolean readOnly) throws IOException {
50     final ProjectDescriptor pd = context.getProjectDescriptor();
51     final Timestamps timestamps = pd.timestamps.getStorage();
52     final BuildTargetConfiguration configuration = pd.getTargetsState().getTargetConfiguration(target);
53     if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) {
54       FSOperations.markDirtyFiles(context, target, CompilationRound.CURRENT, timestamps, true, null, null);
55       pd.fsState.markInitialScanPerformed(target);
56       if (!readOnly) {
57         configuration.save(context);
58       }
59     }
60     else if (context.getScope().isBuildForced(target) || configuration.isTargetDirty(context) || configuration.outputRootWasDeleted(context)) {
61       initTargetFSState(context, target, true);
62       if (!readOnly) {
63         if (!context.getScope().isBuildForced(target)) {
64           // case when target build is forced, is handled separately
65           IncProjectBuilder.clearOutputFiles(context, target);
66         }
67         pd.dataManager.cleanTargetStorages(target);
68         configuration.save(context);
69       }
70     }
71     else if (!pd.fsState.isInitialScanPerformed(target)) {
72       initTargetFSState(context, target, false);
73     }
74   }
75
76   private static void initTargetFSState(CompileContext context, BuildTarget<?> target, final boolean forceMarkDirty) throws IOException {
77     final ProjectDescriptor pd = context.getProjectDescriptor();
78     final Timestamps timestamps = pd.timestamps.getStorage();
79     final THashSet<File> currentFiles = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
80     FSOperations.markDirtyFiles(context, target, CompilationRound.CURRENT, timestamps, forceMarkDirty, currentFiles, null);
81
82     // handle deleted paths
83     final BuildFSState fsState = pd.fsState;
84     final SourceToOutputMapping sourceToOutputMap = pd.dataManager.getSourceToOutputMap(target);
85     for (final Iterator<String> it = sourceToOutputMap.getSourcesIterator(); it.hasNext(); ) {
86       final String path = it.next();
87       // can check if the file exists
88       final File file = new File(path);
89       if (!currentFiles.contains(file)) {
90         fsState.registerDeleted(context, target, file, timestamps);
91       }
92     }
93     pd.fsState.markInitialScanPerformed(target);
94   }
95
96   public static void markTargetsUpToDate(CompileContext context, BuildTargetChunk chunk) throws IOException {
97     final ProjectDescriptor pd = context.getProjectDescriptor();
98     final BuildFSState fsState = pd.fsState;
99     for (BuildTarget<?> target : chunk.getTargets()) {
100       pd.getTargetsState().getTargetConfiguration(target).storeNonexistentOutputRoots(context);
101     }
102     if (!Utils.errorsDetected(context) && !context.getCancelStatus().isCanceled()) {
103       boolean marked = dropRemovedPaths(context, chunk);
104       for (BuildTarget<?> target : chunk.getTargets()) {
105         if (target instanceof ModuleBuildTarget) {
106           context.clearNonIncrementalMark((ModuleBuildTarget)target);
107         }
108         final Timestamps timestamps = pd.timestamps.getStorage();
109         for (BuildRootDescriptor rd : pd.getBuildRootIndex().getTargetRoots(target, context)) {
110           marked |= fsState.markAllUpToDate(context, rd, timestamps);
111         }
112       }
113
114       if (marked) {
115         context.processMessage(DoneSomethingNotification.INSTANCE);
116       }
117     }
118   }
119
120   private static boolean dropRemovedPaths(CompileContext context, BuildTargetChunk chunk) throws IOException {
121     final Map<BuildTarget<?>, Collection<String>> map = Utils.REMOVED_SOURCES_KEY.get(context);
122     boolean dropped = false;
123     if (map != null) {
124       for (BuildTarget<?> target : chunk.getTargets()) {
125         final Collection<String> paths = map.remove(target);
126         if (paths != null) {
127           final SourceToOutputMapping storage = context.getProjectDescriptor().dataManager.getSourceToOutputMap(target);
128           for (String path : paths) {
129             storage.remove(path);
130             dropped = true;
131           }
132         }
133       }
134     }
135     return dropped;
136   }
137
138   public static <R extends BuildRootDescriptor, T extends BuildTarget<R>>
139   Map<T, Set<File>> cleanOutputsCorrespondingToChangedFiles(final CompileContext context, DirtyFilesHolder<R, T> dirtyFilesHolder) throws ProjectBuildException {
140     final BuildDataManager dataManager = context.getProjectDescriptor().dataManager;
141     try {
142       final Map<T, Set<File>> cleanedSources = new HashMap<>();
143
144       final THashSet<File> dirsToDelete = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
145       final Collection<String> deletedPaths = new ArrayList<>();
146
147       dirtyFilesHolder.processDirtyFiles(new FileProcessor<R, T>() {
148         private final Map<T, SourceToOutputMapping> mappingsCache = new HashMap<>(); // cache the mapping locally
149         private final TObjectIntHashMap<T> idsCache = new TObjectIntHashMap<>();
150
151         @Override
152         public boolean apply(T target, File file, R sourceRoot) throws IOException {
153           SourceToOutputMapping srcToOut = mappingsCache.get(target);
154           if (srcToOut == null) {
155             srcToOut = dataManager.getSourceToOutputMap(target);
156             mappingsCache.put(target, srcToOut);
157           }
158           final int targetId;
159           if (!idsCache.containsKey(target)) {
160             targetId = dataManager.getTargetsState().getBuildTargetId(target);
161             idsCache.put(target, targetId);
162           }
163           else {
164             targetId = idsCache.get(target);
165           }
166           final String srcPath = file.getPath();
167           final Collection<String> outputs = srcToOut.getOutputs(srcPath);
168           if (outputs != null) {
169             final boolean shouldPruneOutputDirs = target instanceof ModuleBasedTarget;
170             final List<String> deletedForThisSource = new ArrayList<>(outputs.size());
171             for (String output : outputs) {
172               deleteRecursively(output, deletedForThisSource, shouldPruneOutputDirs ? dirsToDelete : null);
173             }
174             deletedPaths.addAll(deletedForThisSource);
175             dataManager.getOutputToTargetRegistry().removeMapping(deletedForThisSource, targetId);
176             Set<File> cleaned = cleanedSources.get(target);
177             if (cleaned == null) {
178               cleaned = new THashSet<>(FileUtil.FILE_HASHING_STRATEGY);
179               cleanedSources.put(target, cleaned);
180             }
181             cleaned.add(file);
182           }
183           return true;
184         }
185
186       });
187
188       if (JavaBuilderUtil.isCompileJavaIncrementally(context)) {
189         final ProjectBuilderLogger logger = context.getLoggingManager().getProjectBuilderLogger();
190         if (logger.isEnabled()) {
191           logger.logDeletedFiles(deletedPaths);
192         }
193       }
194
195       if (!deletedPaths.isEmpty()) {
196         context.processMessage(new FileDeletedEvent(deletedPaths));
197       }
198       // attempting to delete potentially empty directories
199       FSOperations.pruneEmptyDirs(context, dirsToDelete);
200
201       return cleanedSources;
202     }
203     catch (Exception e) {
204       throw new ProjectBuildException(e);
205     }
206   }
207
208   public static boolean deleteRecursively(@NotNull String path, @NotNull Collection<? super String> deletedPaths, @Nullable Set<? super File> parentDirs) {
209     File file = new File(path);
210     boolean deleted = deleteRecursively(file, deletedPaths);
211     if (deleted && parentDirs != null) {
212       File parent = file.getParentFile();
213       if (parent != null) {
214         parentDirs.add(parent);
215       }
216     }
217     return deleted;
218   }
219
220   private static boolean deleteRecursively(final File file, final Collection<? super String> deletedPaths) {
221     try {
222       Files.walkFileTree(file.toPath(), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
223         @Override
224         public FileVisitResult visitFile(Path f, BasicFileAttributes attrs) throws IOException {
225           try {
226             Files.delete(f);
227           }
228           catch (AccessDeniedException e) {
229             if (!f.toFile().delete()) { // fallback
230               throw e;
231             }
232           }
233           deletedPaths.add(FileUtil.toSystemIndependentName(f.toString()));
234           return FileVisitResult.CONTINUE;
235         }
236
237         @Override
238         public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
239           try {
240             Files.delete(dir);
241           }
242           catch (AccessDeniedException e) {
243             if (!dir.toFile().delete()) { // fallback
244               throw e;
245             }
246           }
247           return FileVisitResult.CONTINUE;
248         }
249
250       });
251       return true;
252     }
253     catch (IOException e) {
254       return false;
255     }
256   }
257 }