diff --git a/jps/.idea/artifacts/jps_appLauncher.xml b/jps/.idea/artifacts/jps_appLauncher.xml new file mode 100644 index 000000000000..9757aa31580c --- /dev/null +++ b/jps/.idea/artifacts/jps_appLauncher.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/out/artifacts + + + + + \ No newline at end of file diff --git a/jps/.idea/artifacts/jps_scala.xml b/jps/.idea/artifacts/jps_scala.xml new file mode 100644 index 000000000000..c29aa330f344 --- /dev/null +++ b/jps/.idea/artifacts/jps_scala.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/out/artifacts + + + + + \ No newline at end of file diff --git a/jps/.idea/artifacts/jps_sources.xml b/jps/.idea/artifacts/jps_sources.xml index 1f4a64488f27..744955d2cde3 100644 --- a/jps/.idea/artifacts/jps_sources.xml +++ b/jps/.idea/artifacts/jps_sources.xml @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/jps/.idea/inspectionProfiles/profiles_settings.xml b/jps/.idea/inspectionProfiles/profiles_settings.xml index 24c0574ce220..cf204912324d 100644 --- a/jps/.idea/inspectionProfiles/profiles_settings.xml +++ b/jps/.idea/inspectionProfiles/profiles_settings.xml @@ -1,5 +1,8 @@ + diff --git a/jps/plugins/gwt/src/org/jetbrains/jps/gwt/GwtModuleUtil.groovy b/jps/plugins/gwt/src/org/jetbrains/jps/gwt/GwtModuleUtil.groovy new file mode 100644 index 000000000000..cd744b880ac6 --- /dev/null +++ b/jps/plugins/gwt/src/org/jetbrains/jps/gwt/GwtModuleUtil.groovy @@ -0,0 +1,21 @@ +package org.jetbrains.jps.gwt + +import org.xml.sax.SAXParseException; + +/** + * @author nik + */ +class GwtModuleUtil { + public static boolean hasEntryPoints(File child) { + try { + def root = new XmlParser(false, false).parse(child) + return !root."entry-point".isEmpty() + } + catch (IOException e) { + return true; + } + catch (SAXParseException e) { + return true; + } + } +} diff --git a/jps/plugins/gwt/src/org/jetbrains/jps/gwt/GwtModulesSearcher.java b/jps/plugins/gwt/src/org/jetbrains/jps/gwt/GwtModulesSearcher.java index b3dfe01f84bf..b1e7658e06df 100644 --- a/jps/plugins/gwt/src/org/jetbrains/jps/gwt/GwtModulesSearcher.java +++ b/jps/plugins/gwt/src/org/jetbrains/jps/gwt/GwtModulesSearcher.java @@ -1,5 +1,7 @@ package org.jetbrains.jps.gwt; +import groovy.util.XmlParser; + import java.io.File; import java.util.ArrayList; import java.util.List; @@ -28,7 +30,9 @@ public class GwtModulesSearcher { for (File child : files) { final String name = child.getName(); if (child.isFile() && name.endsWith(GWT_XML_SUFFIX)) { - result.add(packageName + name.substring(0, name.length() - GWT_XML_SUFFIX.length())); + if (GwtModuleUtil.hasEntryPoints(child)) { + result.add(packageName + name.substring(0, name.length() - GWT_XML_SUFFIX.length())); + } } else { collectGwtModules(child, packageName + name + ".", result); diff --git a/jps/plugins/gwt/testData/modules/com/app/App.gwt.xml b/jps/plugins/gwt/testData/modules/com/app/App.gwt.xml new file mode 100644 index 000000000000..d31f3637779f --- /dev/null +++ b/jps/plugins/gwt/testData/modules/com/app/App.gwt.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/jps/plugins/gwt/testData/modules/com/app/InvalidXml.gwt.xml b/jps/plugins/gwt/testData/modules/com/app/InvalidXml.gwt.xml new file mode 100644 index 000000000000..4e87e20e57f4 --- /dev/null +++ b/jps/plugins/gwt/testData/modules/com/app/InvalidXml.gwt.xml @@ -0,0 +1,5 @@ + + + + diff --git a/jps/plugins/gwt/testData/modules/com/app/NoEntryPoints.gwt.xml b/jps/plugins/gwt/testData/modules/com/app/NoEntryPoints.gwt.xml new file mode 100644 index 000000000000..86a82eb089d4 --- /dev/null +++ b/jps/plugins/gwt/testData/modules/com/app/NoEntryPoints.gwt.xml @@ -0,0 +1,5 @@ + + + + diff --git a/jps/plugins/gwt/testSrc/org/jetbrains/jps/gwt/GwtModulesSearcherTest.java b/jps/plugins/gwt/testSrc/org/jetbrains/jps/gwt/GwtModulesSearcherTest.java new file mode 100644 index 000000000000..0a1d17892aba --- /dev/null +++ b/jps/plugins/gwt/testSrc/org/jetbrains/jps/gwt/GwtModulesSearcherTest.java @@ -0,0 +1,19 @@ +package org.jetbrains.jps.gwt; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author nik + */ +public class GwtModulesSearcherTest extends TestCase { + public void test() { + List modules = GwtModulesSearcher.findGwtModules(Arrays.asList("plugins/gwt/testData/modules")); + assertEquals(new HashSet(Arrays.asList("com.app.App", "com.app.InvalidXml")), + new HashSet(modules)); + } +} diff --git a/jps/plugins/scala/scala.iml b/jps/plugins/scala/scala.iml new file mode 100644 index 000000000000..9d8bc05610ac --- /dev/null +++ b/jps/plugins/scala/scala.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/jps/plugins/scala/src/META-INF/services/org.jetbrains.jps.builders.ModuleBuilderService b/jps/plugins/scala/src/META-INF/services/org.jetbrains.jps.builders.ModuleBuilderService new file mode 100644 index 000000000000..cdb9aab312f6 --- /dev/null +++ b/jps/plugins/scala/src/META-INF/services/org.jetbrains.jps.builders.ModuleBuilderService @@ -0,0 +1 @@ +org.jetbrains.jps.scala.ScalaModuleBuilderService \ No newline at end of file diff --git a/jps/plugins/scala/src/META-INF/services/org.jetbrains.jps.idea.FacetTypeService b/jps/plugins/scala/src/META-INF/services/org.jetbrains.jps.idea.FacetTypeService new file mode 100644 index 000000000000..0334cbd8725b --- /dev/null +++ b/jps/plugins/scala/src/META-INF/services/org.jetbrains.jps.idea.FacetTypeService @@ -0,0 +1 @@ +org.jetbrains.jps.scala.ScalaFacetType \ No newline at end of file diff --git a/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaFacet.groovy b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaFacet.groovy new file mode 100644 index 000000000000..cf9107da65db --- /dev/null +++ b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaFacet.groovy @@ -0,0 +1,7 @@ +package org.jetbrains.jps.scala + +import org.jetbrains.jps.idea.Facet + +class ScalaFacet extends Facet { + String compilerLibraryName +} diff --git a/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaFacetType.groovy b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaFacetType.groovy new file mode 100644 index 000000000000..0bff391cfd1d --- /dev/null +++ b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaFacetType.groovy @@ -0,0 +1,27 @@ +package org.jetbrains.jps.scala + +import org.jetbrains.jps.idea.FacetTypeService +import org.jetbrains.jps.idea.Facet +import org.jetbrains.jps.Module +import org.jetbrains.jps.MacroExpander + +class ScalaFacetType extends FacetTypeService { + ScalaFacetType() { + super("scala") + } + + @Override + Facet createFacet(Module module, String name, Node facetConfiguration, MacroExpander macroExpander) { + Facet facet = new ScalaFacet(name: name) + facetConfiguration.option.each {Node child -> + String value = child."@value" + switch (child."@name") { + case "compilerLibraryName": + facet.compilerLibraryName = value + break + } + } + + return facet; + } +} diff --git a/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaModuleBuilder.groovy b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaModuleBuilder.groovy new file mode 100644 index 000000000000..3850f2ea0649 --- /dev/null +++ b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaModuleBuilder.groovy @@ -0,0 +1,54 @@ +package org.jetbrains.jps.scala + +import org.jetbrains.jps.* + +class ScalaModuleBuilder implements ModuleBuilder { + def processModule(ModuleBuildState state, ModuleChunk moduleChunk, Project project) { + List facets = [] + moduleChunk.modules.each { + it.facets.values().each { + if (it instanceof ScalaFacet) { + facets << it + } + } + } + + if (facets.isEmpty()) return + + facets.each {ScalaFacet facet -> + compileScalaFacet(facet, project, state) + } + } + + def compileScalaFacet(ScalaFacet facet, Project project, ModuleBuildState state) { + Library scalaCompiler = project.libraries[facet.compilerLibraryName] + if (scalaCompiler == null) { + project.error("Cannot find Scala compiler project library with name: ${facet.compilerLibraryName}"); + return + } + + def ant = project.binding.ant + + if (!ant.hasProperty("scalac")) { + ant.taskdef(name: "scalac", classname: "scala.tools.ant.Scalac") { + classpath { + scalaCompiler.classpath.each { + pathelement(location: it) + } + } + } + } + + ant.scalac(destdir: state.targetFolder) { + state.sourceRoots.each { + src(path: it) + } + + classpath { + state.classpath.each { + pathelement(location: it) + } + } + } + } +} diff --git a/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaModuleBuilderService.groovy b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaModuleBuilderService.groovy new file mode 100644 index 000000000000..6632f7511038 --- /dev/null +++ b/jps/plugins/scala/src/org/jetbrains/jps/scala/ScalaModuleBuilderService.groovy @@ -0,0 +1,11 @@ +package org.jetbrains.jps.scala + +import org.jetbrains.jps.builders.ModuleBuilderService +import org.jetbrains.jps.ProjectBuilder + +class ScalaModuleBuilderService extends ModuleBuilderService { + @Override + registerBuilders(ProjectBuilder builder) { + builder.sourceModifyingBuilders << new ScalaModuleBuilder(); + } +} diff --git a/jps/plugins/scala/testData/scala-test/.idea/.name b/jps/plugins/scala/testData/scala-test/.idea/.name new file mode 100644 index 000000000000..c3c60d83f47e --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/.name @@ -0,0 +1 @@ +scala-test \ No newline at end of file diff --git a/jps/plugins/scala/testData/scala-test/.idea/ant.xml b/jps/plugins/scala/testData/scala-test/.idea/ant.xml new file mode 100644 index 000000000000..2581ca3fe84c --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/ant.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/.idea/compiler.xml b/jps/plugins/scala/testData/scala-test/.idea/compiler.xml new file mode 100644 index 000000000000..a1b41c52c724 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/compiler.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/.idea/encodings.xml b/jps/plugins/scala/testData/scala-test/.idea/encodings.xml new file mode 100644 index 000000000000..e206d70d8595 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/jps/plugins/scala/testData/scala-test/.idea/highlighting.xml b/jps/plugins/scala/testData/scala-test/.idea/highlighting.xml new file mode 100644 index 000000000000..f33b64d94953 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/highlighting.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/.idea/libraries/scala_compiler_2_9_1.xml b/jps/plugins/scala/testData/scala-test/.idea/libraries/scala_compiler_2_9_1.xml new file mode 100644 index 000000000000..ab9095bd6e78 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/libraries/scala_compiler_2_9_1.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/jps/plugins/scala/testData/scala-test/.idea/libraries/scala_library_2_9_1.xml b/jps/plugins/scala/testData/scala-test/.idea/libraries/scala_library_2_9_1.xml new file mode 100644 index 000000000000..4fef4474375f --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/libraries/scala_library_2_9_1.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/jps/plugins/scala/testData/scala-test/.idea/misc.xml b/jps/plugins/scala/testData/scala-test/.idea/misc.xml new file mode 100644 index 000000000000..27d0cf379957 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/misc.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + http://www.w3.org/1999/xhtml + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/.idea/modules.xml b/jps/plugins/scala/testData/scala-test/.idea/modules.xml new file mode 100644 index 000000000000..afcdcf7f94a7 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/modules.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/.idea/vcs.xml b/jps/plugins/scala/testData/scala-test/.idea/vcs.xml new file mode 100644 index 000000000000..f05d78fd07e1 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/.idea/vcs.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/lib/scala-compiler.jar b/jps/plugins/scala/testData/scala-test/lib/scala-compiler.jar new file mode 100644 index 000000000000..5e5ef82ae87c Binary files /dev/null and b/jps/plugins/scala/testData/scala-test/lib/scala-compiler.jar differ diff --git a/jps/plugins/scala/testData/scala-test/lib/scala-library.jar b/jps/plugins/scala/testData/scala-test/lib/scala-library.jar new file mode 100644 index 000000000000..d38bb5afd341 Binary files /dev/null and b/jps/plugins/scala/testData/scala-test/lib/scala-library.jar differ diff --git a/jps/plugins/scala/testData/scala-test/mod1/mod1.iml b/jps/plugins/scala/testData/scala-test/mod1/mod1.iml new file mode 100644 index 000000000000..520f4f91d19e --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/mod1/mod1.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/mod1/src/HelloWorld.scala b/jps/plugins/scala/testData/scala-test/mod1/src/HelloWorld.scala new file mode 100644 index 000000000000..6c50b5a42ab4 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/mod1/src/HelloWorld.scala @@ -0,0 +1,6 @@ + +class HelloWorld { + def main(args: Array[String]) { + println("Hello world!") + } +} \ No newline at end of file diff --git a/jps/plugins/scala/testData/scala-test/mod2/mod2.iml b/jps/plugins/scala/testData/scala-test/mod2/mod2.iml new file mode 100644 index 000000000000..520f4f91d19e --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/mod2/mod2.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/jps/plugins/scala/testData/scala-test/mod2/src/A.scala b/jps/plugins/scala/testData/scala-test/mod2/src/A.scala new file mode 100644 index 000000000000..aa371d229855 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/mod2/src/A.scala @@ -0,0 +1,6 @@ + +class A { + def fun() { + System.out.println("Hello!"); + } +} \ No newline at end of file diff --git a/jps/plugins/scala/testData/scala-test/mod2/src/B.java b/jps/plugins/scala/testData/scala-test/mod2/src/B.java new file mode 100644 index 000000000000..2167f4ba4e43 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/mod2/src/B.java @@ -0,0 +1,5 @@ +public class B { + public void doIt() { + new A().fun(); + } +} \ No newline at end of file diff --git a/jps/plugins/scala/testData/scala-test/scala-test.iml b/jps/plugins/scala/testData/scala-test/scala-test.iml new file mode 100644 index 000000000000..ef582b1af949 --- /dev/null +++ b/jps/plugins/scala/testData/scala-test/scala-test.iml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/jps/plugins/scala/testSrc/org/jetbrains/jps/scala/ScalaModuleTest.groovy b/jps/plugins/scala/testSrc/org/jetbrains/jps/scala/ScalaModuleTest.groovy new file mode 100644 index 000000000000..0d67cad50320 --- /dev/null +++ b/jps/plugins/scala/testSrc/org/jetbrains/jps/scala/ScalaModuleTest.groovy @@ -0,0 +1,37 @@ +package org.jetbrains.jps.scala + +import org.jetbrains.jps.JpsBuildTestCase +import org.jetbrains.jps.Project +import org.jetbrains.jps.idea.Facet + +class ScalaModuleTest extends JpsBuildTestCase { + public void test_load_scala_facet() { + Project project = loadProject("plugins/scala/testData/scala-test", [:]); + Map facets = project.modules["mod1"].facets; + assertEquals(1, facets.size()); + ScalaFacet facet = facets.values().iterator().next(); + assertNotNull(facet); + assertEquals("scala-compiler-2.9.1", facet.compilerLibraryName); + } + + public void test_compile_scala_module() { + def projectPath = "plugins/scala/testData/scala-test" + Project project = loadProject(projectPath, [:]); + project.clean(); + project.modules["mod1"].make(); + + def path = projectPath + "/out/production/mod1/HelloWorld.class" + assertTrue(path, new File(path).isFile()); + } + + public void test_joint_compilation() { + def projectPath = "plugins/scala/testData/scala-test" + Project project = loadProject(projectPath, [:]); + project.clean(); + project.modules["mod2"].make(); + + def path = projectPath + "/out/production/mod2/" + assertTrue(path, new File(path, "A.class").isFile()); + assertTrue(path, new File(path, "B.class").isFile()); + } +} diff --git a/jps/src/org/jetbrains/jps/Jps.groovy b/jps/src/org/jetbrains/jps/Jps.groovy index 5d551ffe4774..39225713092d 100644 --- a/jps/src/org/jetbrains/jps/Jps.groovy +++ b/jps/src/org/jetbrains/jps/Jps.groovy @@ -40,7 +40,7 @@ final class Jps { def old = binding.getVariable("module") def layoutInfo = new LayoutInfo() - ["module", "zip", "dir"].each {tag -> + ["module", "moduleTests", "zip", "dir"].each {tag -> binding.setVariable(tag, {Object[] args -> if (args.length == 1) { binding.ant."$tag"(name: args[0]) @@ -58,7 +58,22 @@ final class Jps { } binding.setVariable("jar", {Object[] args -> if (args.length == 2) { - binding.ant.jar(name: args[0], compress: project.builder.compressJars, args[1]) + def param0 = args[0] + String name; + String duplicate = null; + if (param0 instanceof Map) { + name = param0.name; + duplicate = param0.duplicate; + } + else { + name = (String)param0; + } + if (duplicate != null) { + binding.ant.jar(name: name, compress: project.builder.compressJars, duplicate: duplicate, args[1]) + } + else { + binding.ant.jar(name: name, compress: project.builder.compressJars, args[1]) + } } else { project.error("unexpected number of parameters for 'jar' task: $args.length") diff --git a/jps/src/org/jetbrains/jps/PathUtil.groovy b/jps/src/org/jetbrains/jps/PathUtil.groovy new file mode 100644 index 000000000000..eb50aa913e18 --- /dev/null +++ b/jps/src/org/jetbrains/jps/PathUtil.groovy @@ -0,0 +1,10 @@ +package org.jetbrains.jps + +/** + * @author nik + */ +class PathUtil { + static String toSystemIndependentPath(String path) { + return path.replace('\\', '/') + } +} diff --git a/jps/src/org/jetbrains/jps/Project.groovy b/jps/src/org/jetbrains/jps/Project.groovy index 09ee1fb2b897..797148769d01 100644 --- a/jps/src/org/jetbrains/jps/Project.groovy +++ b/jps/src/org/jetbrains/jps/Project.groovy @@ -27,6 +27,7 @@ class Project { final Map modules = [:] final Map libraries = [:] final Map artifacts = [:] + final Map runConfigurations = [:] String targetFolder = null String tempFolder = null diff --git a/jps/src/org/jetbrains/jps/RunConfiguration.groovy b/jps/src/org/jetbrains/jps/RunConfiguration.groovy new file mode 100644 index 000000000000..5715f9a82b97 --- /dev/null +++ b/jps/src/org/jetbrains/jps/RunConfiguration.groovy @@ -0,0 +1,75 @@ +package org.jetbrains.jps + +import org.jetbrains.jps.idea.IdeaProjectLoadingUtil +import org.jetbrains.jps.idea.ModuleMacroExpander +import org.jetbrains.jps.idea.OwnServiceLoader +import org.jetbrains.jps.runConf.RunConfigurationLauncherService + +/** + * Represents IntelliJ IDEA run configuration. + * @author pavel.sher + */ +public class RunConfiguration { + final Project project; + final String name; + final String type; + final Module module; + final String workingDir; + final Map allOptions; + final Map envVars; + final Node node; + final MacroExpander macroExpander; + + def RunConfiguration(Project project, MacroExpander macroExpander, Node confTag) { + this.project = project; + this.name = confTag.'@name'; + this.type = confTag.'@type'; + this.node = confTag; + + this.allOptions = [:]; + confTag.option.each{ opt -> + def value = opt.'@value'; + if (value == null) { + value = opt.value ? opt.value[0].'@defaultName' : null; + } + this.allOptions[opt.'@name'] = value; + } + + def moduleNode = confTag.module[0]; + if (moduleNode != null && !"wholeProject".equals(this.allOptions['TEST_SEARCH_SCOPE'])) { + this.module = project.modules[moduleNode.'@name']; + } else { + this.module = null; + } + + this.macroExpander = macroExpander; + if (this.module != null) { + this.macroExpander = new ModuleMacroExpander(macroExpander, this.module.basePath); + } + + def String workDirUrl = this.allOptions['WORKING_DIRECTORY']; + if (workDirUrl == null) workDirUrl = ""; + if (workDirUrl != '') { + workDirUrl = this.macroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(workDirUrl)); + } + + this.workingDir = workDirUrl == '' ? new File(".").getCanonicalPath() : new File(workDirUrl).getCanonicalPath(); + + this.envVars = [:]; + confTag.envs.env.each{ el -> + this.envVars[el.'@name'] = el.'@value'; + } + } + + private static OwnServiceLoader runConfLauncherServices = OwnServiceLoader.load(RunConfigurationLauncherService.class) + + def RunConfigurationLauncherService getLauncher() { + for (RunConfigurationLauncherService service: runConfLauncherServices.iterator()) { + if (service.typeId == type) { + return service; + } + } + + return null; + } +} \ No newline at end of file diff --git a/jps/src/org/jetbrains/jps/artifacts/LayoutElement.groovy b/jps/src/org/jetbrains/jps/artifacts/LayoutElement.groovy index aaa534c25d26..d85c2d6015c8 100644 --- a/jps/src/org/jetbrains/jps/artifacts/LayoutElement.groovy +++ b/jps/src/org/jetbrains/jps/artifacts/LayoutElement.groovy @@ -1,9 +1,8 @@ package org.jetbrains.jps.artifacts import org.jetbrains.jps.Project -import org.jetbrains.jps.Library -/** + /** * @author nik */ abstract class LayoutElement { @@ -58,3 +57,11 @@ class ModuleOutputElement extends LayoutElement { project.binding.module.call(moduleName) } } + +class ModuleTestOutputElement extends LayoutElement { + String moduleName + + def build(Project project) { + project.binding.moduleTests.call(moduleName) + } +} diff --git a/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy b/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy index 2830202528b5..6c66afb9d73e 100644 --- a/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy +++ b/jps/src/org/jetbrains/jps/builders/StandardBuilders.groovy @@ -17,399 +17,399 @@ import org.jetbrains.jps.* */ class JavacBuilder implements ModuleBuilder, ModuleCycleBuilder { - def preprocessModuleCycle(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { - doBuildModule(moduleChunk, state) + def preprocessModuleCycle(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { + doBuildModule(moduleChunk, state) + } + + def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { + doBuildModule(moduleChunk, state) + } + + def doBuildModule(ModuleChunk module, ModuleBuildState state) { + if (state.sourceRoots.isEmpty()) return; + + String sourceLevel = module["sourceLevel"] + String targetLevel = module["targetLevel"] + String customArgs = module["javac_args"]; // it seems javac_args property is not set, can we drop it? + if (module.project.builder.useInProcessJavac) { + String version = System.getProperty("java.version") + if (true) { + if (Java16ApiCompilerRunner.compile(module, state, sourceLevel, targetLevel, customArgs)) { + return + } + } + else { + module.project.info("In-process Javac won't be used for '${module.name}', because Java version ($version) doesn't match to source level ($sourceLevel)") + } } - def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { - doBuildModule(moduleChunk, state) + def params = [:] + params.destdir = state.targetFolder + if (sourceLevel != null) params.source = sourceLevel + if (targetLevel != null) params.target = targetLevel + + def javacOpts = module.project.props["compiler.javac.options"] ?: [:]; + def memHeapSize = javacOpts["MAXIMUM_HEAP_SIZE"] == null ? "512m" : javacOpts["MAXIMUM_HEAP_SIZE"] + "m"; + def boolean debugInfo = !"false".equals(javacOpts["DEBUGGING_INFO"]); + def boolean nowarn = "true".equals(javacOpts["GENERATE_NO_WARNINGS"]); + def boolean deprecation = !"false".equals(javacOpts["DEPRECATION"]); + customArgs = javacOpts["ADDITIONAL_OPTIONS_STRING"]; + + params.fork = "true" + params.memoryMaximumSize = memHeapSize; + params.debug = String.valueOf(debugInfo); + params.nowarn = String.valueOf(nowarn); + params.deprecation = String.valueOf(deprecation); + params.verbose = "true" + + def javacExecutable = getJavacExecutable(module) + if (javacExecutable != null) { + params.executable = javacExecutable } - def doBuildModule(ModuleChunk module, ModuleBuildState state) { - if (state.sourceRoots.isEmpty()) return; + def ant = module.project.binding.ant - String sourceLevel = module["sourceLevel"] - String targetLevel = module["targetLevel"] - String customArgs = module["javac_args"]; // it seems javac_args property is not set, can we drop it? - if (module.project.builder.useInProcessJavac) { - String version = System.getProperty("java.version") - if (true) { - if (Java16ApiCompilerRunner.compile(module, state, sourceLevel, targetLevel, customArgs)) { - return - } - } - else { - module.project.info("In-process Javac won't be used for '${module.name}', because Java version ($version) doesn't match to source level ($sourceLevel)") + final BuildListener listener = new AntListener(state.targetFolder, state.sourceRoots, state.callback); + + ant.project.addBuildListener(listener); + + ant.javac(params) { + if (customArgs) { + compilerarg(line: customArgs) + } + + if (state.sourceFiles != null) { + List patterns = [] + + state.sourceFiles.each { + for (String root: state.sourceRoots) { + if (it.startsWith(root) && it.endsWith(".java")) { + patterns << it.substring(root.length() + 1) + break; } + } + + patterns.each { + include(name: it) + } } + } - def params = [:] - params.destdir = state.targetFolder - if (sourceLevel != null) params.source = sourceLevel - if (targetLevel != null) params.target = targetLevel + state.sourceRoots.each { + src(path: it) + } - def javacOpts = module.project.props["compiler.javac.options"]; - def memHeapSize = javacOpts["MAXIMUM_HEAP_SIZE"] == null ? "512m" : javacOpts["MAXIMUM_HEAP_SIZE"] + "m"; - def boolean debugInfo = !"false".equals(javacOpts["DEBUGGING_INFO"]); - def boolean nowarn = "true".equals(javacOpts["GENERATE_NO_WARNINGS"]); - def boolean deprecation = !"false".equals(javacOpts["DEPRECATION"]); - customArgs = javacOpts["ADDITIONAL_OPTIONS_STRING"]; - - params.fork = "true" - params.memoryMaximumSize = memHeapSize; - params.debug = String.valueOf(debugInfo); - params.nowarn = String.valueOf(nowarn); - params.deprecation = String.valueOf(deprecation); - params.verbose = "true" - - def javacExecutable = getJavacExecutable(module) - if (javacExecutable != null) { - params.executable = javacExecutable + state.excludes.each { String root -> + state.sourceRoots.each {String src -> + if (root.startsWith("${src}/")) { + exclude(name: "${root.substring(src.length() + 1)}/**") + } } + } - def ant = module.project.binding.ant - - final BuildListener listener = new AntListener(state.targetFolder, state.sourceRoots, state.callback); - - ant.project.addBuildListener(listener); - - ant.javac(params) { - if (customArgs) { - compilerarg(line: customArgs) - } - - if (state.sourceFiles != null) { - List patterns = [] - - state.sourceFiles.each { - for (String root: state.sourceRoots) { - if (it.startsWith(root) && it.endsWith(".java")) { - patterns << it.substring(root.length() + 1) - break; - } - } - - patterns.each { - include(name: it) - } - } - } - - state.sourceRoots.each { - src(path: it) - } - - state.excludes.each { String root -> - state.sourceRoots.each {String src -> - if (root.startsWith("${src}/")) { - exclude(name: "${root.substring(src.length() + 1)}/**") - } - } - } - - classpath { - state.classpath.each { - pathelement(location: it) - } - } - } - - ant.project.removeBuildListener(listener); - - if (state.sourceFiles != null) { - module.project.builder.listeners*.onJavaFilesCompiled(module, state.sourceFiles.size()) + classpath { + state.classpath.each { + pathelement(location: it) } + } } - private String getJavacExecutable(ModuleChunk module) { - def customJavac = module["javac"] - def jdk = module.getSdk() - if (customJavac != null) { - return customJavac - } - else if (jdk instanceof JavaSdk) { - return jdk.getJavacExecutable() - } - return null + ant.project.removeBuildListener(listener); + + if (state.sourceFiles != null) { + module.project.builder.listeners*.onJavaFilesCompiled(module, state.sourceFiles.size()) } + } + + private String getJavacExecutable(ModuleChunk module) { + def customJavac = module["javac"] + def jdk = module.getSdk() + if (customJavac != null) { + return customJavac + } + else if (jdk instanceof JavaSdk) { + return jdk.getJavacExecutable() + } + return null + } } class ResourceCopier implements ModuleBuilder { - def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { - if (state.iterated) return; + def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { + if (state.iterated) return; - state.iterated = true + state.iterated = true - if (state.sourceRoots.isEmpty()) return; + if (state.sourceRoots.isEmpty()) return; - def ant = project.binding.ant + def ant = project.binding.ant - state.sourceRoots.each {String root -> - final File f = MockFS.fromFiles(root, state.sourceFiles) + state.sourceRoots.each {String root -> + final File f = MockFS.fromFiles(root, state.sourceFiles) - if (new File(root).exists()) { - def target = state.targetFolder - def prefix = moduleChunk.modules.collect { it.sourceRootPrefixes[root] }.find {it != null} - if (prefix != null) { - if (!(target.endsWith("/") || target.endsWith("\\"))) { - target += "/" - } - target += prefix - } - - ant.copy(todir: target) { - fileset(dir: root) { - patternset(refid: moduleChunk["compiler.resources.id"]) - type(type: "file") - } - } - } - else { - project.warning("$root doesn't exist") - } + if (new File(root).exists()) { + def target = state.targetFolder + def prefix = moduleChunk.modules.collect { it.sourceRootPrefixes[root] }.find {it != null} + if (prefix != null) { + if (!(target.endsWith("/") || target.endsWith("\\"))) { + target += "/" + } + target += prefix } + + ant.copy(todir: target) { + fileset(dir: root) { + patternset(refid: moduleChunk["compiler.resources.id"]) + type(type: "file") + } + } + } + else { + project.warning("$root doesn't exist") + } } + } } class GroovycBuilder implements ModuleBuilder { - def GroovycBuilder(org.jetbrains.jps.Project project) { - project.taskdef(name: "groovyc", classname: "org.codehaus.groovy.ant.Groovyc") + def GroovycBuilder(org.jetbrains.jps.Project project) { + project.taskdef(name: "groovyc", classname: "org.codehaus.groovy.ant.Groovyc") + } + + def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { + if (!GroovyFileSearcher.containGroovyFiles(state.sourceRoots)) return + + def ant = project.binding.ant + + final String destDir = state.targetFolder + + ant.touch(millis: 239) { + fileset(dir: destDir) { + include(name: "**/*.class") + } } - def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { - if (!GroovyFileSearcher.containGroovyFiles(state.sourceRoots)) return + // unfortunately we have to disable fork here because of a bug in Groovyc task: it creates too long command line if classpath is large + ant.groovyc(destdir: destDir /*, fork: "true"*/) { + state.sourceRoots.each { + src(path: it) + } - def ant = project.binding.ant + include(name: "**/*.groovy") - final String destDir = state.targetFolder - - ant.touch(millis: 239) { - fileset(dir: destDir) { - include(name: "**/*.class") - } + classpath { + state.classpath.each { + pathelement(location: it) } - // unfortunately we have to disable fork here because of a bug in Groovyc task: it creates too long command line if classpath is large - ant.groovyc(destdir: destDir /*, fork: "true"*/) { - state.sourceRoots.each { - src(path: it) - } - - include(name: "**/*.groovy") - - classpath { - state.classpath.each { - pathelement(location: it) - } - - pathelement(location: destDir) // Includes classes generated there by javac compiler - } - } - - ant.touch() { - fileset(dir: destDir) { - include(name: "**/*.class") - } - } + pathelement(location: destDir) // Includes classes generated there by javac compiler + } } + + ant.touch() { + fileset(dir: destDir) { + include(name: "**/*.class") + } + } + } } class GroovyStubGenerator implements ModuleBuilder { - def GroovyStubGenerator(org.jetbrains.jps.Project project) { - project.taskdef(name: "generatestubs", classname: "org.codehaus.groovy.ant.GenerateStubsTask") + def GroovyStubGenerator(org.jetbrains.jps.Project project) { + project.taskdef(name: "generatestubs", classname: "org.codehaus.groovy.ant.GenerateStubsTask") + } + + def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { + if (!GroovyFileSearcher.containGroovyFiles(state.sourceRoots)) return + + def ant = project.binding.ant + + String targetFolder = project.targetFolder + File dir = new File(targetFolder != null ? targetFolder : ".", "___temp___") + BuildUtil.deleteDir(project, dir.absolutePath) + ant.mkdir(dir: dir) + + def stubsRoot = dir.getAbsolutePath() + ant.generatestubs(destdir: stubsRoot) { + state.sourceRoots.each { + src(path: it) + } + + include(name: "**/*.groovy") + include(name: "**/*.java") + + classpath { + state.classpath.each { + pathelement(location: it) + } + } } - def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { - if (!GroovyFileSearcher.containGroovyFiles(state.sourceRoots)) return + if (state.incremental) { + Set excluded = state.excludes.collect { new File(it.toString()) } + List filesToCompile = new LinkedList() - def ant = project.binding.ant + JavaFileCollector.collectRecursively(new File(stubsRoot), filesToCompile, excluded) - String targetFolder = project.targetFolder - File dir = new File(targetFolder != null ? targetFolder : ".", "___temp___") - BuildUtil.deleteDir(project, dir.absolutePath) - ant.mkdir(dir: dir) - - def stubsRoot = dir.getAbsolutePath() - ant.generatestubs(destdir: stubsRoot) { - state.sourceRoots.each { - src(path: it) - } - - include(name: "**/*.groovy") - include(name: "**/*.java") - - classpath { - state.classpath.each { - pathelement(location: it) - } - } - } - - if (state.incremental) { - Set excluded = state.excludes.collect { new File(it.toString()) } - List filesToCompile = new LinkedList() - - JavaFileCollector.collectRecursively(new File(stubsRoot), filesToCompile, excluded) - - filesToCompile.each {state.sourceFiles << it.getAbsolutePath()} - } - - state.sourceRoots << stubsRoot - state.tempRootsToDelete << stubsRoot + filesToCompile.each {state.sourceFiles << it.getAbsolutePath()} } + state.sourceRoots << stubsRoot + state.tempRootsToDelete << stubsRoot + } + } class JetBrainsInstrumentations implements ModuleBuilder { - class CustomFormInstrumenter extends - FormInstrumenter { - final List formFiles; - final ModuleBuildState state; + class CustomFormInstrumenter extends + FormInstrumenter { + final List formFiles; + final ModuleBuildState state; - @Override - void associate(final String formFile, final String classFile) { - if (state.callback != null) { - final String formRelPath = state.projectWrapper.getRelativePath(formFile); - final String classRelPath = state.projectWrapper.getRelativePath(classFile); + @Override + void associate(final String formFile, final String classFile) { + if (state.callback != null) { + final String formRelPath = state.projectWrapper.getRelativePath(formFile); + final String classRelPath = state.projectWrapper.getRelativePath(classFile); - state.callback.associateForm(StringCache.get(formRelPath), StringCache.get(classRelPath)); - } - } - - @Override - void log(String msg, int option) { - System.out.println(msg); - } - - @Override - void fireError(String msg) { - throw new RuntimeException(msg); - } - - CustomFormInstrumenter(final File destDir, final List nestedFormPathList, final List ff, final ModuleBuildState s) { - super(destDir, nestedFormPathList); - formFiles = ff; - state = s; - } + state.callback.associateForm(StringCache.get(formRelPath), StringCache.get(classRelPath)); + } } - def JetBrainsInstrumentations(org.jetbrains.jps.Project project) { - project.taskdef(name: "jb_instrumentations", classname: "com.intellij.ant.InstrumentIdeaExtensions") + @Override + void log(String msg, int option) { + System.out.println(msg); } - def getPrefixedPath(org.jetbrains.jps.Project project, String root, ModuleChunk moduleChunk) { - final path = new PrefixedPath(project.binding.ant.project, root) - - moduleChunk.elements.each {module -> - final String prefix = module.sourceRootPrefixes[root] - if (prefix != null) { - path.setPrefix(prefix) - } - } - - return path + @Override + void fireError(String msg) { + throw new RuntimeException(msg); } - def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { - if (state.loader == null) { - final StringBuilder cp = new StringBuilder() - - cp.append(state.targetFolder) - cp.append(File.pathSeparator) - - state.classpath.each { - cp.append(it) - cp.append(File.pathSeparator) - } - - state.loader = InstrumentationUtil.createPseudoClassLoader(cp.toString()) - - final List formFiles = new ArrayList(); - final ProjectWrapper pw = state.projectWrapper; - - for (Module m: moduleChunk.elements) { - final Set names = state.tests ? pw.getModule(m.getName()).getTests() : pw.getModule(m.getName()).getSources(); - for (S name: names) { - if (name.value.endsWith(".form")) { - formFiles.add(new File(pw.getAbsolutePath(name.value))); - } - } - } - - final List nestedFormDirs = new ArrayList(); - - state.sourceRoots.each { - nestedFormDirs << getPrefixedPath(project, it, moduleChunk) - } - - state.moduleDependenciesSourceRoots.each { - nestedFormDirs << getPrefixedPath(project, it, moduleChunk) - } - - state.formInstrumenter = new CustomFormInstrumenter(new File(state.targetFolder), nestedFormDirs, formFiles, state); - - if (!state.incremental) { - for (File formFile: formFiles) { - state.formInstrumenter.instrumentForm(formFile, state.loader); - } - } - } - - if (state.incremental) { - for (String f: state.sourceFiles) { - if (f.endsWith(".form")) { - state.formInstrumenter.instrumentForm(new File(f), state.loader); - } - } - } - - if (project.getBuilder().useInProcessJavac) - return; - - if (!state.incremental) { - new Object() { - public void traverse(final File root) { - final File[] files = root.listFiles(); - - for (File f: files) { - final String name = f.getName(); - - if (name.endsWith(".class")) { - InstrumentationUtil.instrumentNotNull(f, state.loader) - } - else if (f.isDirectory()) { - traverse(f) - } - } - } - }.traverse(new File(state.targetFolder)) - } - else { - final Collection classes = state.callback.getClassFiles() - - classes.each { - InstrumentationUtil.instrumentNotNull(new File(state.targetFolder + File.separator + it.value + ".class"), state.loader) - } - } + CustomFormInstrumenter(final File destDir, final List nestedFormPathList, final List ff, final ModuleBuildState s) { + super(destDir, nestedFormPathList); + formFiles = ff; + state = s; } + } + + def JetBrainsInstrumentations(org.jetbrains.jps.Project project) { + project.taskdef(name: "jb_instrumentations", classname: "com.intellij.ant.InstrumentIdeaExtensions") + } + + def getPrefixedPath(org.jetbrains.jps.Project project, String root, ModuleChunk moduleChunk) { + final path = new PrefixedPath(project.binding.ant.project, root) + + moduleChunk.elements.each {module -> + final String prefix = module.sourceRootPrefixes[root] + if (prefix != null) { + path.setPrefix(prefix) + } + } + + return path + } + + def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { + if (state.loader == null) { + final StringBuilder cp = new StringBuilder() + + cp.append(state.targetFolder) + cp.append(File.pathSeparator) + + state.classpath.each { + cp.append(it) + cp.append(File.pathSeparator) + } + + state.loader = InstrumentationUtil.createPseudoClassLoader(cp.toString()) + + final List formFiles = new ArrayList(); + final ProjectWrapper pw = state.projectWrapper; + + for (Module m: moduleChunk.elements) { + final Set names = state.tests ? pw.getModule(m.getName()).getTests() : pw.getModule(m.getName()).getSources(); + for (S name: names) { + if (name.value.endsWith(".form")) { + formFiles.add(new File(pw.getAbsolutePath(name.value))); + } + } + } + + final List nestedFormDirs = new ArrayList(); + + state.sourceRoots.each { + nestedFormDirs << getPrefixedPath(project, it, moduleChunk) + } + + state.moduleDependenciesSourceRoots.each { + nestedFormDirs << getPrefixedPath(project, it, moduleChunk) + } + + state.formInstrumenter = new CustomFormInstrumenter(new File(state.targetFolder), nestedFormDirs, formFiles, state); + + if (!state.incremental) { + for (File formFile: formFiles) { + state.formInstrumenter.instrumentForm(formFile, state.loader); + } + } + } + + if (state.incremental) { + for (String f: state.sourceFiles) { + if (f.endsWith(".form")) { + state.formInstrumenter.instrumentForm(new File(f), state.loader); + } + } + } + + if (project.getBuilder().useInProcessJavac) + return; + + if (!state.incremental) { + new Object() { + public void traverse(final File root) { + final File[] files = root.listFiles(); + + for (File f: files) { + final String name = f.getName(); + + if (name.endsWith(".class")) { + InstrumentationUtil.instrumentNotNull(f, state.loader) + } + else if (f.isDirectory()) { + traverse(f) + } + } + } + }.traverse(new File(state.targetFolder)) + } + else { + final Collection classes = state.callback.getClassFiles() + + classes.each { + InstrumentationUtil.instrumentNotNull(new File(state.targetFolder + File.separator + it.value + ".class"), state.loader) + } + } + } } class CustomTasksBuilder implements ModuleBuilder { - List tasks = [] + List tasks = [] - def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { - moduleChunk.modules.each {Module module -> - tasks*.perform(module, state.targetFolder) - } + def processModule(ModuleBuildState state, ModuleChunk moduleChunk, org.jetbrains.jps.Project project) { + moduleChunk.modules.each {Module module -> + tasks*.perform(module, state.targetFolder) } + } - def registerTask(String moduleName, Closure task) { - tasks << ({Module module, String outputFolder -> - if (module.name == moduleName) { - task(module, outputFolder) - } - } as ModuleBuildTask) - } -} \ No newline at end of file + def registerTask(String moduleName, Closure task) { + tasks << ({Module module, String outputFolder -> + if (module.name == moduleName) { + task(module, outputFolder) + } + } as ModuleBuildTask) + } +} diff --git a/jps/src/org/jetbrains/jps/idea/ArtifactLoader.groovy b/jps/src/org/jetbrains/jps/idea/ArtifactLoader.groovy index 8fb5b60056cb..2084764e08a7 100644 --- a/jps/src/org/jetbrains/jps/idea/ArtifactLoader.groovy +++ b/jps/src/org/jetbrains/jps/idea/ArtifactLoader.groovy @@ -48,7 +48,7 @@ class ArtifactLoader { def jarPath = macroExpander.expandMacros(tag."@path") String pathInJar = tag."@path-in-jar" if (pathInJar == null) pathInJar = "/" - if (!new File(pathInJar).exists()) { + if (!new File(jarPath).exists()) { project.warning("Error in '$artifactName' artifact: file '$jarPath' doesn't exist") } return new ExtractedDirectoryElement(jarPath: jarPath, pathInJar: pathInJar) @@ -58,6 +58,12 @@ class ArtifactLoader { project.error("Unknown module '$name' in '$artifactName' artifact") } return new ModuleOutputElement(moduleName: name); + case "module-test-output": + def name = tag."@name" + if (project.modules[name] == null) { + project.error("Unknown module '$name' in '$artifactName' artifact") + } + return new ModuleTestOutputElement(moduleName: name); case "library": return new LibraryFilesElement(libraryLevel: tag."@level", libraryName: tag."@name", moduleName: tag."@module-name"); } diff --git a/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy b/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy index 6dbfff2209e8..d07e6bea6190 100644 --- a/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy +++ b/jps/src/org/jetbrains/jps/idea/IdeaProjectLoader.groovy @@ -2,7 +2,6 @@ package org.jetbrains.jps.idea import org.jetbrains.jps.artifacts.Artifact import org.jetbrains.jps.* -import org.jetbrains.jps.builders.GroovyStubGenerator /** * @author max @@ -93,6 +92,7 @@ public class IdeaProjectLoader { loadModules(getComponent(root, "ProjectModuleManager")) loadProjectLibraries(getComponent(root, "libraryTable")) loadArtifacts(getComponent(root, "ArtifactManager")) + loadRunConfigurations(getComponent(root, "ProjectRunConfigurationManager")) } def loadFromDirectoryBased(File dir) { @@ -131,6 +131,16 @@ public class IdeaProjectLoader { } } } + + def runConfFolder = new File(dir, "runConfigurations") + if (runConfFolder.isDirectory()) { + runConfFolder.eachFile {File file -> + if (file.isFile()) { + def runConfManager = new XmlParser(false, false).parse(file); + loadRunConfigurations(runConfManager); + } + } + } } private def loadCompilerConfiguration(Node root) { @@ -204,6 +214,16 @@ public class IdeaProjectLoader { } } + def loadRunConfigurations(Node runConfManager) { + if (runConfManager == null) return; + + runConfManager.configuration.each {Node confTag -> + def name = confTag.'@name'; + RunConfiguration runConf = new RunConfiguration(project, projectMacroExpander, confTag); + project.runConfigurations[name] = runConf; + } + } + private def loadModules(Node modulesComponent) { modulesComponent?.modules?.module?.each {Node moduleTag -> loadModule(projectMacroExpander.expandMacros(moduleTag.@filepath)) @@ -379,8 +399,8 @@ public class IdeaProjectLoader { if (projectOutputPath == null) { project.error("Module '$currentModuleName' uses output path inherited from project but project output path is not specified") } - currentModule.outputPath = new File(new File(projectOutputPath, "production"), currentModuleName).absolutePath - currentModule.testOutputPath = new File(new File(projectOutputPath, "test"), currentModuleName).absolutePath + currentModule.outputPath = PathUtil.toSystemIndependentPath(new File(new File(projectOutputPath, "production"), currentModuleName).absolutePath) + currentModule.testOutputPath = PathUtil.toSystemIndependentPath(new File(new File(projectOutputPath, "test"), currentModuleName).absolutePath) } else { currentModule.outputPath = moduleMacroExpander.expandMacros(IdeaProjectLoadingUtil.pathFromUrl(componentTag.output[0]?.@url)) diff --git a/jps/src/org/jetbrains/jps/idea/ProjectMacroExpander.groovy b/jps/src/org/jetbrains/jps/idea/ProjectMacroExpander.groovy index e8d644caf022..5d8a68c9e524 100644 --- a/jps/src/org/jetbrains/jps/idea/ProjectMacroExpander.groovy +++ b/jps/src/org/jetbrains/jps/idea/ProjectMacroExpander.groovy @@ -1,6 +1,7 @@ package org.jetbrains.jps.idea import org.jetbrains.jps.MacroExpander +import org.jetbrains.jps.PathUtil /** * @author nik @@ -11,7 +12,7 @@ public class ProjectMacroExpander implements MacroExpander { ProjectMacroExpander(Map pathVariables, String projectBasePath) { this.pathVariables = pathVariables - this.projectBasePath = projectBasePath + this.projectBasePath = PathUtil.toSystemIndependentPath(projectBasePath) } @Override @@ -19,8 +20,8 @@ public class ProjectMacroExpander implements MacroExpander { if (path == null) return path path = path.replace("\$PROJECT_DIR\$", projectBasePath) pathVariables.each { name, value -> - path = path.replace("\$${name}\$", value); + path = path.replace("\$${name}\$", value) } - return path.replace("/", File.separator); + return path } } diff --git a/jps/src/org/jetbrains/jps/runConf/RunConfigurationLauncherService.java b/jps/src/org/jetbrains/jps/runConf/RunConfigurationLauncherService.java new file mode 100644 index 000000000000..d47073267ce5 --- /dev/null +++ b/jps/src/org/jetbrains/jps/runConf/RunConfigurationLauncherService.java @@ -0,0 +1,30 @@ +package org.jetbrains.jps.runConf; + +import org.jetbrains.jps.RunConfiguration; + +public abstract class RunConfigurationLauncherService { + private final String typeId; + + public RunConfigurationLauncherService(String typeId) { + this.typeId = typeId; + } + + public String getTypeId() { + return typeId; + } + + public void beforeStart(RunConfiguration runConf) {} + + public void afterFinish(RunConfiguration runConf) {} + + public final void start(RunConfiguration runConf) { + beforeStart(runConf); + try { + startInternal(runConf); + } finally { + afterFinish(runConf); + } + } + + protected abstract void startInternal(RunConfiguration runConf); +} diff --git a/jps/src/org/jetbrains/jps/runConf/java/ClasspathUtil.java b/jps/src/org/jetbrains/jps/runConf/java/ClasspathUtil.java new file mode 100644 index 000000000000..090fc3e9f979 --- /dev/null +++ b/jps/src/org/jetbrains/jps/runConf/java/ClasspathUtil.java @@ -0,0 +1,118 @@ +package org.jetbrains.jps.runConf.java; + +import java.io.File; +import java.net.URL; +import java.util.LinkedHashSet; +import java.util.Set; + +public class ClasspathUtil { + private static final String FILE = "file"; + private static final String JAR = "jar"; + private static final String JAR_DELIMITER = "!"; + private static final String PROTOCOL_DELIMITER = ":"; + + public static String composeClasspath(Class[] classes) { + Set cp = new LinkedHashSet(); + if (classes != null) { + for (Class classFromLib : classes) { + cp.add(getResourceRoot(classFromLib)); + } + } + + StringBuilder result = new StringBuilder(); + for (String path : cp) { + if (result.length() > 0) { + result.append(File.pathSeparator); + } + result.append(path); + } + + return result.toString(); + } + + /** + * Attempts to detect classpath entry which contains given resource + */ + private static String getResourceRoot(Class context, String path) { + URL url = context.getResource(path); + if (url == null) { + url = ClassLoader.getSystemResource(path.substring(1)); + } + if (url == null) { + return null; + } + return extractRoot(url, path); + } + + /** + * Attempts to extract classpath entry part from passed URL. + */ + private static String extractRoot(URL resourceURL, String resourcePath) { + if (!(resourcePath.startsWith("/") || resourcePath.startsWith("\\"))) { + //noinspection HardCodedStringLiteral + System.err.println("precondition failed"); + return null; + } + String protocol = resourceURL.getProtocol(); + String resultPath = null; + + if (FILE.equals(protocol)) { + String path = resourceURL.getFile(); + String testPath = path.replace('\\', '/').toLowerCase(); + String testResourcePath = resourcePath.replace('\\', '/').toLowerCase(); + if (testPath.endsWith(testResourcePath)) { + resultPath = path.substring(0, path.length() - resourcePath.length()); + } + } + else if (JAR.equals(protocol)) { + String fullPath = resourceURL.getFile(); + int delimiter = fullPath.indexOf(JAR_DELIMITER); + if (delimiter >= 0) { + String archivePath = fullPath.substring(0, delimiter); + if (archivePath.startsWith(FILE + PROTOCOL_DELIMITER)) { + resultPath = archivePath.substring(FILE.length() + PROTOCOL_DELIMITER.length()); + } + } + } + + if (resultPath != null && resultPath.endsWith(File.separator)) { + resultPath = resultPath.substring(0, resultPath.length() - 1); + } + + resultPath = replaceAll(resultPath, "%20", " "); + resultPath = replaceAll(resultPath, "%23", "#"); + + // !Workaround for /D:/some/path/a.jar, which doesn't work if D is subst disk + if (resultPath.startsWith("/") && resultPath.indexOf(":") == 2) { + resultPath = resultPath.substring(1); + } + + return resultPath; + } + + private static String replaceAll(final String text, final String pattern, final String replacement) { + if (pattern.length() == 0 || text.length() < pattern.length()) { + return text; + } + final StringBuilder buf = new StringBuilder(text.length()); + int currentTextIndex = 0; + while (currentTextIndex < text.length()) { + final int startOfPattern = text.indexOf(pattern, currentTextIndex); + if (startOfPattern < 0) { + if (currentTextIndex == 0) { // there are no patterns in the text at all + return text; + } + buf.append(text.substring(currentTextIndex)); // append the rest of the text + return buf.toString(); + } + buf.append(text.substring(currentTextIndex, startOfPattern)); + buf.append(replacement); + currentTextIndex = startOfPattern + pattern.length(); + } + return buf.toString(); + } + + private static String getResourceRoot(final Class aClass) { + return getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class"); + } +} diff --git a/jps/src/org/jetbrains/jps/runConf/java/JavaBasedRunConfigurationLauncher.groovy b/jps/src/org/jetbrains/jps/runConf/java/JavaBasedRunConfigurationLauncher.groovy new file mode 100644 index 000000000000..3e76b1d757a9 --- /dev/null +++ b/jps/src/org/jetbrains/jps/runConf/java/JavaBasedRunConfigurationLauncher.groovy @@ -0,0 +1,214 @@ +package org.jetbrains.jps.runConf.java + +import org.jetbrains.jps.ClasspathKind +import org.jetbrains.jps.RunConfiguration +import org.jetbrains.jps.runConf.RunConfigurationLauncherService + +/** + * This launcher is able can be used to start Java main class. + */ +public abstract class JavaBasedRunConfigurationLauncher extends RunConfigurationLauncherService { + private File myOutputFile; + private File myErrorFile; + private Map mySystemProperties = [:]; + + public JavaBasedRunConfigurationLauncher(String typeId) { + super(typeId) + } + + /** + * @return FQN of the main class to execute + */ + public abstract String getMainClassName(RunConfiguration runConf); + + /** + * @return main class arguments + */ + public abstract String getMainClassArguments(RunConfiguration runConf); + + /** + * @return additional JVM arguments + */ + public String getJVMArguments(RunConfiguration runConf) { + return runConf.macroExpander.expandMacros(runConf.allOptions["VM_PARAMETERS"]); + } + + /** + * @return system properties (can be specified in JVM arguments too, but this call is more convenient) + */ + public Map getSystemProperties(RunConfiguration runConf) { return mySystemProperties; }; + + /** + * @return classpath required to launch specified main class + */ + public abstract List getMainClassClasspath(RunConfiguration runConf); + + /** + * Sets file where to write output of the process. + */ + public void setOutputFile(File outputFile) { + myOutputFile = outputFile; + } + + /** + * Sets file where to write error output of the process. + */ + public void setErrorFile(File errFile) { + myErrorFile = errFile; + } + + /** + * Adds system properties. + */ + public void addSystemProperties(Map props) { + mySystemProperties.putAll(props); + } + + final void startInternal(RunConfiguration runConf) { + def project = runConf.project; + + def ant = project.binding.ant; + def params = [ + mainClass: getMainClassName(runConf), + jvmArgs: getJVMArguments(runConf), + classArgs: getMainClassArguments(runConf) + ]; + + def module = runConf.module; + def runConfRuntimeCp = getRuntimeClasspath(runConf); + + def attrs = [:]; + def sdk = module?.sdk ? module.sdk : project.projectSdk; + if (sdk != null) { + attrs["jvm"] = sdk.getJavaExecutable(); + } else { + project.warning("Cannot find java executable, will use java of the current process."); + } + + attrs["classname"] = MainClassLauncher.class.getName(); + attrs["classpath"] = ClasspathUtil.composeClasspath([MainClassLauncher] as Class[]); + attrs["fork"] = "true"; + attrs["dir"] = runConf.workingDir; + attrs["logError"] = "true"; + attrs["failonerror"] = "true"; + + if (myOutputFile != null) { + attrs["output"] = myOutputFile.absolutePath; + } + + if (myErrorFile != null) { + attrs["error"] = myErrorFile.absolutePath; + } + + def runConfRuntimeCpFile = createTempFile(runConfRuntimeCp); + def mainClassCpFile = createTempFile(getMainClassClasspath(runConf)); + def tmpArgs = createTempFile(splitCommandArgumentsAndUnquote(params.classArgs)); + project.info("Starting run configuration $runConf.name ..."); + + ant.java(attrs) { + arg(line: "$params.mainClass \"$mainClassCpFile\" \"$runConfRuntimeCpFile\" \"$tmpArgs\""); + jvmarg(line: params.jvmArgs); + for (Map.Entry envVar: runConf.envVars.entrySet()) { + env(key: envVar.getKey(), value: envVar.getValue()); + } + for (Map.Entry propEntry: getSystemProperties(runConf).entrySet()) { + sysproperty(key: propEntry.getKey(), value: propEntry.getValue()); + } + }; + } + + /** This utility differs from splitHonorQuote: it considers quote in sequence 'ddd\" -' as boundary quote. + * So it can split "-Dffoo=c:\some\path\ddd\" -Dfff=sss correctly. + * */ + private static List splitCommandArgumentsAndUnquote(String line) { + final ArrayList result = new ArrayList(); + final StringBuilder builder = new StringBuilder(); + boolean inQuotes = false; + for (int i = 0; i < line.length(); i++) { + final char c = line.charAt(i); + if (c == ' ' && !inQuotes) { + if (builder.length() > 0) { + result.add(builder.toString()); + builder.setLength(0); + } + continue; + } + + if ((c == '"' || c == '\'') && isNotEscapedQuote(line, i)) { + inQuotes = !inQuotes; + } + builder.append(c); + } + + if (builder.length() > 0) { + result.add(builder.toString()); + } + return removeQuotes(result); + } + + private static List removeQuotes(final List result) { + for (int i = 0; i < result.size(); i++) { + String value = result.get(i); + if (value.length() > 1 && value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"') { + value = value.substring(1, value.length()-1); + } + result.set(i, value); + } + return result; + } + + private static boolean isNotEscapedQuote(final String line, final int i) { + if (i == 0) return true; + if (line.charAt(i - 1) == '\\') { // Previous character is escaping one + + int j; + for(j = i + 1; j < line.length(); j ++) { // inspect chars after the quote + if (line.charAt(j) == ' ') continue; + return (line.charAt(j) == '-') || (line.charAt(j) == '\"' && j + 1 < line.length() && line.charAt(j + 1) == '-'); // next option started, so quote is not escaped actually + } + return j == line.length(); + } + return true; + } + + protected String createTempFile(Collection runtimeClasspath) { + def tmp = File.createTempFile("runConf", "suffix"); + def writer = new BufferedWriter(new FileWriter(tmp)); + + try { + for (String item: runtimeClasspath) { + if (item == null) continue; + writer.writeLine(item); + } + } finally { + writer.close(); + } + return tmp.getCanonicalPath(); + } + + protected Collection splitClasspath(String classpathStr) { + def result = new LinkedHashSet(); + if (classpathStr != null) { + result.addAll(Arrays.asList(classpathStr.split(File.pathSeparator))); + } + return result; + } + + private Collection getRuntimeClasspath(RunConfiguration runConf) { + def runConfRuntimeCp = new LinkedHashSet(); + if (runConf.module != null) { + runConfRuntimeCp.addAll(runConf.module.testRuntimeClasspath()); + } else { + runConfRuntimeCp.addAll(runConf.project.testRuntimeClasspath()); + } + + def sdk = runConf.module?.sdk ? runConf.module.sdk : runConf.project.projectSdk; + if (sdk != null) { + for (String pathEl: sdk.getClasspathRoots(ClasspathKind.TEST_RUNTIME)) { + runConfRuntimeCp.add(pathEl); + } + } + + return runConfRuntimeCp; + } +} diff --git a/jps/src/org/jetbrains/jps/runConf/java/MainClassLauncher.java b/jps/src/org/jetbrains/jps/runConf/java/MainClassLauncher.java new file mode 100644 index 000000000000..12ad02f6bf32 --- /dev/null +++ b/jps/src/org/jetbrains/jps/runConf/java/MainClassLauncher.java @@ -0,0 +1,107 @@ +package org.jetbrains.jps.runConf.java; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class MainClassLauncher { + public static void main(String[] args) throws Throwable { + if (args.length < 4) { + return; + } + + String className = args[0]; + String testRunnerCp = args[1]; + String testsRuntimeCp = args[2]; + String tempArgsFileName = args[3]; + + URL[] testsRuntimeClasspath = getClasspathElems(testsRuntimeCp); + URL[] tcRuntimeClasspath = getClasspathElems(testRunnerCp); + List arguments = getArguments(tempArgsFileName); + + System.out.println("Runtime classpath: " + Arrays.asList(testsRuntimeClasspath)); + + ClassLoader testsRuntimeClassloader = new URLClassLoader(testsRuntimeClasspath, null); + ClassLoader mainClassLoader = new MainClassClassLoader(tcRuntimeClasspath, testsRuntimeClassloader); + + Class main = mainClassLoader.loadClass(className); + Method mainMethod = main.getMethod("main", String[].class); + + // we must not set mainClassLoader to context classloader, because it allows access to our classes, which we want to hide + Thread.currentThread().setContextClassLoader(testsRuntimeClassloader); + + try { + mainMethod.invoke(null, new Object[] { arguments.toArray(new String[arguments.size()])}); + } catch (Throwable e) { + throw new RuntimeException("Exception occurred in main class: " + main.getName() + ", error: " + e.toString(), e); + } + + System.exit(0); + } + + private static URL[] getClasspathElems(String tempFileName) throws IOException { + List result = new ArrayList(); + for (String line: loadLines(tempFileName)) { + URL url = new File(line).toURL(); + result.add(url); + } + + //System.out.println("DEBUG INFO: libraries: " + result); + URL[] array = new URL[result.size()]; + result.toArray(array); + return array; + } + + private static List getArguments(String tempFileName) throws IOException { + return loadLines(tempFileName); + } + + private static List loadLines(final String tempFileName) throws IOException { + List result = new ArrayList(); + BufferedReader reader = new BufferedReader(new FileReader(tempFileName)); + try { + while(true) { + String line = reader.readLine(); + if (line == null) break; + result.add(line); + } + //System.out.println("DEBUG INFO: arguments: " + result.toString()); + return result; + } finally { + reader.close(); + } + } + + /** + * This classloader makes classes from the specified URLs more preferrable than classes from the parent classloader. + * Specified main class is loaded by this classloader. + */ + private static class MainClassClassLoader extends URLClassLoader { + public MainClassClassLoader(final URL[] urls, final ClassLoader parent) { + super(urls, parent); + } + + public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class c = findLoadedClass(name); + if (c == null) { + try { + c = findClass(name); + } + catch (ClassNotFoundException e) { + c = super.loadClass(name, resolve); + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + } +} diff --git a/jps/testData/manifestInArtifact/META-INF/MANIFEST.MF b/jps/testData/manifestInArtifact/META-INF/MANIFEST.MF new file mode 100644 index 000000000000..2706814bd46f --- /dev/null +++ b/jps/testData/manifestInArtifact/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: MyClass + diff --git a/jps/testData/manifestInArtifact/manifest.ipr b/jps/testData/manifestInArtifact/manifest.ipr new file mode 100644 index 000000000000..9710b41f1c2e --- /dev/null +++ b/jps/testData/manifestInArtifact/manifest.ipr @@ -0,0 +1,21 @@ + + + + + $PROJECT_DIR$/out/artifacts/simple + + + + + + + + + + + + + + + + diff --git a/jps/testData/moduleTestOutput/moduleTestOutput.iml b/jps/testData/moduleTestOutput/moduleTestOutput.iml new file mode 100644 index 000000000000..26c800ce3109 --- /dev/null +++ b/jps/testData/moduleTestOutput/moduleTestOutput.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jps/testData/moduleTestOutput/moduleTestOutput.ipr b/jps/testData/moduleTestOutput/moduleTestOutput.ipr new file mode 100644 index 000000000000..3bbd2eb52f70 --- /dev/null +++ b/jps/testData/moduleTestOutput/moduleTestOutput.ipr @@ -0,0 +1,20 @@ + + + + + $PROJECT_DIR$/out/artifacts/tests + + + + + + + + + + + + + + + diff --git a/jps/testData/moduleTestOutput/src/MyClass.java b/jps/testData/moduleTestOutput/src/MyClass.java new file mode 100644 index 000000000000..bef6c0e59445 --- /dev/null +++ b/jps/testData/moduleTestOutput/src/MyClass.java @@ -0,0 +1,9 @@ +/** + * Created by IntelliJ IDEA. + * User: nik + * Date: 4/5/11 + * Time: 4:39 PM + * To change this template use File | Settings | File Templates. + */ +public class MyClass { +} diff --git a/jps/testData/moduleTestOutput/testSrc/MyTest.java b/jps/testData/moduleTestOutput/testSrc/MyTest.java new file mode 100644 index 000000000000..a301f651cc77 --- /dev/null +++ b/jps/testData/moduleTestOutput/testSrc/MyTest.java @@ -0,0 +1,9 @@ +/** + * Created by IntelliJ IDEA. + * User: nik + * Date: 4/5/11 + * Time: 4:39 PM + * To change this template use File | Settings | File Templates. + */ +public class MyTest { +} diff --git a/jps/testData/runConfigurationsDir/.idea/misc.xml b/jps/testData/runConfigurationsDir/.idea/misc.xml new file mode 100644 index 000000000000..1c91dd1a2eb5 --- /dev/null +++ b/jps/testData/runConfigurationsDir/.idea/misc.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://www.w3.org/1999/xhtml + + + + + + + + diff --git a/jps/testData/runConfigurationsDir/.idea/modules.xml b/jps/testData/runConfigurationsDir/.idea/modules.xml new file mode 100644 index 000000000000..97cecbdbbe66 --- /dev/null +++ b/jps/testData/runConfigurationsDir/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/jps/testData/runConfigurationsDir/.idea/runConfigurations/Web.xml b/jps/testData/runConfigurationsDir/.idea/runConfigurations/Web.xml new file mode 100644 index 000000000000..abd2436dfecd --- /dev/null +++ b/jps/testData/runConfigurationsDir/.idea/runConfigurations/Web.xml @@ -0,0 +1,80 @@ + + + + \ No newline at end of file diff --git a/jps/testData/runConfigurationsDir/.idea/runConfigurations/all_tests.xml b/jps/testData/runConfigurationsDir/.idea/runConfigurations/all_tests.xml new file mode 100644 index 000000000000..0a4a465af9a8 --- /dev/null +++ b/jps/testData/runConfigurationsDir/.idea/runConfigurations/all_tests.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jps/testData/runConfigurationsDir/jps.iml b/jps/testData/runConfigurationsDir/jps.iml new file mode 100644 index 000000000000..87c3864535e1 --- /dev/null +++ b/jps/testData/runConfigurationsDir/jps.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jps/testData/runConfigurationsIpr/maven-watcher.iml b/jps/testData/runConfigurationsIpr/maven-watcher.iml new file mode 100644 index 000000000000..00e06fbdb4d2 --- /dev/null +++ b/jps/testData/runConfigurationsIpr/maven-watcher.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/jps/testData/runConfigurationsIpr/maven-watcher.ipr b/jps/testData/runConfigurationsIpr/maven-watcher.ipr new file mode 100644 index 000000000000..fc1a2d0b9e70 --- /dev/null +++ b/jps/testData/runConfigurationsIpr/maven-watcher.ipr @@ -0,0 +1,1122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://www.w3.org/1999/xhtml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jps/testSrc/org/jetbrains/jps/AntPrePostStepsInArtifactsTest.groovy b/jps/testSrc/org/jetbrains/jps/AntPrePostStepsInArtifactsTest.groovy index 7e0000c88edc..ccd3de3d740b 100644 --- a/jps/testSrc/org/jetbrains/jps/AntPrePostStepsInArtifactsTest.groovy +++ b/jps/testSrc/org/jetbrains/jps/AntPrePostStepsInArtifactsTest.groovy @@ -18,7 +18,7 @@ class AntPrePostStepsInArtifactsTest extends JpsBuildTestCase { file("file.txt") } file("prestep.txt", "pre1") - file("poststep.txt", "$outDir.absolutePath" + File.separator + "artifacts" + File.separator + "main") + file("poststep.txt", "${PathUtil.toSystemIndependentPath(outDir.absolutePath)}/artifacts/main") } } } diff --git a/jps/testSrc/org/jetbrains/jps/JpsBuildTestCase.groovy b/jps/testSrc/org/jetbrains/jps/JpsBuildTestCase.groovy index a4185e4c3731..729b88c21a2a 100644 --- a/jps/testSrc/org/jetbrains/jps/JpsBuildTestCase.groovy +++ b/jps/testSrc/org/jetbrains/jps/JpsBuildTestCase.groovy @@ -5,70 +5,88 @@ import org.codehaus.gant.GantBinding import org.jetbrains.ether.ProjectWrapper import org.jetbrains.jps.idea.IdeaProjectLoader import org.jetbrains.jps.util.FileSystemItem -import org.jetbrains.jps.util.FileUtil +import org.jetbrains.jps.util.TempFiles /** * @author nik */ abstract class JpsBuildTestCase extends TestCase { + private TempFiles myTempFiles; - def doTest(String projectPath, Closure initProject, Closure expectedOutput) { - doTest(projectPath, [:], initProject, expectedOutput) + @Override + protected void setUp() { + myTempFiles = new TempFiles(); + } + + @Override + protected void tearDown() { + myTempFiles.cleanup(); + } + + def doTest(String projectPath, Closure initProject, Closure expectedOutput) { + doTest(projectPath, [:], initProject, expectedOutput) + } + + def doTest(String projectPath, Map pathVariables, Closure initProject, Closure expectedOutput) { + Project project = buildAll(projectPath, pathVariables, initProject) + assertOutput(project, project.targetFolder, expectedOutput); + } + + def protected assertOutput(Project project, String targetFolder, Closure expectedOutput) { + def root = new FileSystemItem(name: "") + initFileSystemItem(root, expectedOutput) + root.assertDirectoryEqual(new File(targetFolder), "") + } + + def protected buildAll(String projectPath, Map pathVariables, Closure initProject) { + def binding = new GantBinding() + binding.includeTool << Jps + ProjectWrapper pw = ProjectWrapper.load(binding, projectPath, null, pathVariables, false) + Project project = pw.getProject() + def target = createTempDir() + project.targetFolder = target.absolutePath + initProject(project) + pw.clean() + pw.rebuild() + pw.buildArtifacts() + pw.deleteTempFiles() + return project + } + + protected Project loadProject(String projectPath, Map pathVariables) { + def binding = new GantBinding() + binding.includeTool << Jps + def project = new Project(binding) + IdeaProjectLoader.loadFromPath(project, projectPath, pathVariables, null) + return project + } + + def initFileSystemItem(FileSystemItem item, Closure initializer) { + def meta = new InitializingExpando() + meta.dir = {String name, Closure content -> + def dir = new FileSystemItem(name: name, directory: true) + initFileSystemItem(dir, content) + item << dir + } + meta.archive = {String name, Closure content -> + def archive = new FileSystemItem(name: name, archive: true) + initFileSystemItem(archive, content) + item << archive + } + meta.file = {Object[] args -> + item << new FileSystemItem(name: args[0], content: args.length > 1 ? args[1] : null) } - def doTest(String projectPath, Map pathVariables, Closure initProject, Closure expectedOutput) { - Project project = buildAll(projectPath, pathVariables, initProject) - assertOutput(project, project.targetFolder, expectedOutput); - } + initializer.delegate = meta + initializer.setResolveStrategy Closure.DELEGATE_FIRST + initializer() + } - def protected assertOutput(Project project, String targetFolder, Closure expectedOutput) { - def root = new FileSystemItem(name: "") - initFileSystemItem(root, expectedOutput) - root.assertDirectoryEqual(new File(targetFolder), "") - } - - def protected buildAll(String projectPath, Map pathVariables, Closure initProject) { - def binding = new GantBinding() - binding.includeTool << Jps - ProjectWrapper pw = ProjectWrapper.load(binding, projectPath, null, pathVariables, false) - Project project = pw.getProject() - def target = FileUtil.createTempDirectory("targetDir") - project.targetFolder = target.absolutePath - initProject(project) - pw.clean() - pw.rebuild() - pw.buildArtifacts() - pw.deleteTempFiles() - return project - } - - protected Project loadProject(String projectPath, Map pathVariables) { - def binding = new GantBinding() - binding.includeTool << Jps - def project = new Project(binding) - IdeaProjectLoader.loadFromPath(project, projectPath, pathVariables, null) - return project - } - - def initFileSystemItem(FileSystemItem item, Closure initializer) { - def meta = new InitializingExpando() - meta.dir = {String name, Closure content -> - def dir = new FileSystemItem(name: name, directory: true) - initFileSystemItem(dir, content) - item << dir - } - meta.archive = {String name, Closure content -> - def archive = new FileSystemItem(name: name, archive: true) - initFileSystemItem(archive, content) - item << archive - } - meta.file = {Object[] args -> - item << new FileSystemItem(name: args[0], content: args.length > 1 ? args[1] : null) - } - - initializer.delegate = meta - initializer.setResolveStrategy Closure.DELEGATE_FIRST - initializer() - } + def File createTempDir() { + return myTempFiles.createTempDir(); + } + def File createTempFile() { + return myTempFiles.createTempFile(); + } } diff --git a/jps/testSrc/org/jetbrains/jps/ManifestInArtifactTest.groovy b/jps/testSrc/org/jetbrains/jps/ManifestInArtifactTest.groovy new file mode 100644 index 000000000000..a81208420d61 --- /dev/null +++ b/jps/testSrc/org/jetbrains/jps/ManifestInArtifactTest.groovy @@ -0,0 +1,21 @@ +package org.jetbrains.jps + +import java.util.jar.Manifest +import org.jetbrains.jps.util.ZipUtil +import java.util.jar.Attributes + +/** + * @author nik + */ +class ManifestInArtifactTest extends JpsBuildTestCase { + public void test() { + def project = buildAll("testData/manifestInArtifact/manifest.ipr", [:], {}) + File jarFile = new File(project.targetFolder + "/artifacts/simple/simple.jar") + assertTrue(jarFile.exists()) + File extracted = ZipUtil.extractToTempDir(jarFile) + File manifestFile = new File(extracted, "META-INF/MANIFEST.MF") + assertTrue(manifestFile.exists()) + Manifest manifest = new Manifest(new FileInputStream(manifestFile)) + assertEquals("MyClass", manifest.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS)) + } +} diff --git a/jps/testSrc/org/jetbrains/jps/ModuleTestOutputElementTest.groovy b/jps/testSrc/org/jetbrains/jps/ModuleTestOutputElementTest.groovy new file mode 100644 index 000000000000..df0aeb1e8767 --- /dev/null +++ b/jps/testSrc/org/jetbrains/jps/ModuleTestOutputElementTest.groovy @@ -0,0 +1,26 @@ +package org.jetbrains.jps + +/** + * @author nik + */ +class ModuleTestOutputElementTest extends JpsBuildTestCase { + public void test() { + doTest("testData/moduleTestOutput/moduleTestOutput.ipr", {}, { + dir("artifacts") { + dir("tests") { + file("MyTest.class") + } + } + dir("production") { + dir("moduleTestOutput") { + file("MyClass.class") + } + } + dir("test") { + dir("moduleTestOutput") { + file("MyTest.class") + } + } + }) + } +} diff --git a/jps/testSrc/org/jetbrains/jps/RunConfLoaderTest.groovy b/jps/testSrc/org/jetbrains/jps/RunConfLoaderTest.groovy new file mode 100644 index 000000000000..83f6e75a7441 --- /dev/null +++ b/jps/testSrc/org/jetbrains/jps/RunConfLoaderTest.groovy @@ -0,0 +1,25 @@ +package org.jetbrains.jps + +/** + * @author pavel.sher + */ +class RunConfLoaderTest extends JpsBuildTestCase { + public void testLoadRunConfigurationsIpr() { + Project project = loadProject("testData/runConfigurationsIpr/maven-watcher.ipr", [:]); + assertEquals(2, project.runConfigurations.size()); + assertNotNull(project.runConfigurations["maven-watcher-core"]); + assertNotNull(project.runConfigurations["testng maven-watcher-core"]); + + RunConfiguration junitRunConf = project.runConfigurations["maven-watcher-core"]; + assertEquals("JUnit", junitRunConf.type); + assertEquals("com.jetbrains.maven.watcher", junitRunConf.allOptions["PACKAGE_NAME"]); + assertEquals(project.modules["maven-watcher-core"], junitRunConf.module) + } + + public void testLoadRunConfigurationsDir() { + Project project = loadProject("testData/runConfigurationsDir", [:]); + assertEquals(2, project.runConfigurations.size()); + assertNotNull(project.runConfigurations["all tests"]); + assertNotNull(project.runConfigurations["Web"]); + } +} diff --git a/jps/testSrc/org/jetbrains/jps/util/TempFiles.java b/jps/testSrc/org/jetbrains/jps/util/TempFiles.java new file mode 100644 index 000000000000..9c753c3367f7 --- /dev/null +++ b/jps/testSrc/org/jetbrains/jps/util/TempFiles.java @@ -0,0 +1,83 @@ +package org.jetbrains.jps.util; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * @author Pavel.Sher + * Date: 05.03.2008 + */ +public class TempFiles { + private static final File ourCurrentTempDir = new File(System.getProperty("java.io.tmpdir")); + private final File myCurrentTempDir; + + private static Random ourRandom; + + static { + ourRandom = new Random(); + ourRandom.setSeed(System.currentTimeMillis()); + } + + private final List myFilesToDelete = new ArrayList(); + + public TempFiles() { + myCurrentTempDir = ourCurrentTempDir; + if (!myCurrentTempDir.isDirectory()) { + throw new IllegalStateException("Temp directory is not a directory, was deleted by some process: " + myCurrentTempDir.getAbsolutePath()); + } + } + + private File doCreateTempDir(String prefix, String suffix) throws IOException { + prefix = prefix == null ? "" : prefix; + suffix = suffix == null ? ".tmp" : suffix; + + do { + int count = ourRandom.nextInt(); + final File f = new File(myCurrentTempDir, prefix + count + suffix); + if (!f.exists() && f.mkdirs()) { + return f.getCanonicalFile(); + } + } while (true); + } + + private File doCreateTempFile(String prefix, String suffix) throws IOException { + final File file = doCreateTempDir(prefix, suffix); + file.delete(); + file.createNewFile(); + return file; + } + + public final File createTempFile() throws IOException { + File tempFile = doCreateTempFile("test", null); + registerAsTempFile(tempFile); + return tempFile; + } + + private void registerAsTempFile(final File tempFile) { + myFilesToDelete.add(tempFile); + } + + /** + * Returns a File object for created temp directory. + * + * @return a File object for created temp directory + * @throws IOException if directory creation fails. + */ + public final File createTempDir() throws IOException { + File f = doCreateTempDir("test", ""); + registerAsTempFile(f); + return f; + } + + public void cleanup() { + for (File file : myFilesToDelete) { + if (file.exists()) { + FileUtil.delete(file); + } + } + myFilesToDelete.clear(); + } +}