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/.idea/modules.xml b/jps/.idea/modules.xml
index 7ea43e2d9aea..acfb3c92e586 100644
--- a/jps/.idea/modules.xml
+++ b/jps/.idea/modules.xml
@@ -3,10 +3,12 @@
+
+
diff --git a/jps/.idea/vcs.xml b/jps/.idea/vcs.xml
index 47750bf7e46b..cbc984988338 100644
--- a/jps/.idea/vcs.xml
+++ b/jps/.idea/vcs.xml
@@ -1,10 +1,10 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/jps/antLayout/src/jetbrains/antlayout/antlib.xml b/jps/antLayout/src/jetbrains/antlayout/antlib.xml
index 3ef82b992989..bc7053c450e8 100755
--- a/jps/antLayout/src/jetbrains/antlayout/antlib.xml
+++ b/jps/antLayout/src/jetbrains/antlayout/antlib.xml
@@ -7,4 +7,5 @@
+
\ No newline at end of file
diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/Container.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/Container.java
index 517d3a37a5d1..d2b487512025 100755
--- a/jps/antLayout/src/jetbrains/antlayout/datatypes/Container.java
+++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/Container.java
@@ -60,6 +60,10 @@ public abstract class Container extends Content {
children.add(new FileSetContainer(module));
}
+ public void addModuleTests(IdeaModuleTests module) {
+ children.add(new FileSetContainer(module));
+ }
+
public List getChildren() {
return children;
}
@@ -100,6 +104,9 @@ public abstract class Container extends Content {
if (set instanceof IdeaModule) {
return new IdeaModule((IdeaModule) set);
}
+ if (set instanceof IdeaModuleTests) {
+ return new IdeaModuleTests((IdeaModuleTests) set);
+ }
if (set instanceof ZipFileSet) {
return new LayoutFileSet((ZipFileSet) set.clone());
} else {
diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/FileSetContainer.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/FileSetContainer.java
index 07468b91c13b..8c872813cab2 100644
--- a/jps/antLayout/src/jetbrains/antlayout/datatypes/FileSetContainer.java
+++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/FileSetContainer.java
@@ -27,8 +27,8 @@ public class FileSetContainer extends Content {
@Override
public void validateArguments() throws BuildException {
- if (fileSet instanceof IdeaModule) {
- ((IdeaModule) fileSet).validateArguments();
+ if (fileSet instanceof IdeaModuleBase) {
+ ((IdeaModuleBase) fileSet).validateArguments();
}
}
}
diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModule.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModule.java
index 7435db1053d7..5f2fc37938f1 100755
--- a/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModule.java
+++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModule.java
@@ -1,65 +1,23 @@
-package jetbrains.antlayout.datatypes;
-
-import jetbrains.antlayout.util.LayoutFileSet;
-import org.apache.tools.ant.BuildException;
-
-import java.io.File;
-
-/**
- * @author max
- */
-public class IdeaModule extends LayoutFileSet {
- private String name;
-
- public IdeaModule() {
- }
-
- public IdeaModule(IdeaModule fileset) {
- super(fileset);
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- setDir(getMainOutput());
- }
-
- private File getMainOutput() {
- String common = getProject().getProperty("modules.output");
- if (common != null) {
- return new File(new File(common), "production/" + name);
- }
-
- String adhoc = getProject().getProperty("module." + name +".output.main");
- return adhoc != null ? new File(adhoc) : null;
- }
-
- public void validateArguments() throws BuildException {
- if (name == null) {
- throw new BuildException("name attribute must be specified for moduleentry");
- }
-
- File main = getMainOutput();
-
- if (main == null || !main.exists()) {
- throw new BuildException("No production output found for module " + name +
- ". Either modules.output property references project output that doesn't contain this module or " +
- "module." + name +".output.main is not defined or references non-existing directory.");
- }
- }
-
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- IdeaModule that = (IdeaModule) o;
- return getDir(getProject()).equals(that.getDir(getProject()));
- }
-
- public int hashCode() {
- return getDir(getProject()).hashCode();
- }
-}
+package jetbrains.antlayout.datatypes;
+
+import java.io.File;
+
+/**
+ * @author max
+ */
+public class IdeaModule extends IdeaModuleBase {
+ public IdeaModule() {
+ }
+
+ public IdeaModule(IdeaModule fileset) {
+ super(fileset);
+ }
+
+ protected String getKind() {
+ return "production";
+ }
+
+ protected String getOutputDirProperty() {
+ return "module." + getName() + ".output.main";
+ }
+}
diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModuleBase.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModuleBase.java
new file mode 100644
index 000000000000..ca5becf998cd
--- /dev/null
+++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModuleBase.java
@@ -0,0 +1,70 @@
+package jetbrains.antlayout.datatypes;
+
+import jetbrains.antlayout.util.LayoutFileSet;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.ZipFileSet;
+
+import java.io.File;
+
+/**
+ * @author nik
+ */
+public abstract class IdeaModuleBase extends LayoutFileSet {
+ private String name;
+
+ protected IdeaModuleBase() {
+ }
+
+ public IdeaModuleBase(ZipFileSet fileset) {
+ super(fileset);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ setDir(getOutputDir());
+ }
+
+ protected File getOutputDir() {
+ String common = getProject().getProperty("modules.output");
+ if (common != null) {
+ return new File(new File(common), getKind() + "/" + getName());
+ }
+
+ String adhoc = getProject().getProperty(getOutputDirProperty());
+ return adhoc != null ? new File(adhoc) : null;
+ }
+
+ public void validateArguments() throws BuildException {
+ if (name == null) {
+ throw new BuildException("name attribute must be specified for module entry");
+ }
+
+ File outputDir = getOutputDir();
+
+ if (outputDir == null || !outputDir.exists()) {
+ throw new BuildException("No " + getKind() + " output found for module " + name +
+ ". Either modules.output property references project output that doesn't contain this module or " +
+ getOutputDirProperty() + " is not defined or references non-existing directory.");
+ }
+ }
+
+ protected abstract String getOutputDirProperty();
+
+ protected abstract String getKind();
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ IdeaModule that = (IdeaModule) o;
+ return getDir(getProject()).equals(that.getDir(getProject()));
+ }
+
+ public int hashCode() {
+ return getDir(getProject()).hashCode();
+ }
+}
diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModuleTests.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModuleTests.java
new file mode 100644
index 000000000000..10abc63abd64
--- /dev/null
+++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/IdeaModuleTests.java
@@ -0,0 +1,27 @@
+package jetbrains.antlayout.datatypes;
+
+import org.apache.tools.ant.types.ZipFileSet;
+
+import java.io.File;
+
+/**
+ * @author nik
+ */
+public class IdeaModuleTests extends IdeaModuleBase {
+ public IdeaModuleTests() {
+ }
+
+ public IdeaModuleTests(ZipFileSet fileset) {
+ super(fileset);
+ }
+
+ @Override
+ protected String getOutputDirProperty() {
+ return "module." + getName() + ".output.test";
+ }
+
+ @Override
+ protected String getKind() {
+ return "test";
+ }
+}
diff --git a/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java b/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java
index 01d490cbb6fb..ad1d02c664a0 100644
--- a/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java
+++ b/jps/antLayout/src/jetbrains/antlayout/datatypes/JarContainer.java
@@ -1,46 +1,50 @@
-package jetbrains.antlayout.datatypes;
-
-import org.apache.tools.ant.taskdefs.Zip;
-import org.apache.tools.ant.taskdefs.Jar;
-import org.apache.tools.ant.taskdefs.Manifest;
-import org.apache.tools.ant.taskdefs.ManifestException;
-import org.apache.tools.ant.types.ZipFileSet;
-import org.apache.tools.ant.types.Path;
-
-import java.io.File;
-
-/**
- * @author max
- */
-public class JarContainer extends ZipContainer {
- protected Zip createTask() {
- Jar task = new Jar();
- task.setTaskName("jar");
- task.setWhenmanifestonly((Zip.WhenEmpty) Zip.WhenEmpty.getInstance(Zip.WhenEmpty.class, "skip"));
- return task;
- }
-
- public void setIndex(boolean flag) {
- ((Jar) task).setIndex(flag);
- }
-
- public void setManifestEncoding(String manifestEncoding) {
- ((Jar) task).setManifestEncoding(manifestEncoding);
- }
-
- public void setManifest(File manifestFile) {
- ((Jar) task).setManifest(manifestFile);
- }
-
- public void setFilesetmanifest(Jar.FilesetManifestConfig config) {
- ((Jar) task).setFilesetmanifest(config);
- }
-
- public void addConfiguredManifest(Manifest newManifest) throws ManifestException {
- ((Jar) task).addConfiguredManifest(newManifest);
- }
-
- public void addMetainf(ZipFileSet fs) {
- ((Jar) task).addMetainf(fs);
- }
-}
+package jetbrains.antlayout.datatypes;
+
+import org.apache.tools.ant.Main;
+import org.apache.tools.ant.taskdefs.*;
+import org.apache.tools.ant.types.ZipFileSet;
+
+import java.io.File;
+
+/**
+ * @author max
+ */
+public class JarContainer extends ZipContainer {
+ protected Zip createTask() {
+ String version = Main.getAntVersion();
+ Jar task;
+ if (version != null && (version.indexOf("1.8.0") != -1 || version.indexOf("1.8.1") != -1)) {
+ task = new PatchedJar();
+ }
+ else {
+ task = new Jar();
+ }
+ task.setTaskName("jar");
+ task.setWhenmanifestonly((Zip.WhenEmpty) Zip.WhenEmpty.getInstance(Zip.WhenEmpty.class, "skip"));
+ return task;
+ }
+
+ public void setIndex(boolean flag) {
+ ((Jar) task).setIndex(flag);
+ }
+
+ public void setManifestEncoding(String manifestEncoding) {
+ ((Jar) task).setManifestEncoding(manifestEncoding);
+ }
+
+ public void setManifest(File manifestFile) {
+ ((Jar) task).setManifest(manifestFile);
+ }
+
+ public void setFilesetmanifest(Jar.FilesetManifestConfig config) {
+ ((Jar) task).setFilesetmanifest(config);
+ }
+
+ public void addConfiguredManifest(Manifest newManifest) throws ManifestException {
+ ((Jar) task).addConfiguredManifest(newManifest);
+ }
+
+ public void addMetainf(ZipFileSet fs) {
+ ((Jar) task).addMetainf(fs);
+ }
+}
diff --git a/jps/antLayout/src/jetbrains/antlayout/tasks/LayoutTask.java b/jps/antLayout/src/jetbrains/antlayout/tasks/LayoutTask.java
index 62c7557156c8..2ccb269349a8 100755
--- a/jps/antLayout/src/jetbrains/antlayout/tasks/LayoutTask.java
+++ b/jps/antLayout/src/jetbrains/antlayout/tasks/LayoutTask.java
@@ -41,6 +41,10 @@ public class LayoutTask extends Task {
containers.add(new FileSetContainer(module));
}
+ public void addModuleTests(IdeaModuleTests module) {
+ containers.add(new FileSetContainer(module));
+ }
+
public void addFileset(FileSet fileSet) {
containers.add(new FileSetContainer(fileSet));
}
diff --git a/jps/antLayout/src/org/apache/tools/ant/taskdefs/PatchedJar.java b/jps/antLayout/src/org/apache/tools/ant/taskdefs/PatchedJar.java
new file mode 100644
index 000000000000..4c77d5361c98
--- /dev/null
+++ b/jps/antLayout/src/org/apache/tools/ant/taskdefs/PatchedJar.java
@@ -0,0 +1,83 @@
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Jar;
+import org.apache.tools.ant.taskdefs.Zip;
+import org.apache.tools.ant.types.ArchiveFileSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+
+import java.io.File;
+
+/**
+ * This class is added to workaround https://issues.apache.org/bugzilla/show_bug.cgi?id=49605
+ * in Ant 1.8.0 and 1.8.1
+ *
+ * @author nik
+ */
+public class PatchedJar extends Jar {
+ private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+
+ @Override
+ protected ArchiveState getResourcesToAdd(ResourceCollection[] rcs, File zipFile, boolean needsUpdate) throws BuildException {
+ if (skipWriting) {
+ // this pass is only there to construct the merged
+ // manifest this means we claim an update was needed and
+ // only include the manifests, skipping any uptodate
+ // checks here defering them for the second run
+ Resource[][] manifests = grabManifests(rcs);
+ int count = 0;
+ for (int i = 0; i < manifests.length; i++) {
+ count += manifests[i].length;
+ }
+ log("found a total of " + count + " manifests in "
+ + manifests.length + " resource collections",
+ Project.MSG_VERBOSE);
+ return new ArchiveState(true, manifests);
+ }
+
+ return super.getResourcesToAdd(rcs, zipFile, needsUpdate);
+ }
+
+ /**
+ * This method is copied from Jar class in Ant 1.8.2
+ */
+ private Resource[][] grabManifests(ResourceCollection[] rcs) {
+ Resource[][] manifests = new Resource[rcs.length][];
+ for (int i = 0; i < rcs.length; i++) {
+ Resource[][] resources = null;
+ if (rcs[i] instanceof FileSet) {
+ resources = grabResources(new FileSet[] {(FileSet) rcs[i]});
+ } else {
+ resources = grabNonFileSetResources(new ResourceCollection[] {
+ rcs[i]
+ });
+ }
+ for (int j = 0; j < resources[0].length; j++) {
+ String name = resources[0][j].getName().replace('\\', '/');
+ if (rcs[i] instanceof ArchiveFileSet) {
+ ArchiveFileSet afs = (ArchiveFileSet) rcs[i];
+ if (!"".equals(afs.getFullpath(getProject()))) {
+ name = afs.getFullpath(getProject());
+ } else if (!"".equals(afs.getPrefix(getProject()))) {
+ String prefix = afs.getPrefix(getProject());
+ if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
+ prefix += "/";
+ }
+ name = prefix + name;
+ }
+ }
+ if (name.equalsIgnoreCase(MANIFEST_NAME)) {
+ manifests[i] = new Resource[] {resources[0][j]};
+ break;
+ }
+ }
+ if (manifests[i] == null) {
+ manifests[i] = new Resource[0];
+ }
+ }
+ return manifests;
+ }
+}
diff --git a/jps/lib/javac2-all.jar b/jps/lib/javac2-all.jar
index 5f7f2ae671cb..703863d76e14 100644
Binary files a/jps/lib/javac2-all.jar and b/jps/lib/javac2-all.jar differ
diff --git a/jps/plugins/appLauncher/appLauncher.iml b/jps/plugins/appLauncher/appLauncher.iml
new file mode 100644
index 000000000000..9d7005eff21a
--- /dev/null
+++ b/jps/plugins/appLauncher/appLauncher.iml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jps/plugins/appLauncher/src/META-INF/services/org.jetbrains.jps.runConf.RunConfigurationLauncherService b/jps/plugins/appLauncher/src/META-INF/services/org.jetbrains.jps.runConf.RunConfigurationLauncherService
new file mode 100644
index 000000000000..fb3c75f1c264
--- /dev/null
+++ b/jps/plugins/appLauncher/src/META-INF/services/org.jetbrains.jps.runConf.RunConfigurationLauncherService
@@ -0,0 +1 @@
+org.jetbrains.jps.runConf.java.JavaAppLauncher
\ No newline at end of file
diff --git a/jps/plugins/appLauncher/src/org/jetbrains/jps/runConf/java/JavaAppLauncher.groovy b/jps/plugins/appLauncher/src/org/jetbrains/jps/runConf/java/JavaAppLauncher.groovy
new file mode 100644
index 000000000000..08f38b1daf52
--- /dev/null
+++ b/jps/plugins/appLauncher/src/org/jetbrains/jps/runConf/java/JavaAppLauncher.groovy
@@ -0,0 +1,24 @@
+package org.jetbrains.jps.runConf.java
+
+import org.jetbrains.jps.RunConfiguration
+
+class JavaAppLauncher extends JavaBasedRunConfigurationLauncher {
+ public JavaAppLauncher() {
+ super("Application")
+ }
+
+ @Override
+ String getMainClassName(RunConfiguration runConf) {
+ return runConf.allOptions["MAIN_CLASS_NAME"]
+ }
+
+ @Override
+ String getMainClassArguments(RunConfiguration runConf) {
+ return runConf.allOptions["PROGRAM_PARAMETERS"]
+ }
+
+ @Override
+ List getMainClassClasspath(RunConfiguration runConf) {
+ return Collections.emptyList()
+ }
+}
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/compiler.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/compiler.xml
new file mode 100644
index 000000000000..97c9ca5c8b0a
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/compiler.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/misc.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/misc.xml
new file mode 100644
index 000000000000..482d866a4028
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/misc.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/modules.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/modules.xml
new file mode 100644
index 000000000000..758808d8019b
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClass.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClass.xml
new file mode 100644
index 000000000000..9d98446bbf0b
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClass.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassComplexArgs.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassComplexArgs.xml
new file mode 100644
index 000000000000..07bae55f2566
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassComplexArgs.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassEnvVars.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassEnvVars.xml
new file mode 100644
index 000000000000..50b5f6ede5f7
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassEnvVars.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassProperties.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassProperties.xml
new file mode 100644
index 000000000000..4bd61c1e4cf5
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassProperties.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassVmArgs.xml b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassVmArgs.xml
new file mode 100644
index 000000000000..0d093fdbe0e4
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/.idea/runConfigurations/MainClassVmArgs.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/main-class-run-conf.iml b/jps/plugins/appLauncher/testData/main-class-run-conf/main-class-run-conf.iml
new file mode 100644
index 000000000000..d5c074327501
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/main-class-run-conf.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClass.java b/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClass.java
new file mode 100644
index 000000000000..b4f0cb1d6d33
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClass.java
@@ -0,0 +1,8 @@
+
+public class MainClass {
+ public static void main(String[] args) {
+ for (String arg: args) {
+ System.out.println(arg);
+ }
+ }
+}
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClassEnvVars.java b/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClassEnvVars.java
new file mode 100644
index 000000000000..2e48ab455e4e
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClassEnvVars.java
@@ -0,0 +1,8 @@
+import java.lang.System;
+
+public class MainClassEnvVars {
+ public static void main(String[] args) {
+ System.out.println(System.getenv("ENV1"));
+ System.out.println(System.getenv("ENV2"));
+ }
+}
diff --git a/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClassProperties.java b/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClassProperties.java
new file mode 100644
index 000000000000..5f19e3ca4829
--- /dev/null
+++ b/jps/plugins/appLauncher/testData/main-class-run-conf/src/MainClassProperties.java
@@ -0,0 +1,9 @@
+import java.lang.System;
+
+public class MainClassProperties {
+ public static void main(String[] args) {
+ System.out.println(System.getProperty("my.prop1"));
+ System.out.println(System.getProperty("my.prop2"));
+ System.out.println(System.getProperty("my.prop3"));
+ }
+}
diff --git a/jps/plugins/appLauncher/testSrc/org/jetbrains/jps/runConf/java/JavaAppLauncherTest.groovy b/jps/plugins/appLauncher/testSrc/org/jetbrains/jps/runConf/java/JavaAppLauncherTest.groovy
new file mode 100644
index 000000000000..ee7fe78a51f8
--- /dev/null
+++ b/jps/plugins/appLauncher/testSrc/org/jetbrains/jps/runConf/java/JavaAppLauncherTest.groovy
@@ -0,0 +1,72 @@
+package org.jetbrains.jps.runConf.java
+
+import org.jetbrains.jps.JpsBuildTestCase
+import org.jetbrains.jps.Project
+import org.jetbrains.jps.RunConfiguration
+import org.jetbrains.jps.util.FileUtil
+
+class JavaAppLauncherTest extends JpsBuildTestCase {
+ public void test_simple() {
+ runAndAssertOutput("MainClass", null, { output ->
+ assertTrue(output, output.indexOf("arg1" + System.getProperty("line.separator")) != -1)
+ assertTrue(output, output.indexOf("arg2") != -1)
+ })
+ }
+
+ public void test_properties() {
+ runAndAssertOutput("MainClassProperties", {JavaBasedRunConfigurationLauncher launcher ->
+ launcher.addSystemProperties(["my.prop1" : "val1", "my.prop2" : "val2"])
+ }, { output ->
+ assertTrue(output, output.indexOf("val1" + System.getProperty("line.separator")) != -1)
+ assertTrue(output, output.indexOf("val2") != -1)
+ })
+ }
+
+ public void test_env_vars() {
+ runAndAssertOutput("MainClassEnvVars", null, { output ->
+ assertTrue(output, output.indexOf("val1" + System.getProperty("line.separator")) != -1)
+ assertTrue(output, output.indexOf("val2") != -1)
+ })
+ }
+
+ public void test_jvm_args() {
+ runAndAssertOutput("MainClassVmArgs", null, { output ->
+ assertTrue(output, output.indexOf("val1" + System.getProperty("line.separator")) != -1)
+ assertTrue(output, output.indexOf("val2") != -1)
+ assertTrue(output, output.indexOf("plugins/appLauncher/testData/main-class-run-conf") != -1)
+ })
+ }
+
+ public void test_args_with_spaces() {
+ runAndAssertOutput("MainClassComplexArgs", null, { output ->
+ assertTrue(output, output.indexOf("argument with space1" + System.getProperty("line.separator")) != -1)
+ assertTrue(output, output.indexOf("argument with space2" + System.getProperty("line.separator")) != -1)
+ assertTrue(output, output.indexOf("aa") != -1)
+ assertTrue(output, output.indexOf("bb") != -1)
+ })
+ }
+
+ private void runAndAssertOutput(String runConfName, Closure launcherInitializer, Closure assertions) {
+ Project project = loadProject("plugins/appLauncher/testData/main-class-run-conf", [:]);
+
+ RunConfiguration runConf = project.runConfigurations[runConfName];
+
+ project.targetFolder = createTempDir().absolutePath;
+
+ project.makeAll();
+
+ File outFile = createTempFile();
+
+ JavaAppLauncher launcher = new JavaAppLauncher();
+ launcher.setOutputFile(outFile);
+
+ if (launcherInitializer != null) {
+ launcherInitializer(launcher);
+ }
+
+ launcher.start(runConf);
+
+ String output = FileUtil.loadFileText(outFile);
+ assertions(output);
+ }
+}
diff --git a/jps/plugins/gwt/gwt.iml b/jps/plugins/gwt/gwt.iml
index 8a514adfdbda..54e5a9f02098 100644
--- a/jps/plugins/gwt/gwt.iml
+++ b/jps/plugins/gwt/gwt.iml
@@ -4,12 +4,22 @@
+
+
+
+
+
+
+
+
+
+
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();
+ }
+}