| /* |
| * 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.application.options.ReplacePathToMacroMap; |
| import com.intellij.compiler.ModuleCompilerUtil; |
| import com.intellij.openapi.application.PathMacros; |
| import com.intellij.openapi.application.PathManager; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.projectRoots.ProjectJdkTable; |
| import com.intellij.openapi.projectRoots.Sdk; |
| import com.intellij.openapi.roots.CompilerModuleExtension; |
| import com.intellij.openapi.roots.OrderRootType; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.util.Chunk; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.graph.CachingSemiGraph; |
| import com.intellij.util.graph.Graph; |
| import com.intellij.util.graph.GraphGenerator; |
| |
| import javax.naming.OperationNotSupportedException; |
| import java.io.File; |
| import java.util.*; |
| |
| /** |
| * Implementation class for Ant generation options |
| * |
| * @author Eugene Zhuravlev |
| * Date: Mar 25, 2004 |
| */ |
| public class GenerationOptionsImpl extends GenerationOptions { |
| |
| /** |
| * from absolute path to macro substitutions |
| */ |
| private final ReplacePathToMacroMap myMacroReplacementMap; |
| /** |
| * from absolute path to macro substitutions |
| */ |
| private final Map<String, String> myOutputUrlToPropertyRefMap; |
| /** |
| * module chunks |
| */ |
| private final ModuleChunk[] myModuleChunks; |
| /** |
| * the project to be converted |
| */ |
| private final Project myProject; |
| private final boolean myGenerateIdeaHomeProperty; |
| private final String myOutputFileName; |
| private Set<String> myJdkUrls; |
| /** |
| * Custom compilers used in the ant build. |
| */ |
| private final Set<ChunkCustomCompilerExtension> myCustomCompilers = new HashSet<ChunkCustomCompilerExtension>(); |
| /** |
| * map from modules to chunks |
| */ |
| private final Map<Module, ModuleChunk> myModuleToChunkMap = new HashMap<Module, ModuleChunk>(); |
| |
| /** |
| * A constructor |
| * |
| * @param project a project to generate |
| * @param generateSingleFile a value of corresponding option |
| * @param enableFormCompiler a value of corresponding option |
| * @param backupPreviouslyGeneratedFiles a value of corresponding option |
| * @param forceTargetJdk a value of corresponding option |
| * @param inlineRuntimeClasspath if true a runtiem classpaths are inlined |
| * @param representativeModuleNames a module name that represents module chunks. |
| * @param outputFileName a name for the output file |
| */ |
| public GenerationOptionsImpl(Project project, |
| boolean generateSingleFile, |
| boolean enableFormCompiler, |
| boolean backupPreviouslyGeneratedFiles, |
| boolean forceTargetJdk, |
| boolean inlineRuntimeClasspath, |
| boolean generateIdeaHomeProperty, |
| String[] representativeModuleNames, String outputFileName) { |
| super(forceTargetJdk, generateSingleFile, enableFormCompiler, backupPreviouslyGeneratedFiles, inlineRuntimeClasspath); |
| myProject = project; |
| myGenerateIdeaHomeProperty = generateIdeaHomeProperty; |
| myOutputFileName = outputFileName; |
| myMacroReplacementMap = createReplacementMap(); |
| myModuleChunks = createModuleChunks(representativeModuleNames); |
| myOutputUrlToPropertyRefMap = createOutputUrlToPropertyRefMap(myModuleChunks); |
| } |
| |
| /** |
| * A constructor |
| * |
| * @param project a project to generate |
| * @param forceTargetJdk a value of corresponding option |
| * @param generateSingleFile a value of corresponding option |
| * @param enableFormCompiler a value of corresponding option |
| * @param backupPreviouslyGeneratedFiles a value of corresponding option |
| * @param representativeModuleNames a module name that represents module chunks. |
| */ |
| @Deprecated |
| public GenerationOptionsImpl(Project project, |
| boolean generateSingleFile, |
| boolean enableFormCompiler, |
| boolean backupPreviouslyGeneratedFiles, |
| boolean forceTargetJdk, |
| String[] representativeModuleNames) { |
| this(project, forceTargetJdk, generateSingleFile, enableFormCompiler, backupPreviouslyGeneratedFiles, false, false, |
| representativeModuleNames, null); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean isIdeaHomeGenerated() { |
| return myGenerateIdeaHomeProperty; |
| } |
| |
| public String getBuildFileName() { |
| return getOutputFileName() + ".xml"; |
| } |
| |
| public String getPropertiesFileName() { |
| return getOutputFileName() + ".properties"; |
| } |
| |
| private String getOutputFileName() { |
| if (myOutputFileName == null || myOutputFileName.length() == 0) { |
| return BuildProperties.getProjectBuildFileName(myProject); |
| } |
| return myOutputFileName; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public ModuleChunk getChunkByModule(final Module module) { |
| if (myModuleToChunkMap.isEmpty()) { |
| for (ModuleChunk c : myModuleChunks) { |
| for (Module m : c.getModules()) { |
| myModuleToChunkMap.put(m, c); |
| } |
| } |
| } |
| return myModuleToChunkMap.get(module); |
| } |
| |
| @Override |
| public String subsitutePathWithMacros(String path) { |
| return myMacroReplacementMap.substitute(path, SystemInfo.isFileSystemCaseSensitive); |
| } |
| |
| public String getPropertyRefForUrl(String url) { |
| return myOutputUrlToPropertyRefMap.get(url); |
| } |
| |
| private static ReplacePathToMacroMap createReplacementMap() { |
| final PathMacros pathMacros = PathMacros.getInstance(); |
| final Set<String> macroNames = pathMacros.getUserMacroNames(); |
| final ReplacePathToMacroMap map = new ReplacePathToMacroMap(); |
| for (final String macroName : macroNames) { |
| map.put(GenerationUtils.normalizePath(pathMacros.getValue(macroName)), |
| BuildProperties.propertyRef(BuildProperties.getPathMacroProperty(macroName))); |
| } |
| map.put(GenerationUtils.normalizePath(PathManager.getHomePath()), BuildProperties.propertyRef(BuildProperties.PROPERTY_IDEA_HOME)); |
| return map; |
| } |
| |
| private static Map<String, String> createOutputUrlToPropertyRefMap(ModuleChunk[] chunks) { |
| final Map<String, String> map = new HashMap<String, String>(); |
| |
| for (final ModuleChunk chunk : chunks) { |
| final String outputPathRef = BuildProperties.propertyRef(BuildProperties.getOutputPathProperty(chunk.getName())); |
| final String testsOutputPathRef = BuildProperties.propertyRef(BuildProperties.getOutputPathForTestsProperty(chunk.getName())); |
| |
| final Module[] modules = chunk.getModules(); |
| for (final Module module : modules) { |
| final String outputPathUrl = CompilerModuleExtension.getInstance(module).getCompilerOutputUrl(); |
| if (outputPathUrl != null) { |
| map.put(outputPathUrl, outputPathRef); |
| } |
| final String outputPathForTestsUrl = CompilerModuleExtension.getInstance(module).getCompilerOutputUrlForTests(); |
| if (outputPathForTestsUrl != null) { |
| if (outputPathUrl == null || !outputPathForTestsUrl.equals(outputPathUrl)) { |
| map.put(outputPathForTestsUrl, testsOutputPathRef); |
| } |
| } |
| } |
| } |
| return map; |
| } |
| |
| @Override |
| public ModuleChunk[] getModuleChunks() { |
| return myModuleChunks; |
| } |
| |
| private ModuleChunk[] createModuleChunks(String[] representativeModuleNames) { |
| final Set<String> mainModuleNames = new HashSet<String>(Arrays.asList(representativeModuleNames)); |
| final Graph<Chunk<Module>> chunkGraph = ModuleCompilerUtil.toChunkGraph(ModuleManager.getInstance(myProject).moduleGraph()); |
| final Map<Chunk<Module>, ModuleChunk> map = new HashMap<Chunk<Module>, ModuleChunk>(); |
| final Map<ModuleChunk, Chunk<Module>> reverseMap = new HashMap<ModuleChunk, Chunk<Module>>(); |
| for (final Chunk<Module> chunk : chunkGraph.getNodes()) { |
| final Set<Module> modules = chunk.getNodes(); |
| final ModuleChunk moduleChunk = new ModuleChunk(modules.toArray(new Module[modules.size()])); |
| for (final Module module : modules) { |
| if (mainModuleNames.contains(module.getName())) { |
| moduleChunk.setMainModule(module); |
| break; |
| } |
| } |
| map.put(chunk, moduleChunk); |
| reverseMap.put(moduleChunk, chunk); |
| } |
| |
| final Graph<ModuleChunk> moduleChunkGraph = |
| GraphGenerator.create(CachingSemiGraph.create(new GraphGenerator.SemiGraph<ModuleChunk>() { |
| public Collection<ModuleChunk> getNodes() { |
| return map.values(); |
| } |
| |
| public Iterator<ModuleChunk> getIn(ModuleChunk n) { |
| final Chunk<Module> chunk = reverseMap.get(n); |
| final Iterator<Chunk<Module>> in = chunkGraph.getIn(chunk); |
| return new Iterator<ModuleChunk>() { |
| public boolean hasNext() { |
| return in.hasNext(); |
| } |
| |
| public ModuleChunk next() { |
| return map.get(in.next()); |
| } |
| |
| public void remove() { |
| new OperationNotSupportedException(); |
| } |
| }; |
| } |
| })); |
| final Collection<ModuleChunk> nodes = moduleChunkGraph.getNodes(); |
| final ModuleChunk[] moduleChunks = nodes.toArray(new ModuleChunk[nodes.size()]); |
| for (ModuleChunk moduleChunk : moduleChunks) { |
| final Iterator<ModuleChunk> depsIterator = moduleChunkGraph.getIn(moduleChunk); |
| List<ModuleChunk> deps = new ArrayList<ModuleChunk>(); |
| while (depsIterator.hasNext()) { |
| deps.add(depsIterator.next()); |
| } |
| moduleChunk.setDependentChunks(deps.toArray(new ModuleChunk[deps.size()])); |
| ContainerUtil.addAll(myCustomCompilers, moduleChunk.getCustomCompilers()); |
| } |
| Arrays.sort(moduleChunks, new ChunksComparator()); |
| if (generateSingleFile) { |
| final File baseDir = BuildProperties.getProjectBaseDir(myProject); |
| for (ModuleChunk chunk : moduleChunks) { |
| chunk.setBaseDir(baseDir); |
| } |
| } |
| return moduleChunks; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public ChunkCustomCompilerExtension[] getCustomCompilers() { |
| ChunkCustomCompilerExtension[] sorted = myCustomCompilers.toArray(new ChunkCustomCompilerExtension[myCustomCompilers.size()]); |
| Arrays.sort(sorted, ChunkCustomCompilerExtension.COMPARATOR); |
| return sorted; |
| } |
| |
| Set<String> getAllJdkUrls() { |
| if (myJdkUrls != null) { |
| return myJdkUrls; |
| } |
| final Sdk[] projectJdks = ProjectJdkTable.getInstance().getAllJdks(); |
| myJdkUrls = new HashSet<String>(); |
| for (Sdk jdk : projectJdks) { |
| ContainerUtil.addAll(myJdkUrls, jdk.getRootProvider().getUrls(OrderRootType.CLASSES)); |
| } |
| return myJdkUrls; |
| } |
| |
| private static class ChunksComparator implements Comparator<ModuleChunk> { |
| final Map<ModuleChunk, Integer> myCachedLevels = new HashMap<ModuleChunk, Integer>(); |
| |
| public int compare(final ModuleChunk o1, final ModuleChunk o2) { |
| final int level1 = getChunkLevel(o1); |
| final int level2 = getChunkLevel(o2); |
| return (level1 == level2) ? o1.getName().compareToIgnoreCase(o2.getName()) : (level1 - level2); |
| } |
| |
| private int getChunkLevel(ModuleChunk chunk) { |
| Integer level = myCachedLevels.get(chunk); |
| if (level == null) { |
| final ModuleChunk[] chunks = chunk.getDependentChunks(); |
| if (chunks.length > 0) { |
| int maxLevel = 0; |
| for (ModuleChunk dependent : chunks) { |
| maxLevel = Math.max(maxLevel, getChunkLevel(dependent)); |
| } |
| level = 1 + maxLevel; |
| } |
| else { |
| level = 0; |
| } |
| myCachedLevels.put(chunk, level); |
| } |
| return level.intValue(); |
| } |
| } |
| } |