| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.compiler.ant; |
| |
| import com.intellij.compiler.ant.taskdefs.Path; |
| import com.intellij.compiler.ant.taskdefs.PathElement; |
| import com.intellij.compiler.ant.taskdefs.PathRef; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.ex.ProjectEx; |
| import com.intellij.openapi.roots.*; |
| import com.intellij.openapi.vfs.JarFileSystem; |
| import com.intellij.openapi.vfs.VirtualFileManager; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.OrderedSet; |
| |
| import java.io.File; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| /** |
| * Generator of module chunk classspath. The class used to generate both runtime and compile time classpaths. |
| * |
| * @author Eugene Zhuravlev |
| */ |
| public class ModuleChunkClasspath extends Path { |
| |
| /** |
| * A constructor |
| * |
| * @param chunk a chunk to process |
| * @param genOptions a generation options |
| * @param generateRuntimeClasspath if true, runtime classpath is being generated. Otherwise a compile time classpath is constructed |
| * @param generateTestClasspath if true, a test classpath is generated. |
| */ |
| @SuppressWarnings({"unchecked"}) |
| public ModuleChunkClasspath(final ModuleChunk chunk, |
| final GenerationOptions genOptions, |
| final boolean generateRuntimeClasspath, |
| final boolean generateTestClasspath) { |
| super(generateClasspathName(chunk, generateRuntimeClasspath, generateTestClasspath)); |
| |
| final OrderedSet<ClasspathItem> pathItems = |
| new OrderedSet<ClasspathItem>(); |
| final String moduleChunkBasedirProperty = BuildProperties.getModuleChunkBasedirProperty(chunk); |
| final Module[] modules = chunk.getModules(); |
| // processed chunks (used only in runtime classpath), every chunk is referenced exactly once |
| final Set<ModuleChunk> processedChunks = new HashSet<ModuleChunk>(); |
| // pocessed modules |
| final Set<Module> processedModules = new HashSet<Module>(); |
| for (final Module module : modules) { |
| new Object() { |
| /** |
| * Process the module. The logic is different for compile-time case and runtime case. |
| * In the case of runtime, only directly referenced objects are included in classpath. |
| * Indirectly referenced are |
| * |
| * @param module a module to process. |
| * @param dependencyLevel is increased with every of recursion. |
| * @param isModuleExported if true the module is exported from the previous level |
| */ |
| public void processModule(final Module module, final int dependencyLevel, final boolean isModuleExported) { |
| if (processedModules.contains(module)) { |
| // the module is already processed, nothing should be done |
| return; |
| } |
| if (dependencyLevel > 1 && !isModuleExported && !(genOptions.inlineRuntimeClasspath && generateRuntimeClasspath)) { |
| // the module is not in exports and it is not directly included skip it in the case of library pathgeneration |
| return; |
| } |
| processedModules.add(module); |
| final ProjectEx project = (ProjectEx)chunk.getProject(); |
| final File baseDir = BuildProperties.getProjectBaseDir(project); |
| OrderEnumerator enumerator = ModuleRootManager.getInstance(module).orderEntries(); |
| if (generateRuntimeClasspath) { |
| enumerator = enumerator.runtimeOnly(); |
| } |
| else { |
| enumerator = enumerator.compileOnly(); |
| if (!generateTestClasspath && (dependencyLevel == 0 || chunk.contains(module))) { |
| // this is the entry for outpath of the currently processed module |
| // the root module is never included |
| enumerator = enumerator.withoutModuleSourceEntries(); |
| } |
| } |
| if (!generateTestClasspath) { |
| enumerator = enumerator.productionOnly(); |
| } |
| enumerator.forEach(new Processor<OrderEntry>() { |
| @Override |
| public boolean process(OrderEntry orderEntry) { |
| if (!orderEntry.isValid()) { |
| return true; |
| } |
| |
| if (!generateRuntimeClasspath && |
| !(orderEntry instanceof ModuleOrderEntry) && |
| !(orderEntry instanceof ModuleSourceOrderEntry)) { |
| // needed for compilation classpath only |
| final boolean isExported = (orderEntry instanceof ExportableOrderEntry) && ((ExportableOrderEntry)orderEntry).isExported(); |
| if (dependencyLevel > 0 && !isExported) { |
| // non-exported dependencies are excluded and not processed |
| return true; |
| } |
| } |
| |
| if (orderEntry instanceof JdkOrderEntry) { |
| if (genOptions.forceTargetJdk && !generateRuntimeClasspath) { |
| pathItems |
| .add(new PathRefItem(BuildProperties.propertyRef(BuildProperties.getModuleChunkJdkClasspathProperty(chunk.getName())))); |
| } |
| } |
| else if (orderEntry instanceof ModuleOrderEntry) { |
| final ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)orderEntry; |
| final Module dependentModule = moduleOrderEntry.getModule(); |
| if (!chunk.contains(dependentModule)) { |
| if (generateRuntimeClasspath && !genOptions.inlineRuntimeClasspath) { |
| // in case of runtime classpath, just an referenced to corresponding classpath is created |
| final ModuleChunk depChunk = genOptions.getChunkByModule(dependentModule); |
| if (!processedChunks.contains(depChunk)) { |
| // chunk references are included in the runtime classpath only once |
| processedChunks.add(depChunk); |
| String property = generateTestClasspath ? BuildProperties.getTestRuntimeClasspathProperty(depChunk.getName()) |
| : BuildProperties.getRuntimeClasspathProperty(depChunk.getName()); |
| pathItems.add(new PathRefItem(property)); |
| } |
| } |
| else { |
| // in case of compile classpath or inlined runtime classpath, |
| // the referenced module is processed recursively |
| processModule(dependentModule, dependencyLevel + 1, moduleOrderEntry.isExported()); |
| } |
| } |
| } |
| else if (orderEntry instanceof LibraryOrderEntry) { |
| final LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry)orderEntry; |
| final String libraryName = libraryOrderEntry.getLibraryName(); |
| if (((LibraryOrderEntry)orderEntry).isModuleLevel()) { |
| CompositeGenerator gen = new CompositeGenerator(); |
| gen.setHasLeadingNewline(false); |
| LibraryDefinitionsGeneratorFactory.genLibraryContent(project, genOptions, libraryOrderEntry.getLibrary(), baseDir, gen); |
| pathItems.add(new GeneratorItem(libraryName, gen)); |
| } |
| else { |
| pathItems.add(new PathRefItem(BuildProperties.getLibraryPathId(libraryName))); |
| } |
| } |
| else if (orderEntry instanceof ModuleSourceOrderEntry) { |
| // Module source entry? |
| for (String url : getCompilationClasses(module, ((GenerationOptionsImpl)genOptions), generateRuntimeClasspath, |
| generateTestClasspath, dependencyLevel == 0)) { |
| if (url.endsWith(JarFileSystem.JAR_SEPARATOR)) { |
| url = url.substring(0, url.length() - JarFileSystem.JAR_SEPARATOR.length()); |
| } |
| final String propertyRef = genOptions.getPropertyRefForUrl(url); |
| if (propertyRef != null) { |
| pathItems.add(new PathElementItem(propertyRef)); |
| } |
| else { |
| final String path = VirtualFileManager.extractPath(url); |
| pathItems.add(new PathElementItem( |
| GenerationUtils.toRelativePath(path, chunk.getBaseDir(), moduleChunkBasedirProperty, genOptions))); |
| } |
| } |
| } |
| else { |
| // Unknown order entry type. If it is actually encountered, extension point should be implemented |
| pathItems.add(new GeneratorItem(orderEntry.getClass().getName(), |
| new Comment("Unknown OrderEntryType: " + orderEntry.getClass().getName()))); |
| } |
| return true; |
| } |
| }); |
| } |
| }.processModule(module, 0, false); |
| } |
| // convert path items to generators |
| for (final ClasspathItem pathItem : pathItems) { |
| add(pathItem.toGenerator()); |
| } |
| } |
| |
| /** |
| * Generate classpath name |
| * |
| * @param chunk a chunk |
| * @param generateRuntimeClasspath |
| * @param generateTestClasspath |
| * @return a name for the classpath |
| */ |
| private static String generateClasspathName(ModuleChunk chunk, boolean generateRuntimeClasspath, boolean generateTestClasspath) { |
| if (generateTestClasspath) { |
| return generateRuntimeClasspath |
| ? BuildProperties.getTestRuntimeClasspathProperty(chunk.getName()) |
| : BuildProperties.getTestClasspathProperty(chunk.getName()); |
| } |
| else { |
| return generateRuntimeClasspath |
| ? BuildProperties.getRuntimeClasspathProperty(chunk.getName()) |
| : BuildProperties.getClasspathProperty(chunk.getName()); |
| } |
| } |
| |
| private static String[] getCompilationClasses(final Module module, |
| final GenerationOptionsImpl options, |
| final boolean forRuntime, |
| final boolean forTest, |
| final boolean firstLevel) { |
| final CompilerModuleExtension extension = CompilerModuleExtension.getInstance(module); |
| if (extension == null) return ArrayUtil.EMPTY_STRING_ARRAY; |
| |
| if (!forRuntime) { |
| if (forTest) { |
| return extension.getOutputRootUrls(!firstLevel); |
| } |
| else { |
| return firstLevel ? ArrayUtil.EMPTY_STRING_ARRAY : extension.getOutputRootUrls(false); |
| } |
| } |
| final Set<String> jdkUrls = options.getAllJdkUrls(); |
| |
| final OrderedSet<String> urls = new OrderedSet<String>(); |
| ContainerUtil.addAll(urls, extension.getOutputRootUrls(forTest)); |
| urls.removeAll(jdkUrls); |
| return ArrayUtil.toStringArray(urls); |
| } |
| |
| /** |
| * The base class for an item in the class path. Instances of the subclasses are used instead |
| * of generators when building the class path content. The subclasses implement {@link Object#equals(Object)} |
| * and {@link Object#hashCode()} methods in order to eliminate duplicates when building classpath. |
| */ |
| private abstract static class ClasspathItem { |
| /** |
| * primary reference or path of the element |
| */ |
| protected final String myValue; |
| |
| /** |
| * A constructor |
| * |
| * @param value primary value of the element |
| */ |
| public ClasspathItem(String value) { |
| myValue = value; |
| } |
| |
| /** |
| * @return a generator for path elements. |
| */ |
| public abstract Generator toGenerator(); |
| } |
| |
| /** |
| * Class path item that directly embeds generator |
| */ |
| private static class GeneratorItem extends ClasspathItem { |
| /** |
| * An embedded generator |
| */ |
| final Generator myGenerator; |
| |
| /** |
| * A constructor |
| * |
| * @param value primary value of the element |
| * @param generator a generator to use |
| */ |
| public GeneratorItem(String value, final Generator generator) { |
| super(value); |
| myGenerator = generator; |
| } |
| |
| public Generator toGenerator() { |
| return myGenerator; |
| } |
| } |
| |
| /** |
| * This path element directly references some location. |
| */ |
| private static class PathElementItem extends ClasspathItem { |
| /** |
| * A constructor |
| * |
| * @param value a referenced location |
| */ |
| public PathElementItem(String value) { |
| super(value); |
| } |
| |
| @Override |
| public Generator toGenerator() { |
| return new PathElement(myValue); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (!(o instanceof PathElementItem)) return false; |
| |
| final PathElementItem pathElementItem = (PathElementItem)o; |
| |
| if (!myValue.equals(pathElementItem.myValue)) return false; |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| return myValue.hashCode(); |
| } |
| } |
| |
| /** |
| * This path element references a path |
| */ |
| private static class PathRefItem extends ClasspathItem { |
| /** |
| * A constructor |
| * |
| * @param value an indentifier of referenced classpath |
| */ |
| public PathRefItem(String value) { |
| super(value); |
| } |
| |
| @Override |
| public Generator toGenerator() { |
| return new PathRef(myValue); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (!(o instanceof PathRefItem)) return false; |
| |
| final PathRefItem pathRefItem = (PathRefItem)o; |
| |
| if (!myValue.equals(pathRefItem.myValue)) return false; |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| return myValue.hashCode(); |
| } |
| } |
| } |