13658dcea859c324e07b4b44dc8a3e8eb52e0b0d
[idea/community.git] / jps / jps-builders / src / org / jetbrains / jps / ProjectPaths.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;
17
18 import com.intellij.openapi.util.Condition;
19 import com.intellij.openapi.util.text.StringUtil;
20 import com.intellij.util.Consumer;
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.annotations.Nullable;
23 import org.jetbrains.jps.model.JpsDummyElement;
24 import org.jetbrains.jps.model.java.*;
25 import org.jetbrains.jps.model.java.compiler.ProcessorConfigProfile;
26 import org.jetbrains.jps.model.library.JpsOrderRootType;
27 import org.jetbrains.jps.model.library.sdk.JpsSdk;
28 import org.jetbrains.jps.model.module.JpsDependencyElement;
29 import org.jetbrains.jps.model.module.JpsModule;
30 import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
31 import org.jetbrains.jps.model.module.JpsSdkDependency;
32 import org.jetbrains.jps.util.JpsPathUtil;
33
34 import java.io.File;
35 import java.util.*;
36
37 /**
38  * @author Eugene Zhuravlev
39  */
40 public class ProjectPaths {
41   private ProjectPaths() { }
42
43   @NotNull
44   public static Collection<File> getCompilationClasspathFiles(ModuleChunk chunk,
45                                                               boolean includeTests,
46                                                               boolean excludeMainModuleOutput,
47                                                               boolean exportedOnly) {
48     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(includeTests), excludeMainModuleOutput, ClasspathPart.WHOLE, exportedOnly);
49   }
50
51   @NotNull
52   public static Collection<File> getPlatformCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
53     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.BEFORE_PLUS_JDK, true);
54   }
55
56   @NotNull
57   public static Collection<File> getCompilationClasspath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
58     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK, true);
59   }
60
61   @NotNull
62   public static Collection<File> getCompilationModulePath(ModuleChunk chunk, boolean excludeMainModuleOutput) {
63     return getClasspathFiles(chunk, JpsJavaClasspathKind.compile(chunk.containsTests()), excludeMainModuleOutput, ClasspathPart.AFTER_JDK, false);
64   }
65
66   @NotNull
67   private static Collection<File> getClasspathFiles(ModuleChunk chunk,
68                                                     JpsJavaClasspathKind kind,
69                                                     boolean excludeMainModuleOutput,
70                                                     ClasspathPart classpathPart,
71                                                     boolean exportedOnly) {
72     final Set<File> files = new LinkedHashSet<>();
73     for (JpsModule module : chunk.getModules()) {
74       JpsJavaDependenciesEnumerator enumerator = JpsJavaExtensionService.dependencies(module).includedIn(kind).recursively();
75       if (exportedOnly) {
76         enumerator = enumerator.exportedOnly();
77       }
78       if (classpathPart == ClasspathPart.BEFORE_JDK || classpathPart == ClasspathPart.BEFORE_PLUS_JDK) {
79         enumerator = enumerator.satisfying(new BeforeJavaSdkItemFilter(module));
80       }
81       else if (classpathPart == ClasspathPart.AFTER_JDK) {
82         enumerator = enumerator.satisfying(new AfterJavaSdkItemFilter(module));
83       }
84       JpsJavaDependenciesRootsEnumerator rootsEnumerator = enumerator.classes();
85       if (excludeMainModuleOutput) {
86         rootsEnumerator = rootsEnumerator.withoutSelfModuleOutput();
87       }
88       files.addAll(rootsEnumerator.getRoots());
89     }
90
91     if (classpathPart == ClasspathPart.BEFORE_PLUS_JDK) {
92       for (JpsModule module : chunk.getModules()) {
93         JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE);
94         if (sdk != null) {
95           files.addAll(sdk.getParent().getFiles(JpsOrderRootType.COMPILED));
96         }
97       }
98     }
99     return files;
100   }
101
102   private static void addFile(Set<? super File> classpath, @Nullable String url) {
103     if (url != null) {
104       classpath.add(JpsPathUtil.urlToFile(url));
105     }
106   }
107
108   /**
109    * Returns a mapping "sourceRoot" -> "package prefix". A package prefix uses slashes instead of dots and ends with a trailing slash.
110    */
111   @NotNull
112   public static Map<File, String> getSourceRootsWithDependents(ModuleChunk chunk) {
113     final boolean includeTests = chunk.containsTests();
114     final Map<File, String> result = new LinkedHashMap<>();
115     processModulesRecursively(chunk, JpsJavaClasspathKind.compile(includeTests), module -> {
116       for (JpsModuleSourceRoot root : module.getSourceRoots()) {
117         if (root.getRootType().equals(JavaSourceRootType.SOURCE) ||
118             includeTests && root.getRootType().equals(JavaSourceRootType.TEST_SOURCE)) {
119           String prefix = ((JavaSourceRootProperties)root.getProperties()).getPackagePrefix();
120           if (!prefix.isEmpty()) {
121             prefix = prefix.replace('.', '/');
122             if (!prefix.endsWith("/")) {
123               prefix += "/";
124             }
125           }
126           else {
127             prefix = null;
128           }
129           result.put(JpsPathUtil.urlToFile(root.getUrl()), prefix);
130         }
131       }
132     });
133     return result;
134   }
135
136   public static Collection<File> getOutputPathsWithDependents(final ModuleChunk chunk) {
137     final boolean forTests = chunk.containsTests();
138     final Set<File> sourcePaths = new LinkedHashSet<>();
139     processModulesRecursively(chunk, JpsJavaClasspathKind.compile(forTests),
140                               module -> addFile(sourcePaths, JpsJavaExtensionService.getInstance().getOutputUrl(module, forTests)));
141     return sourcePaths;
142   }
143
144   private static void processModulesRecursively(ModuleChunk chunk, JpsJavaClasspathKind kind, Consumer<? super JpsModule> processor) {
145     JpsJavaExtensionService.getInstance().enumerateDependencies(chunk.getModules()).includedIn(kind).recursively().processModules(processor);
146   }
147
148   @Nullable
149   public static File getModuleOutputDir(JpsModule module, boolean forTests) {
150     return JpsJavaExtensionService.getInstance().getOutputDirectory(module, forTests);
151   }
152
153   @Nullable
154   public static File getAnnotationProcessorGeneratedSourcesOutputDir(JpsModule module, final boolean forTests, ProcessorConfigProfile profile) {
155     final String sourceDirName = profile.getGeneratedSourcesDirectoryName(forTests);
156     if (profile.isOutputRelativeToContentRoot()) {
157       List<String> roots = module.getContentRootsList().getUrls();
158       if (roots.isEmpty()) {
159         return null;
160       }
161       if (roots.size() > 1) {
162         roots = new ArrayList<>(roots); // sort roots to get deterministic result
163         roots.sort(Comparator.naturalOrder());
164       }
165       final File parent = JpsPathUtil.urlToFile(roots.get(0));
166       return StringUtil.isEmpty(sourceDirName)? parent : new File(parent, sourceDirName);
167     }
168
169     final File outputDir = getModuleOutputDir(module, forTests);
170     if (outputDir == null) {
171       return null;
172     }
173     return StringUtil.isEmpty(sourceDirName)? outputDir : new File(outputDir, sourceDirName);
174   }
175
176   private enum ClasspathPart {WHOLE, BEFORE_JDK, BEFORE_PLUS_JDK, AFTER_JDK}
177
178   private static class BeforeJavaSdkItemFilter implements Condition<JpsDependencyElement> {
179     private final JpsModule myModule;
180     private boolean mySdkFound;
181
182     private BeforeJavaSdkItemFilter(JpsModule module) {
183       myModule = module;
184     }
185
186     @Override
187     public boolean value(JpsDependencyElement dependency) {
188       boolean isJavaSdk = dependency instanceof JpsSdkDependency && ((JpsSdkDependency)dependency).getSdkType().equals(JpsJavaSdkType.INSTANCE);
189       if (myModule.equals(dependency.getContainingModule()) && isJavaSdk) {
190         mySdkFound = true;
191       }
192       return !mySdkFound && !isJavaSdk;
193     }
194   }
195
196   private static class AfterJavaSdkItemFilter implements Condition<JpsDependencyElement> {
197     private final JpsModule myModule;
198     private boolean mySdkFound;
199
200     private AfterJavaSdkItemFilter(JpsModule module) {
201       myModule = module;
202     }
203
204     @Override
205     public boolean value(JpsDependencyElement dependency) {
206       if (myModule.equals(dependency.getContainingModule())) {
207         if (dependency instanceof JpsSdkDependency && ((JpsSdkDependency)dependency).getSdkType().equals(JpsJavaSdkType.INSTANCE)) {
208           mySdkFound = true;
209           return false;
210         }
211       }
212       return mySdkFound;
213     }
214   }
215 }