'prepare plugin for deployment' action creates separate jars for modules included...
authornik <Nikolay.Chashnikov@jetbrains.com>
Thu, 12 Sep 2013 12:48:45 +0000 (16:48 +0400)
committernik <Nikolay.Chashnikov@jetbrains.com>
Thu, 12 Sep 2013 12:57:21 +0000 (16:57 +0400)
java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java
platform/testFramework/src/com/intellij/util/io/TestFileSystemItem.java
plugins/devkit/src/build/PrepareToDeployAction.java
plugins/devkit/testData/build/withJpsModule/META-INF/plugin.xml [new file with mode: 0644]
plugins/devkit/testData/build/withJpsModule/jps-plugin/jps-plugin.iml [new file with mode: 0644]
plugins/devkit/testData/build/withJpsModule/jps-plugin/src/Builder.java [new file with mode: 0644]
plugins/devkit/testData/build/withJpsModule/pluginProject.iml [new file with mode: 0644]
plugins/devkit/testData/build/withJpsModule/src/xxx/MyAction.java [new file with mode: 0644]
plugins/devkit/testSources/build/PluginModuleCompilationTest.java

index e91b5ff5ec1da7e957d6cd4a3d4ea6a440fd6a26..f6f140049f2131434b78e7cc5997b82d85fab4ba 100644 (file)
@@ -25,7 +25,6 @@ import com.intellij.openapi.module.StdModuleTypes;
 import com.intellij.openapi.module.impl.ModuleImpl;
 import com.intellij.openapi.project.impl.ProjectImpl;
 import com.intellij.openapi.roots.ModuleRootManager;
-import com.intellij.openapi.roots.impl.ModuleRootManagerImpl;
 import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.io.FileUtil;
