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