'prepare plugin for deployment' action creates separate jars for modules included into classpath of external build

This commit is contained in:
nik
2013-09-12 16:57:21 +04:00
parent 88a2716e2a
commit c2ea835a9a
9 changed files with 205 additions and 45 deletions
@@ -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);
}
});
@@ -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);
@@ -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();
@@ -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>
@@ -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>
@@ -0,0 +1 @@
public class Builder {}
@@ -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>
@@ -0,0 +1,9 @@
package xxx;
import com.intellij.openapi.util.io.FileUtilRt ;
public class MyAction {
public void actionPerformed() {
FileUtilRt.toSystemDependentName("myPath");
}
}
@@ -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;
}
}