simplify GTDUCollector a bit more
[idea/community.git] / java / compiler / impl / src / com / intellij / packaging / impl / artifacts / JarFromModulesTemplate.java
1 /*
2  * Copyright 2000-2017 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.packaging.impl.artifacts;
17
18 import com.intellij.CommonBundle;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.compiler.JavaCompilerBundle;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.module.Module;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.roots.OrderEnumerator;
25 import com.intellij.openapi.roots.OrderRootType;
26 import com.intellij.openapi.roots.ProjectRootManager;
27 import com.intellij.openapi.roots.libraries.Library;
28 import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
29 import com.intellij.openapi.ui.Messages;
30 import com.intellij.openapi.util.ThrowableComputable;
31 import com.intellij.openapi.vfs.VfsUtil;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.packaging.artifacts.ArtifactTemplate;
34 import com.intellij.packaging.elements.*;
35 import com.intellij.packaging.impl.elements.LibraryPackagingElement;
36 import com.intellij.packaging.impl.elements.ManifestFileUtil;
37 import com.intellij.packaging.impl.elements.ProductionModuleOutputElementType;
38 import com.intellij.packaging.impl.elements.TestModuleOutputElementType;
39 import com.intellij.util.CommonProcessors;
40 import gnu.trove.THashSet;
41 import org.jetbrains.annotations.Nls;
42 import org.jetbrains.annotations.Nullable;
43
44 import java.io.IOException;
45 import java.util.*;
46
47 public class JarFromModulesTemplate extends ArtifactTemplate {
48   private static final Logger LOG = Logger.getInstance(JarFromModulesTemplate.class);
49
50   private final PackagingElementResolvingContext myContext;
51
52   public JarFromModulesTemplate(PackagingElementResolvingContext context) {
53     myContext = context;
54   }
55
56   @Override
57   public NewArtifactConfiguration createArtifact() {
58     JarArtifactFromModulesDialog dialog = new JarArtifactFromModulesDialog(myContext);
59     if (!dialog.showAndGet()) {
60       return null;
61     }
62
63     return doCreateArtifact(dialog.getSelectedModules(), dialog.getMainClassName(), dialog.getDirectoryForManifest(),
64                             dialog.isExtractLibrariesToJar(), dialog.isIncludeTests());
65   }
66
67   @Nullable
68   public NewArtifactConfiguration doCreateArtifact(final Module[] modules, final String mainClassName,
69                                                    final String directoryForManifest,
70                                                    final boolean extractLibrariesToJar,
71                                                    final boolean includeTests) {
72     VirtualFile manifestFile = null;
73     final Project project = myContext.getProject();
74     if (mainClassName != null && !mainClassName.isEmpty() || !extractLibrariesToJar) {
75       final VirtualFile directory;
76       try {
77         directory = ApplicationManager.getApplication().runWriteAction(
78           (ThrowableComputable<VirtualFile, IOException>)() -> VfsUtil.createDirectoryIfMissing(directoryForManifest));
79       }
80       catch (IOException e) {
81         LOG.info(e);
82         Messages.showErrorDialog(project, JavaCompilerBundle.message("cannot.create.directory.0.1", directoryForManifest, e.getMessage()),
83                                  CommonBundle.getErrorTitle());
84         return null;
85       }
86       if (directory == null) return null;
87
88       manifestFile = ManifestFileUtil.createManifestFile(directory, project);
89       if (manifestFile == null) {
90         return null;
91       }
92       ManifestFileUtil.updateManifest(manifestFile, mainClassName, null, true);
93     }
94
95     String name = modules.length == 1 ? modules[0].getName() : project.getName();
96
97     final PackagingElementFactory factory = PackagingElementFactory.getInstance();
98     final CompositePackagingElement<?> archive = factory.createArchive(ArtifactUtil.suggestArtifactFileName(name) + ".jar");
99
100     OrderEnumerator orderEnumerator = ProjectRootManager.getInstance(project).orderEntries(Arrays.asList(modules));
101
102     final Set<Library> libraries = new THashSet<>();
103     if (!includeTests) {
104       orderEnumerator = orderEnumerator.productionOnly();
105     }
106     final ModulesProvider modulesProvider = myContext.getModulesProvider();
107     final OrderEnumerator enumerator = orderEnumerator.using(modulesProvider).withoutSdk().runtimeOnly().recursively();
108     enumerator.forEachLibrary(new CommonProcessors.CollectProcessor<>(libraries));
109     enumerator.forEachModule(module -> {
110       if (ProductionModuleOutputElementType.ELEMENT_TYPE.isSuitableModule(modulesProvider, module)) {
111         archive.addOrFindChild(factory.createModuleOutput(module));
112       }
113       if (includeTests && TestModuleOutputElementType.ELEMENT_TYPE.isSuitableModule(modulesProvider, module)) {
114         archive.addOrFindChild(factory.createTestModuleOutput(module));
115       }
116       return true;
117     });
118
119     final JarArtifactType jarArtifactType = JarArtifactType.getInstance();
120     if (manifestFile != null && !manifestFile.equals(ManifestFileUtil.findManifestFile(archive, myContext, jarArtifactType))) {
121       archive.addFirstChild(factory.createFileCopyWithParentDirectories(manifestFile.getPath(), ManifestFileUtil.MANIFEST_DIR_NAME));
122     }
123
124     final String artifactName = name + ":jar";
125     if (extractLibrariesToJar) {
126       addExtractedLibrariesToJar(archive, factory, libraries);
127       return new NewArtifactConfiguration(archive, artifactName, jarArtifactType);
128     }
129     else {
130       final ArtifactRootElement<?> root = factory.createArtifactRootElement();
131       List<String> classpath = new ArrayList<>();
132       root.addOrFindChild(archive);
133       addLibraries(libraries, root, archive, classpath);
134       ManifestFileUtil.updateManifest(manifestFile, mainClassName, classpath, true);
135       return new NewArtifactConfiguration(root, artifactName, PlainArtifactType.getInstance());
136     }
137   }
138
139   private void addLibraries(Set<? extends Library> libraries, ArtifactRootElement<?> root, CompositePackagingElement<?> archive,
140                             List<? super String> classpath) {
141     PackagingElementFactory factory = PackagingElementFactory.getInstance();
142     for (Library library : libraries) {
143       if (LibraryPackagingElement.getKindForLibrary(library).containsDirectoriesWithClasses()) {
144         for (VirtualFile classesRoot : library.getFiles(OrderRootType.CLASSES)) {
145           if (classesRoot.isInLocalFileSystem()) {
146             archive.addOrFindChild(factory.createDirectoryCopyWithParentDirectories(classesRoot.getPath(), "/"));
147           }
148           else {
149             final PackagingElement<?> child = factory.createFileCopyWithParentDirectories(VfsUtil.getLocalFile(classesRoot).getPath(), "/");
150             root.addOrFindChild(child);
151             classpath.addAll(ManifestFileUtil.getClasspathForElements(Collections.singletonList(child), myContext, PlainArtifactType.getInstance()));
152           }
153         }
154
155       }
156       else {
157         final List<? extends PackagingElement<?>> children = factory.createLibraryElements(library);
158         classpath.addAll(ManifestFileUtil.getClasspathForElements(children, myContext, PlainArtifactType.getInstance()));
159         root.addOrFindChildren(children);
160       }
161     }
162   }
163
164   private static void addExtractedLibrariesToJar(CompositePackagingElement<?> archive, PackagingElementFactory factory, Set<? extends Library> libraries) {
165     for (Library library : libraries) {
166       if (LibraryPackagingElement.getKindForLibrary(library).containsJarFiles()) {
167         for (VirtualFile classesRoot : library.getFiles(OrderRootType.CLASSES)) {
168           if (classesRoot.isInLocalFileSystem()) {
169             archive.addOrFindChild(factory.createDirectoryCopyWithParentDirectories(classesRoot.getPath(), "/"));
170           }
171           else {
172             archive.addOrFindChild(factory.createExtractedDirectory(classesRoot));
173           }
174         }
175
176       }
177       else {
178         archive.addOrFindChildren(factory.createLibraryElements(library));
179       }
180     }
181   }
182
183   @Override
184   public @Nls String getPresentableName() {
185     return JavaCompilerBundle.message("jar.from.modules.presentable.name");
186   }
187 }