diff --git a/plugins/maven/lib/plexus-archiver-2.4.4.jar b/plugins/maven/lib/plexus-archiver-2.4.4.jar new file mode 100644 index 000000000000..1a73adb04868 Binary files /dev/null and b/plugins/maven/lib/plexus-archiver-2.4.4.jar differ diff --git a/plugins/maven/maven.iml b/plugins/maven/maven.iml index 9afe51486f6f..231a57e4db15 100644 --- a/plugins/maven/maven.iml +++ b/plugins/maven/maven.iml @@ -63,6 +63,15 @@ + + + + + + + + + diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/ManifestImporter.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/ManifestImporter.java new file mode 100644 index 000000000000..65fb06cebfce --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/importing/ManifestImporter.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.maven.importing; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.roots.DependencyScope; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.maven.model.MavenArtifact; +import org.jetbrains.idea.maven.project.MavenProject; + +import static org.jetbrains.idea.maven.utils.ManifestBuilder.getClasspathPrefix; + +/** + * @author Vladislav.Soroka + * @since 5/26/2014 + */ +public abstract class ManifestImporter { + public static ExtensionPointName EXTENSION_POINT_NAME = + ExtensionPointName.create("org.jetbrains.idea.maven.manifestImporter"); + + @NotNull + public static ManifestImporter getManifestImporter(@NotNull String packaging) { + for (ManifestImporter importer : EXTENSION_POINT_NAME.getExtensions()) { + if (importer.isApplicable(packaging)) { + return importer; + } + } + return new DefaultManifestImporter(); + } + + @NotNull + public String getClasspath(@NotNull MavenProject mavenProject, + @Nullable Element manifestConfiguration) { + StringBuilder classpath = new StringBuilder(); + String classpathPrefix = getClasspathPrefix(manifestConfiguration); + for (MavenArtifact mavenArtifact : mavenProject.getDependencies()) { + final DependencyScope scope = MavenModuleImporter.selectScope(mavenArtifact.getScope()); + if (scope.isForProductionRuntime()) { + if (classpath.length() > 0) { + classpath.append(" "); + } + classpath.append(classpathPrefix); + String artifactFileName = mavenArtifact.getArtifactId() + "-" + mavenArtifact.getVersion() + "." + mavenArtifact.getExtension(); + classpath.append(doGetClasspathItem(mavenProject, mavenArtifact, artifactFileName)); + } + } + return classpath.toString(); + } + + protected abstract boolean isApplicable(String packaging); + + protected abstract String doGetClasspathItem(@NotNull MavenProject mavenProject, + @NotNull MavenArtifact mavenArtifact, + @NotNull String artifactFileName); + + private static class DefaultManifestImporter extends ManifestImporter { + + @Override + protected boolean isApplicable(String packaging) { + return true; + } + + @Override + protected String doGetClasspathItem(@NotNull MavenProject mavenProject, + @NotNull MavenArtifact mavenArtifact, + @NotNull String artifactFileName) { + return artifactFileName; + } + } +} diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java index 4a8365c3f830..86f73e9721fa 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenResourceCompilerConfigurationGenerator.java @@ -7,14 +7,17 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.*; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.impl.elements.ManifestFileUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Document; @@ -23,12 +26,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.dom.references.MavenFilteredPropertyPsiReferenceProvider; import org.jetbrains.idea.maven.model.MavenId; import org.jetbrains.idea.maven.model.MavenResource; +import org.jetbrains.idea.maven.utils.ManifestBuilder; import org.jetbrains.idea.maven.utils.MavenJDOMUtil; import org.jetbrains.idea.maven.utils.MavenUtil; import org.jetbrains.jps.maven.model.impl.*; import java.io.*; import java.util.*; +import java.util.jar.Manifest; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -146,6 +151,8 @@ public class MavenResourceCompilerConfigurationGenerator { } projectConfig.moduleConfigurations.put(module.getName(), resourceConfig); + + generateManifest(mavenProject, module); } addNonMavenResources(projectConfig); @@ -175,6 +182,35 @@ public class MavenResourceCompilerConfigurationGenerator { }); } + private static void generateManifest(@NotNull MavenProject mavenProject, @NotNull Module module) { + try { + String jdkVersion = null; + Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); + if (sdk != null && (jdkVersion = sdk.getVersionString()) != null) { + final int quoteIndex = jdkVersion.indexOf('"'); + if (quoteIndex != -1) { + jdkVersion = jdkVersion.substring(quoteIndex + 1, jdkVersion.length() - 1); + } + } + Manifest manifest = new ManifestBuilder(mavenProject).withJdkVersion(jdkVersion).build(); + File manifestFile = new File(mavenProject.getBuildDirectory(), ManifestFileUtil.MANIFEST_FILE_NAME); + FileUtil.createIfDoesntExist(manifestFile); + OutputStream outputStream = new FileOutputStream(manifestFile); + try { + manifest.write(outputStream); + } + finally { + StreamUtil.closeStream(outputStream); + } + } + catch (ManifestBuilder.ManifestBuilderException e) { + LOG.error("Unable to generate artifact manifest", e); + } + catch (IOException e) { + LOG.error("Unable to save generated artifact manifest", e); + } + } + private Properties getFilteringProperties(MavenProject mavenProject) { final Properties properties = new Properties(); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/ManifestBuilder.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/ManifestBuilder.java new file mode 100644 index 000000000000..eaeacedf72b0 --- /dev/null +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/utils/ManifestBuilder.java @@ -0,0 +1,317 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.maven.utils; + +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.StreamUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.ContainerUtil; +import org.codehaus.plexus.archiver.jar.Manifest; +import org.codehaus.plexus.archiver.jar.ManifestException; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.maven.importing.ManifestImporter; +import org.jetbrains.idea.maven.project.MavenProject; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.jar.Attributes; + +import static org.codehaus.plexus.archiver.jar.Manifest.Attribute; + +/** + * @author Vladislav.Soroka + * @since 5/22/2014 + */ +public class ManifestBuilder { + + private static final Map PACKAGING_PLUGINS = ContainerUtil.newHashMap( + Pair.create("jar", "maven-jar-plugin"), + Pair.create("ejb", "maven-ejb-plugin"), + Pair.create("ejb-client", "maven-ejb-plugin"), + Pair.create("war", "maven-war-plugin"), + Pair.create("ear", "maven-ear-plugin") + ); + + @NotNull private final MavenProject myMavenProject; + @Nullable private String myJdkVersion; + + public ManifestBuilder(@NotNull MavenProject mavenProject) { + myMavenProject = mavenProject; + } + + public ManifestBuilder withJdkVersion(String jdkVersion) { + myJdkVersion = jdkVersion; + return this; + } + + @NotNull + public java.util.jar.Manifest build() throws ManifestBuilderException { + try { + Element mavenPackagingPluginConfiguration = null; + final String packaging = myMavenProject.getPackaging(); + if (StringUtil.isEmpty(packaging)) { + mavenPackagingPluginConfiguration = myMavenProject.getPluginConfiguration("org.apache.maven.plugins", "maven-jar-plugin"); + } + else { + final String pluginArtifactId = PACKAGING_PLUGINS.get(StringUtil.toLowerCase(packaging)); + if (pluginArtifactId != null) { + mavenPackagingPluginConfiguration = myMavenProject.getPluginConfiguration("org.apache.maven.plugins", pluginArtifactId); + } + } + + final Element mavenArchiveConfiguration = + mavenPackagingPluginConfiguration != null ? mavenPackagingPluginConfiguration.getChild("archive") : null; + + if (mavenArchiveConfiguration == null) return getDefaultManifest(Collections.emptyMap()); + + final Element manifestEntries = mavenArchiveConfiguration.getChild("manifestEntries"); + Map entries = getManifestEntries(manifestEntries); + + final Element manifestConfiguration = mavenArchiveConfiguration.getChild("manifest"); + final Manifest configuredManifest = getConfiguredManifest(myMavenProject, manifestConfiguration, entries); + + if (!entries.isEmpty()) { + addManifestEntries(configuredManifest, entries); + } + + addCustomManifestSections(configuredManifest, mavenArchiveConfiguration); + + Manifest finalManifest = getDefaultManifest(entries); + // merge configured manifest + merge(finalManifest, configuredManifest); + + // merge user supplied manifest + final Manifest userSuppliedManifest = getUserSuppliedManifest(mavenArchiveConfiguration); + merge(finalManifest, userSuppliedManifest); + return finalManifest; + } + catch (ManifestException e) { + throw new ManifestBuilderException(e); + } + } + + @NotNull + public static String getClasspathPrefix(@Nullable Element manifestConfiguration) { + String classpathPrefix = MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "classpathPrefix", "").replaceAll("\\\\", "/"); + if (classpathPrefix.length() != 0 && !classpathPrefix.endsWith("/")) { + classpathPrefix += "/"; + } + return classpathPrefix; + } + + + private static Map getManifestEntries(Element manifestEntries) { + boolean hasManifestEntries = manifestEntries != null && manifestEntries.getContentSize() > 0; + Map entries = hasManifestEntries ? + new HashMap(manifestEntries.getContentSize()) : + Collections.emptyMap(); + if (hasManifestEntries) { + for (Element element : manifestEntries.getChildren()) { + entries.put(element.getName(), element.getTextTrim()); + } + } + return entries; + } + + private static void addCustomManifestSections(@NotNull Manifest manifest, @NotNull Element mavenArchiveConfiguration) + throws ManifestException { + + for (Element section : MavenJDOMUtil.findChildrenByPath(mavenArchiveConfiguration, "manifestSections", "manifestSection")) { + Manifest.Section theSection = new Manifest.Section(); + + final String sectionName = MavenJDOMUtil.findChildValueByPath(section, "name"); + theSection.setName(sectionName); + + final Element manifestEntries = section.getChild("manifestEntries"); + Map entries = getManifestEntries(manifestEntries); + + if (!entries.isEmpty()) { + for (Map.Entry entry : entries.entrySet()) { + Attribute attr = new Attribute(entry.getKey(), entry.getValue()); + theSection.addConfiguredAttribute(attr); + } + } + manifest.addConfiguredSection(theSection); + } + } + + @NotNull + private Manifest getDefaultManifest(@NotNull Map entries) throws ManifestException { + Manifest finalManifest = new Manifest(); + addManifestAttribute(finalManifest, entries, "Created-By", "IntelliJ IDEA"); + addManifestAttribute(finalManifest, entries, "Built-By", System.getProperty("user.name")); + if (!StringUtil.isEmpty(myJdkVersion)) { + addManifestAttribute(finalManifest, entries, "Build-Jdk", myJdkVersion); + } + return finalManifest; + } + + private static void addManifestEntries(@NotNull Manifest manifest, @NotNull Map entries) + throws ManifestException { + if (!entries.isEmpty()) { + for (Map.Entry entry : entries.entrySet()) { + Attribute attr = manifest.getMainSection().getAttribute(entry.getKey()); + if ("Class-Path".equals(entry.getKey()) && attr != null) { + // Merge the user-supplied Class-Path value with the programmatically + // generated Class-Path. Note that the user-supplied value goes first + // so that resources there will override any in the standard Class-Path. + attr.setValue(entry.getValue() + " " + attr.getValue()); + } + else { + addManifestAttribute(manifest, entry.getKey(), entry.getValue()); + } + } + } + + Set keys = entries.keySet(); + for (String key : keys) { + String value = entries.get(key); + Attribute attr = manifest.getMainSection().getAttribute(key); + if (key.equals("Class-Path") && attr != null) { + // Merge the user-supplied Class-Path value with the programmatically + // generated Class-Path. Note that the user-supplied value goes first + // so that resources there will override any in the standard Class-Path. + attr.setValue(value + " " + attr.getValue()); + } + else { + addManifestAttribute(manifest, key, value); + } + } + } + + @Nullable + private Manifest getUserSuppliedManifest(@Nullable Element mavenArchiveConfiguration) { + Manifest manifest = null; + String manifestPath = MavenJDOMUtil.findChildValueByPath(mavenArchiveConfiguration, "manifestFile"); + if (manifestPath != null) { + File manifestFile = new File(manifestPath); + if (!manifestFile.isAbsolute()) { + manifestFile = new File(myMavenProject.getDirectory(), manifestPath); + } + if (manifestFile.isFile()) { + FileInputStream fis = null; + try { + //noinspection IOResourceOpenedButNotSafelyClosed + fis = new FileInputStream(manifestFile); + manifest = new Manifest(fis); + } + catch (IOException ignore) { + } + finally { + StreamUtil.closeStream(fis); + } + } + } + + return manifest; + } + + @NotNull + private static Manifest getConfiguredManifest(@NotNull MavenProject mavenProject, + @Nullable Element manifestConfiguration, + @NotNull Map entries) throws ManifestException { + final Manifest manifest = new Manifest(); + + boolean isAddDefaultSpecificationEntries = + Boolean.valueOf(MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "addDefaultSpecificationEntries", "false")); + if (isAddDefaultSpecificationEntries) { + addManifestAttribute(manifest, entries, "Specification-Title", mavenProject.getName()); + addManifestAttribute(manifest, entries, "Specification-Version", mavenProject.getMavenId().getVersion()); + } + + boolean isAddDefaultImplementationEntries = + Boolean.valueOf(MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "addDefaultImplementationEntries", "false")); + if (isAddDefaultImplementationEntries) { + addManifestAttribute(manifest, entries, "Implementation-Title", mavenProject.getName()); + addManifestAttribute(manifest, entries, "Implementation-Version", mavenProject.getMavenId().getVersion()); + addManifestAttribute(manifest, entries, "Implementation-Vendor-Id", mavenProject.getMavenId().getGroupId()); + } + + String packageName = MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "packageName"); + if (packageName != null) { + addManifestAttribute(manifest, entries, "Package", packageName); + } + + String mainClass = MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "mainClass"); + if (!StringUtil.isEmpty(mainClass)) { + addManifestAttribute(manifest, entries, "Main-Class", mainClass); + } + + boolean isAddClasspath = Boolean.valueOf(MavenJDOMUtil.findChildValueByPath(manifestConfiguration, "addClasspath", "false")); + if (isAddClasspath) { + final ManifestImporter manifestImporter = ManifestImporter.getManifestImporter(mavenProject.getPackaging()); + String classpath = manifestImporter.getClasspath(mavenProject, manifestConfiguration); + if (!classpath.isEmpty()) { + addManifestAttribute(manifest, "Class-Path", classpath); + } + } + return manifest; + } + + private static void addManifestAttribute(@NotNull Manifest manifest, @NotNull Map map, String key, String value) + throws ManifestException { + if (map.containsKey(key)) return; + addManifestAttribute(manifest, key, value); + } + + private static void addManifestAttribute(@NotNull Manifest manifest, String key, String value) throws ManifestException { + if (!StringUtil.isEmpty(value)) { + Attribute attr = new Attribute(key, value); + manifest.addConfiguredAttribute(attr); + } + } + + private static void merge(@NotNull java.util.jar.Manifest target, @Nullable java.util.jar.Manifest other) { + if (other != null) { + mergeAttributes(target.getMainAttributes(), other.getMainAttributes()); + + for (Map.Entry o : other.getEntries().entrySet()) { + Attributes ourSection = target.getAttributes(o.getKey()); + Attributes otherSection = o.getValue(); + if (ourSection == null) { + if (otherSection != null) { + target.getEntries().put(o.getKey(), (Attributes)otherSection.clone()); + } + } + else { + mergeAttributes(ourSection, otherSection); + } + } + } + } + + private static void mergeAttributes(@NotNull Attributes target, @NotNull Attributes section) { + for (Object o : section.keySet()) { + Attributes.Name key = (Attributes.Name)o; + final Object value = section.get(o); + target.put(key, value); + } + } + + + public static class ManifestBuilderException extends Exception { + public ManifestBuilderException(Throwable cause) { + super(cause); + } + } +} diff --git a/plugins/maven/src/main/resources/META-INF/plugin.xml b/plugins/maven/src/main/resources/META-INF/plugin.xml index 303c92f1b56c..9580f83405ed 100644 --- a/plugins/maven/src/main/resources/META-INF/plugin.xml +++ b/plugins/maven/src/main/resources/META-INF/plugin.xml @@ -21,6 +21,7 @@ + diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ManifestGenerationTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ManifestGenerationTest.java new file mode 100644 index 000000000000..bdb025bf3edf --- /dev/null +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/compiler/ManifestGenerationTest.java @@ -0,0 +1,224 @@ +/* + * Copyright 2000-2014 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 org.jetbrains.idea.maven.compiler; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Vladislav.Soroka + * @since 5/23/2014 + */ +public class ManifestGenerationTest extends MavenCompilingTestCase { + @Override + protected boolean useJps() { + return true; + } + + public void testBasic() throws Exception { + importProject("test" + + "project" + + "1"); + + compileModules("project"); + + assertSameLinesWithFile(getProjectPath() + "/target/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Build-Jdk: " + extractJdkVersion(getModule("project")) + "\n" + + "Built-By: " + System.getProperty("user.name") + "\n" + + "Created-By: IntelliJ IDEA"); + } + + public void testClasspathEntry() throws Exception { + importProject("test" + + "project" + + "1" + + + "" + + " " + + " test" + + " other-project" + + " 1" + + " " + + "" + + + "" + + " " + + " " + + " maven-jar-plugin" + + " 2.4" + + " " + + " " + + " " + + " true" + + " lib" + + " " + + " " + + " " + + " " + + " " + + ""); + + compileModules("project"); + + assertSameLinesWithFile(getProjectPath() + "/target/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Class-Path: lib/other-project-1.jar\n" + + "Build-Jdk: " + extractJdkVersion(getModule("project")) + "\n" + + "Built-By: " + System.getProperty("user.name") + "\n" + + "Created-By: IntelliJ IDEA\n"); + } + + public void testDefaultEntries() throws Exception { + importProject("test" + + "project" + + "1" + + + "" + + " " + + " test" + + " other-project" + + " 1" + + " " + + "" + + + "" + + " " + + " " + + " maven-jar-plugin" + + " 2.4" + + " " + + " " + + " " + + " true" + + " true" + + " " + + " " + + " " + + " " + + " " + + ""); + + compileModules("project"); + + assertSameLinesWithFile(getProjectPath() + "/target/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Implementation-Version: 1\n" + + "Implementation-Vendor-Id: test\n" + + "Build-Jdk: " + extractJdkVersion(getModule("project")) + "\n" + + "Built-By: " + System.getProperty("user.name") + "\n" + + "Created-By: IntelliJ IDEA\n" + + "Specification-Version: 1"); + } + + + public void testManifestEntries() throws Exception { + importProject("test" + + "project" + + "1" + + + "" + + " " + + " " + + " maven-jar-plugin" + + " 2.4" + + " " + + " " + + " " + + " some.package" + + " other entry value " + + " " + + " " + + " " + + " " + + " " + + ""); + + compileModules("project"); + + assertSameLinesWithFile(getProjectPath() + "/target/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "otherEntry: other entry value\n" + + "Dependencies: some.package\n" + + "Build-Jdk: " + extractJdkVersion(getModule("project")) + "\n" + + "Built-By: " + System.getProperty("user.name") + "\n" + + "Created-By: IntelliJ IDEA"); + } + + public void testManifestSections() throws Exception { + importProject("test" + + "project" + + "1" + + + "" + + " " + + " " + + " maven-jar-plugin" + + " 2.4" + + " " + + " " + + " " + + " " + + " org/test/Some.class" + + " " + + " true" + + " " + + " " + + " " + + " org/test/SomeOther.class" + + " " + + " true" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""); + + compileModules("project"); + + assertSameLinesWithFile(getProjectPath() + "/target/MANIFEST.MF", + "Manifest-Version: 1.0\n" + + "Build-Jdk: " + extractJdkVersion(getModule("project")) + "\n" + + "Built-By: " + System.getProperty("user.name") + "\n" + + "Created-By: IntelliJ IDEA\n" + + "\n" + + "Name: org/test/SomeOther.class\n" + + "Java-Bean: true\n" + + "\n" + + "Name: org/test/Some.class\n" + + "Java-Bean: true"); + } + + @Nullable + public static String extractJdkVersion(@NotNull Module module) { + String jdkVersion = null; + Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); + if (sdk != null && (jdkVersion = sdk.getVersionString()) != null) { + final int quoteIndex = jdkVersion.indexOf('"'); + if (quoteIndex != -1) { + jdkVersion = jdkVersion.substring(quoteIndex + 1, jdkVersion.length() - 1); + } + } + + return jdkVersion; + } +}