diff --git a/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java b/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java index e91b5ff5ec1d..f6f140049f21 100644 --- a/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java +++ b/java/testFramework/src/com/intellij/testFramework/ModuleTestCase.java @@ -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); } }); diff --git a/platform/testFramework/src/com/intellij/util/io/TestFileSystemItem.java b/platform/testFramework/src/com/intellij/util/io/TestFileSystemItem.java index a41984aeb1cb..cd68c079536d 100644 --- a/platform/testFramework/src/com/intellij/util/io/TestFileSystemItem.java +++ b/platform/testFramework/src/com/intellij/util/io/TestFileSystemItem.java @@ -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 notFound = new HashSet(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); diff --git a/plugins/devkit/src/build/PrepareToDeployAction.java b/plugins/devkit/src/build/PrepareToDeployAction.java index 4a450c0f0ad8..3fc828e1565a 100644 --- a/plugins/devkit/src/build/PrepareToDeployAction.java +++ b/plugins/devkit/src/build/PrepareToDeployAction.java @@ -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 errorMessages, final List successMessages) { + public static boolean doPrepare(final Module module, final List errorMessages, final List successMessages) { final String pluginName = module.getName(); final String defaultPath = new File(module.getModuleFilePath()).getParent() + File.separator + pluginName; final HashSet modules = new HashSet(); PluginBuildUtil.getDependencies(module, modules); modules.add(module); final Set libs = new HashSet(); - for (Module module1 : modules) { - PluginBuildUtil.getLibraries(module1, libs); + for (Module dep : modules) { + PluginBuildUtil.getLibraries(dep, libs); } - final boolean isZip = libs.size() != 0; + + final Map 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 collectJpsPluginModules(@NotNull Module module) { + XmlFile pluginXml = PluginModuleType.getPluginXml(module); + if (pluginXml == null) return Collections.emptyMap(); + + DomFileElement fileElement = DomManager.getDomManager(module.getProject()).getFileElement(pluginXml, IdeaPlugin.class); + if (fileElement == null) return Collections.emptyMap(); + + Map jpsPluginToOutputPath = new HashMap(); + IdeaPlugin plugin = fileElement.getRootElement(); + List 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 libs, - final ProgressIndicator progressIndicator) throws IOException { + private static void processLibrariesAndJpsPlugins(final File jarFile, final File zipFile, final String pluginName, + final Set libs, + Map 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(), - createFilter(progressIndicator, myFileTypeManager)); + createFilter(progressIndicator, FileTypeManager.getInstance())); + for (Map.Entry entry : jpsModules.entrySet()) { + File jpsPluginJar = jarModulesOutput(Collections.singleton(entry.getKey()), null, null); + ZipUtil.addFileToZip(zos, jpsPluginJar, getZipPath(pluginName, entry.getValue()), null, null); + } Set usedJarNames = new HashSet(); usedJarNames.add(entryName); Set jarredVirtualFiles = new HashSet(); @@ -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 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 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 modules) throws IOException { + private static File preparePluginsJar(Module module, final HashSet 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 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 writtenItemRelativePaths = new HashSet(); - for (Module module1 : modules) { - final VirtualFile compilerOutputPath = CompilerModuleExtension.getInstance(module1).getCompilerOutputPath(); + final Set writtenItemRelativePaths = new HashSet(); + 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 index 000000000000..0def625fa1c2 --- /dev/null +++ b/plugins/devkit/testData/build/withJpsModule/META-INF/plugin.xml @@ -0,0 +1,8 @@ + + test.plugin + Test Plugin + 1.0 + + + + \ 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 index 000000000000..b88b4205c3de --- /dev/null +++ b/plugins/devkit/testData/build/withJpsModule/jps-plugin/jps-plugin.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + 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 index 000000000000..c85e8e7e2e13 --- /dev/null +++ b/plugins/devkit/testData/build/withJpsModule/jps-plugin/src/Builder.java @@ -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 index 000000000000..08d9950c09ba --- /dev/null +++ b/plugins/devkit/testData/build/withJpsModule/pluginProject.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + 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 index 000000000000..997befe3f867 --- /dev/null +++ b/plugins/devkit/testData/build/withJpsModule/src/xxx/MyAction.java @@ -0,0 +1,9 @@ +package xxx; + +import com.intellij.openapi.util.io.FileUtilRt ; + +public class MyAction { + public void actionPerformed() { + FileUtilRt.toSystemDependentName("myPath"); + } +} diff --git a/plugins/devkit/testSources/build/PluginModuleCompilationTest.java b/plugins/devkit/testSources/build/PluginModuleCompilationTest.java index 6053792ddd78..4a46d708e60a 100644 --- a/plugins/devkit/testSources/build/PluginModuleCompilationTest.java +++ b/plugins/devkit/testSources/build/PluginModuleCompilationTest.java @@ -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 errorMessages = new ArrayList(); + PrepareToDeployAction.doPrepare(module, errorMessages, new ArrayList()); + 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; + } }