@@ -139,13 +138,16 @@ public abstract class ModuleTestCase extends IdeaTestCase {
   }
 
   protected void readJdomExternalizables(final ModuleImpl module) {
+    loadModuleComponentState(module, ModuleRootManager.getInstance(module));
+  }
+
+  protected final void loadModuleComponentState(final Module module, final Object component) {
     ApplicationManager.getApplication().runWriteAction(new Runnable() {
       @Override
       public void run() {
         final ProjectImpl project = (ProjectImpl)myProject;
         project.setOptimiseTestLoadSpeed(false);
-        final ModuleRootManagerImpl moduleRootManager = (ModuleRootManagerImpl)ModuleRootManager.getInstance(module);
-        module.getStateStore().initComponent(moduleRootManager, false);
+        ((ModuleImpl)module).getStateStore().initComponent(component, false);
         project.setOptimiseTestLoadSpeed(true);
       }
     });
index a41984aeb1cb7f00b7077d617d8e5eb98f477f44..cd68c079536dc40be8dc44d1e59cad4af07832d0 100644 (file)
@@ -41,6 +41,11 @@ public class TestFileSystemItem {
     assertDirectoryEqual(file, "/");
   }
 
+  public void assertFileEqual(File file) {
+    TestFileSystemItem fileItem = myChildren.values().iterator().next();
+    fileItem.assertFileEqual(file, "/");
+  }
+
   private void assertDirectoryEqual(File file, String relativePath) {
     final File[] actualChildren = file.listFiles();
     Set<String> notFound = new HashSet<String>(myChildren.keySet());
@@ -60,7 +65,7 @@ public class TestFileSystemItem {
     try {
       Assert.assertEquals("in " + relativePath, myName, file.getName());
       if (myArchive) {
-        final File dirForExtracted = FileUtil.createTempDirectory("extracted_archive", null,false);
+        final File dirForExtracted = FileUtil.createTempDirectory("extracted_archive", null, false);
         ZipUtil.extract(file, dirForExtracted, null);
         assertDirectoryEqual(dirForExtracted, relativePath);
         FileUtil.delete(dirForExtracted);
index 4a450c0f0ad8b95b815c4d4dc5ff5137ccd09422..3fc828e1565ae727351d3b77d6b1d3085c04d857 100644 (file)
@@ -15,6 +15,7 @@
  */
 package org.jetbrains.idea.devkit.build;
 
+import com.intellij.compiler.server.CompileServerPlugin;
 import com.intellij.openapi.actionSystem.AnAction;
 import com.intellij.openapi.actionSystem.AnActionEvent;
 import com.intellij.openapi.actionSystem.LangDataKeys;
@@ -26,6 +27,7 @@ import com.intellij.openapi.compiler.CompilerManager;
 import com.intellij.openapi.compiler.make.ManifestBuilder;
 import com.intellij.openapi.fileTypes.FileTypeManager;
 import com.intellij.openapi.module.Module;
+import com.intellij.openapi.module.ModuleManager;
 import com.intellij.openapi.module.ModuleType;
 import com.intellij.openapi.progress.ProgressIndicator;
 import com.intellij.openapi.progress.ProgressManager;
@@ -36,14 +38,20 @@ import com.intellij.openapi.roots.libraries.Library;
 import com.intellij.openapi.ui.DialogWrapper;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.io.FileUtil;
-import com.intellij.openapi.vfs.JarFileSystem;
-import com.intellij.openapi.vfs.ReadonlyStatusHandler;
-import com.intellij.openapi.vfs.VfsUtil;
-import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.openapi.vfs.*;
+import com.intellij.psi.xml.XmlFile;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.util.PathUtil;
 import com.intellij.util.io.ZipUtil;
+import com.intellij.util.xml.DomFileElement;
+import com.intellij.util.xml.DomManager;
 import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jetbrains.idea.devkit.DevKitBundle;
+import org.jetbrains.idea.devkit.dom.Extensions;
+import org.jetbrains.idea.devkit.dom.IdeaPlugin;
 import org.jetbrains.idea.devkit.module.PluginModuleType;
 
 import java.io.*;
@@ -66,8 +74,6 @@ public class PrepareToDeployAction extends AnAction {
   @NonNls private static final String TEMP_PREFIX = "temp";
   @NonNls private static final String MIDDLE_LIB_DIR = "lib";
 
-  private final FileTypeManager myFileTypeManager = FileTypeManager.getInstance();
-
   public void actionPerformed(final AnActionEvent e) {
     final Module module = LangDataKeys.MODULE.getData(e.getDataContext());
     if (module != null && ModuleType.get(module) instanceof PluginModuleType) {
@@ -117,17 +123,21 @@ public class PrepareToDeployAction extends AnAction {
                          });
   }
 
-  private boolean doPrepare(final Module module, final List<String> errorMessages, final List<String> successMessages) {
+  public static boolean doPrepare(final Module module, final List<String> errorMessages, final List<String> successMessages) {
     final String pluginName = module.getName();
     final String defaultPath = new File(module.getModuleFilePath()).getParent() + File.separator + pluginName;
     final HashSet<Module> modules = new HashSet<Module>();
     PluginBuildUtil.getDependencies(module, modules);
     modules.add(module);
     final Set<Library> libs = new HashSet<Library>();
-    for (Module module1 : modules) {
-      PluginBuildUtil.getLibraries(module1, libs);
+    for (Module dep : modules) {
+      PluginBuildUtil.getLibraries(dep, libs);
     }
-    final boolean isZip = libs.size() != 0;
+
+    final Map<Module, String> jpsModules = collectJpsPluginModules(module);
+    modules.removeAll(jpsModules.keySet());
+
+    final boolean isZip = !libs.isEmpty() || !jpsModules.isEmpty();
     final String oldPath = defaultPath + (isZip ? JAR_EXTENSION : ZIP_EXTENSION);
     final File oldFile = new File(oldPath);
     if (oldFile.exists()) {
@@ -151,7 +161,7 @@ public class PrepareToDeployAction extends AnAction {
         try {
           File jarFile = preparePluginsJar(module, modules);
           if (isZip) {
-            processLibraries(jarFile, dstFile, pluginName, libs, progressIndicator);
+            processLibrariesAndJpsPlugins(jarFile, dstFile, pluginName, libs, jpsModules, progressIndicator);
           }
           else {
             FileUtil.copy(jarFile, dstFile);
@@ -166,6 +176,40 @@ public class PrepareToDeployAction extends AnAction {
 
   }
 
+  @NotNull
+  private static Map<Module, String> collectJpsPluginModules(@NotNull Module module) {
+    XmlFile pluginXml = PluginModuleType.getPluginXml(module);
+    if (pluginXml == null) return Collections.emptyMap();
+
+    DomFileElement<IdeaPlugin> fileElement = DomManager.getDomManager(module.getProject()).getFileElement(pluginXml, IdeaPlugin.class);
+    if (fileElement == null) return Collections.emptyMap();
+
+    Map<Module, String> jpsPluginToOutputPath = new HashMap<Module, String>();
+    IdeaPlugin plugin = fileElement.getRootElement();
+    List<Extensions> extensions = plugin.getExtensions();
+    for (Extensions extensionGroup : extensions) {
+      XmlTag extensionsTag = extensionGroup.getXmlTag();
+      String defaultExtensionNs = extensionsTag.getAttributeValue("defaultExtensionNs");
+      for (XmlTag tag : extensionsTag.getSubTags()) {
+        String name = tag.getLocalName();
+        String qualifiedName = defaultExtensionNs != null ? defaultExtensionNs + "." + name : name;
+        if (CompileServerPlugin.EP_NAME.getName().equals(qualifiedName)) {
+          String classpath = tag.getAttributeValue("classpath");
+          if (classpath != null) {
+            for (String path : StringUtil.split(classpath, ";")) {
+              String moduleName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(path));
+              Module jpsModule = ModuleManager.getInstance(module.getProject()).findModuleByName(moduleName);
+              if (jpsModule != null) {
+                jpsPluginToOutputPath.put(jpsModule, path);
+              }
+            }
+          }
+        }
+      }
+    }
+    return jpsPluginToOutputPath;
+  }
+
   private static boolean clearReadOnly(final Project project, final File dstFile) {
     //noinspection EmptyCatchBlock
     final URL url;
@@ -190,11 +234,9 @@ public class PrepareToDeployAction extends AnAction {
     };
   }
 
-  private void processLibraries(final File jarFile,
-                                final File zipFile,
-                                final String pluginName,
-                                final Set<Library> libs,
-                                final ProgressIndicator progressIndicator) throws IOException {
+  private static void processLibrariesAndJpsPlugins(final File jarFile, final File zipFile, final String pluginName,
+                                                    final Set<Library> libs,
+                                                    Map<Module, String> jpsModules, final ProgressIndicator progressIndicator) throws IOException {
     if (FileUtil.ensureCanCreateFile(zipFile)) {
       ZipOutputStream zos = null;
       try {
@@ -203,7 +245,11 @@ public class PrepareToDeployAction extends AnAction {
         addStructure(pluginName + "/" + MIDDLE_LIB_DIR, zos);
         final String entryName = pluginName + JAR_EXTENSION;
         ZipUtil.addFileToZip(zos, jarFile, getZipPath(pluginName, entryName), new HashSet<String>(),
-                             createFilter(progressIndicator, myFileTypeManager));
+                             createFilter(progressIndicator, FileTypeManager.getInstance()));
+        for (Map.Entry<Module, String> entry : jpsModules.entrySet()) {
+          File jpsPluginJar = jarModulesOutput(Collections.singleton(entry.getKey()), null, null);
+          ZipUtil.addFileToZip(zos, jpsPluginJar, getZipPath(pluginName, entry.getValue()), null, null);
+        }
         Set<String> usedJarNames = new HashSet<String>();
         usedJarNames.add(entryName);
         Set<VirtualFile> jarredVirtualFiles = new HashSet<VirtualFile>();
@@ -231,20 +277,20 @@ public class PrepareToDeployAction extends AnAction {
     return "/" + pluginName + "/" + MIDDLE_LIB_DIR + "/" + entryName;
   }
 
-  private void makeAndAddLibraryJar(final VirtualFile virtualFile,
-                                    final File zipFile,
-                                    final String pluginName,
-                                    final ZipOutputStream zos,
-                                    final Set<String> usedJarNames,
-                                    final ProgressIndicator progressIndicator,
-                                    final String preferredName) throws IOException {
+  private static void makeAndAddLibraryJar(final VirtualFile virtualFile,
+                                           final File zipFile,
+                                           final String pluginName,
+                                           final ZipOutputStream zos,
+                                           final Set<String> usedJarNames,
+                                           final ProgressIndicator progressIndicator,
+                                           final String preferredName) throws IOException {
     File libraryJar = FileUtil.createTempFile(TEMP_PREFIX, JAR_EXTENSION);
     libraryJar.deleteOnExit();
     ZipOutputStream jar = null;
     try {
       jar = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(libraryJar)));
-      ZipUtil.addFileOrDirRecursively(jar, libraryJar, VfsUtil.virtualToIoFile(virtualFile), "",
-                                      createFilter(progressIndicator, myFileTypeManager), null);
+      ZipUtil.addFileOrDirRecursively(jar, libraryJar, VfsUtilCore.virtualToIoFile(virtualFile), "",
+                                      createFilter(progressIndicator, FileTypeManager.getInstance()), null);
     }
     finally {
       if (jar != null) jar.close();
@@ -298,25 +344,32 @@ public class PrepareToDeployAction extends AnAction {
     zos.closeEntry();
   }
 
-  private File preparePluginsJar(Module module, final HashSet<Module> modules) throws IOException {
+  private static File preparePluginsJar(Module module, final HashSet<Module> modules) throws IOException {
     final PluginBuildConfiguration pluginModuleBuildProperties = PluginBuildConfiguration.getInstance(module);
+    final Manifest manifest = createOrFindManifest(pluginModuleBuildProperties);
+
+    return jarModulesOutput(modules, manifest, pluginModuleBuildProperties.getPluginXmlPath());
+  }
+
+  private static File jarModulesOutput(@NotNull Set<Module> modules, @Nullable Manifest manifest, final @Nullable String pluginXmlPath) throws IOException {
     File jarFile = FileUtil.createTempFile(TEMP_PREFIX, JAR_EXTENSION);
     jarFile.deleteOnExit();
-    final Manifest manifest = createOrFindManifest(pluginModuleBuildProperties);
     ZipOutputStream jarPlugin = null;
     try {
-      jarPlugin = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)), manifest);
+      BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(jarFile));
+      jarPlugin = manifest != null ? new JarOutputStream(out, manifest) : new JarOutputStream(out);
       final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
-      final HashSet<String> writtenItemRelativePaths = new HashSet<String>();
-      for (Module module1 : modules) {
-        final VirtualFile compilerOutputPath = CompilerModuleExtension.getInstance(module1).getCompilerOutputPath();
+      final Set<String> writtenItemRelativePaths = new HashSet<String>();
+      for (Module module : modules) {
+        final VirtualFile compilerOutputPath = CompilerModuleExtension.getInstance(module).getCompilerOutputPath();
         if (compilerOutputPath == null) continue; //pre-condition: output dirs for all modules are up-to-date
         ZipUtil.addDirToZipRecursively(jarPlugin, jarFile, new File(compilerOutputPath.getPath()), "",
-                                       createFilter(progressIndicator, myFileTypeManager), writtenItemRelativePaths);
+                                       createFilter(progressIndicator, FileTypeManager.getInstance()), writtenItemRelativePaths);
+      }
+      if (pluginXmlPath != null) {
+        ZipUtil.addFileToZip(jarPlugin, new File(pluginXmlPath), "/META-INF/plugin.xml", writtenItemRelativePaths,
+                             createFilter(progressIndicator, null));
       }
-      final String pluginXmlPath = pluginModuleBuildProperties.getPluginXmlPath();
-      @NonNls final String metaInf = "/META-INF/plugin.xml";
-      ZipUtil.addFileToZip(jarPlugin, new File(pluginXmlPath), metaInf, writtenItemRelativePaths, createFilter(progressIndicator, null));
     }
     finally {
       if (jarPlugin != null) jarPlugin.close();
diff --git a/plugins/devkit/testData/build/withJpsModule/META-INF/plugin.xml b/plugins/devkit/testData/build/withJpsModule/META-INF/plugin.xml
new file mode 100644 (file)
index 0000000..0def625
--- /dev/null
@@ -0,0 +1,8 @@
+<idea-plugin version="2">
+  <id>test.plugin</id>
+  <name>Test Plugin</name>
+  <version>1.0</version>
+  <extensions defaultExtensionNs="com.intellij">
+    <compileServer.plugin classpath="jps/jps-plugin.jar"/>
+  </extensions>
+</idea-plugin>
\ No newline at end of file
diff --git a/plugins/devkit/testData/build/withJpsModule/jps-plugin/jps-plugin.iml b/plugins/devkit/testData/build/withJpsModule/jps-plugin/jps-plugin.iml
new file mode 100644 (file)
index 0000000..b88b420
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="jdk" jdkName="1.7" jdkType="JavaSDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
+
diff --git a/plugins/devkit/testData/build/withJpsModule/jps-plugin/src/Builder.java b/plugins/devkit/testData/build/withJpsModule/jps-plugin/src/Builder.java
new file mode 100644 (file)
index 0000000..c85e8e7
--- /dev/null
@@ -0,0 +1 @@
+public class Builder {}
\ No newline at end of file
diff --git a/plugins/devkit/testData/build/withJpsModule/pluginProject.iml b/plugins/devkit/testData/build/withJpsModule/pluginProject.iml
new file mode 100644 (file)
index 0000000..08d9950
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PLUGIN_MODULE" version="4">
+  <component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/META-INF/plugin.xml" />
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+    </content>
+    <orderEntry type="jdk" jdkName="IDEA plugin SDK" jdkType="IDEA JDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="jps-plugin" />
+  </component>
+</module>
+
diff --git a/plugins/devkit/testData/build/withJpsModule/src/xxx/MyAction.java b/plugins/devkit/testData/build/withJpsModule/src/xxx/MyAction.java
new file mode 100644 (file)
index 0000000..997befe
--- /dev/null
@@ -0,0 +1,9 @@
+package xxx;
+
+import com.intellij.openapi.util.io.FileUtilRt ;
+
+public class MyAction {
+  public void actionPerformed() {
+    FileUtilRt.toSystemDependentName("myPath");
+  }
+}
index 6053792ddd78b4698499f021d20c3fb11518c732..4a46d708e60afe5c848cfe8a498dcd8c1453ce25 100644 (file)
@@ -25,6 +25,7 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable;
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.projectRoots.SdkModificator;
 import com.intellij.openapi.projectRoots.SdkType;
+import com.intellij.openapi.roots.ModuleRootModificationUtil;
 import com.intellij.openapi.roots.OrderRootType;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.io.FileUtilRt;
@@ -33,7 +34,9 @@ import org.jetbrains.idea.devkit.projectRoots.IdeaJdk;
 import org.jetbrains.idea.devkit.projectRoots.Sandbox;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 import static com.intellij.util.io.TestFileSystemBuilder.fs;
 
@@ -80,8 +83,8 @@ public class PluginModuleCompilationTest extends BaseCompilerTestCase {
     super.tearDown();
   }
 
-  public void testMakeModule() {
-    Module module = setupPluginProject();
+  public void testMakeSimpleModule() {
+    Module module = setupSimplePluginProject();
     make(module);
     assertOutput(module, fs().dir("xxx").file("MyAction.class"));
 
@@ -95,16 +98,69 @@ public class PluginModuleCompilationTest extends BaseCompilerTestCase {
     .build().assertDirectoryEqual(sandbox);
   }
 
-  public void testRebuild() {
-    setupPluginProject();
+  public void testRebuildSimpleProject() {
+    setupSimplePluginProject();
     CompilationLog log = rebuild();
     assertTrue("Rebuild finished with warnings: " + Arrays.toString(log.getWarnings()), log.getWarnings().length == 0);
   }
 
-  private Module setupPluginProject() {
+  public void testPrepareSimpleProjectForDeployment() {
+    Module module = setupSimplePluginProject();
+    rebuild();
+    prepareForDeployment(module);
+
+    File outputFile = new File(getProjectBasePath() + "/pluginProject.jar");
+    assertTrue(outputFile + " not found", outputFile.exists());
+    fs()
+      .archive("pluginProject.jar")
+         .dir("META-INF").file("plugin.xml").file("MANIFEST.MF").end()
+         .dir("xxx").file("MyAction.class")
+    .build().assertFileEqual(outputFile);
+  }
+
+  public void testBuildProjectWithJpsModule() {
+    Module module = setupPluginProjectWithJpsModule();
+    rebuild();
+    prepareForDeployment(module);
+
+    File outputFile = new File(getProjectBasePath() + "/pluginProject.zip");
+    assertTrue(outputFile + " not found", outputFile.exists());
+    fs()
+      .archive("pluginProject.zip")
+        .dir("pluginProject")
+         .dir("lib")
+            .archive("pluginProject.jar")
+               .dir("META-INF").file("plugin.xml").file("MANIFEST.MF").end()
+               .dir("xxx").file("MyAction.class").end()
+               .end()
+            .dir("jps")
+               .archive("jps-plugin.jar")
+                  .file("Builder.class")
+
+    .build().assertFileEqual(outputFile);
+  }
+
+  private static void prepareForDeployment(Module module) {
+    List<String> errorMessages = new ArrayList<String>();
+    PrepareToDeployAction.doPrepare(module, errorMessages, new ArrayList<String>());
+    assertTrue("Building plugin zip finished with errors: " + errorMessages, errorMessages.isEmpty());
+  }
+
+  private Module setupSimplePluginProject() {
     copyToProject("plugins/devkit/testData/build/simple");
     Module module = loadModule(getProjectBasePath() + "/pluginProject.iml");
     readJdomExternalizables((ModuleImpl)module);
     return module;
   }
+
+  private Module setupPluginProjectWithJpsModule() {
+    copyToProject("plugins/devkit/testData/build/withJpsModule");
+    Module module = loadModule(getProjectBasePath() + "/pluginProject.iml");
+    readJdomExternalizables((ModuleImpl)module);
+    loadModuleComponentState(module, PluginBuildConfiguration.getInstance(module));
+    Module jpsModule = loadModule(getProjectBasePath() + "/jps-plugin/jps-plugin.iml");
+    readJdomExternalizables((ModuleImpl)jpsModule);
+    ModuleRootModificationUtil.setModuleSdk(jpsModule, getTestProjectJdk());
+    return module;
+  }
 }