diff --git a/jps/jps.iml b/jps/jps.iml index 37fa2efd45d3..725fd836a13f 100644 --- a/jps/jps.iml +++ b/jps/jps.iml @@ -14,7 +14,15 @@ - + + + + + + + + + diff --git a/jps/src/org/jetbrains/ether/DirectoryScanner.java b/jps/src/org/jetbrains/ether/DirectoryScanner.java index 1e53416f293a..bc318ae1a0c7 100644 --- a/jps/src/org/jetbrains/ether/DirectoryScanner.java +++ b/jps/src/org/jetbrains/ether/DirectoryScanner.java @@ -89,11 +89,14 @@ public class DirectoryScanner { } if (alternative.length() > 0) { - if (buf.length() > 0) { - buf.append("|("); - buf.append(alternative); - buf.append(')'); - } + alternative.append(".*"); + + if (buf.length() > 0) + buf.append("|"); + + buf.append("("); + buf.append(alternative); + buf.append(')'); } } @@ -102,9 +105,10 @@ public class DirectoryScanner { return new FileFilter() { public boolean accept (File f) { - final Matcher m = patt.matcher(f.getName()); + final Matcher m = patt.matcher(f.getAbsolutePath()); + final boolean ok = !m.matches(); - return !m.matches(); + return ok; } }; } diff --git a/jps/src/org/jetbrains/ether/Main.java b/jps/src/org/jetbrains/ether/Main.java index 872fa8651656..2809ad79383b 100644 --- a/jps/src/org/jetbrains/ether/Main.java +++ b/jps/src/org/jetbrains/ether/Main.java @@ -1,5 +1,6 @@ package org.jetbrains.ether; +import com.sun.corba.se.spi.ior.MakeImmutable; import org.apache.tools.ant.types.Description; import java.util.List; @@ -16,6 +17,13 @@ import java.util.List; public class Main { private static final Options myOptions; + private enum Action { + REBUILD, + MAKE, + CLEAN, + NONE + } + static { final Options.Descriptor[] descrs = { new Options.Descriptor("inspect", "inspect", "i", Options.ArgumentSpecifier.OPTIONAL, "list relevant information for the whole project or specified module"), @@ -25,14 +33,13 @@ public class Main { new Options.Descriptor("make", "make", "m", Options.ArgumentSpecifier.OPTIONAL, "make the whole project or specified module"), new Options.Descriptor("tests", "tests", "t", Options.ArgumentSpecifier.NONE, "make tests as well"), new Options.Descriptor("force", "force", "f", Options.ArgumentSpecifier.NONE, "force actions"), - new Options.Descriptor("help", "help", "h", Options.ArgumentSpecifier.NONE, "show help on options"), - new Options.Descriptor("tests", "tests", "t", Options.ArgumentSpecifier.NONE, "make tests as well") + new Options.Descriptor("help", "help", "h", Options.ArgumentSpecifier.NONE, "show help on options") }; myOptions = new Options(descrs); } - private static boolean doSave () { + private static boolean doSave() { return myOptions.get("save") instanceof Options.Switch; } @@ -64,15 +71,56 @@ public class Main { return myOptions.get("clean") instanceof Options.Switch; } + private static void checkConsistency() { + int test = 0; + + if (doClean()) test++; + if (doMake() != null) test++; + if (doRebuild()) test++; + + if (test > 1) { + System.err.print("WARNING: Conflicting options (should be --make OR --rebuild OR --clean); preferring "); + if (doMake() != null) + System.err.println("make."); + else if (doRebuild()) + System.err.println("rebuild."); + } + + if (doClean() || doRebuild()) { + if (doTests()) { + System.err.println("WARNING: extra --tests option ignored."); + } + if (doForce()) { + System.err.println("WARNING: extra --force option ignored."); + } + } + } + + private static Action getAction() { + if (doMake() != null) { + return Action.MAKE; + } + + if (doRebuild()) + return Action.REBUILD; + + if (doClean()) + return Action.CLEAN; + + return Action.NONE; + } + public static void main(String[] args) { System.out.println("JetBrains.com build server. (C) JetBrains.com, 2010.\n"); final List notes = myOptions.parse(args); for (String note : notes) { - System.err.println("Warning: " + note); + System.err.println("WARNING: " + note); } + checkConsistency(); + if (doHelp()) { System.out.println("Usage: ??? \n"); System.out.println("Options are:"); @@ -81,7 +129,7 @@ public class Main { final List projects = myOptions.getFree(); - if (projects.isEmpty() && ! doHelp()) { + if (projects.isEmpty() && !doHelp()) { System.out.println("Nothing to do; use --help or -h option to see the help.\n"); } @@ -89,39 +137,43 @@ public class Main { final ProjectWrapper project = new ProjectWrapper(prj); boolean saved = false; - if (doClean()) { - System.out.println("Cleaning project \"" + prj + "\""); - project.load(); - project.clean(); - project.save(); - saved = true; - } + switch (getAction()) { + case CLEAN: - if (doRebuild()) { - System.out.println("Rebuilding project \"" + prj + "\""); - project.load(); - project.rebuild(); - project.save(); - saved = true; - } + System.out.println("Cleaning project \"" + prj + "\""); + project.load(); + project.clean(); + project.save(); + saved = true; + break; - final Options.Argument make = doMake(); + case REBUILD: + System.out.println("Rebuilding project \"" + prj + "\""); + project.load(); + project.rebuild(); + project.save(); + saved = true; + break; - if (make instanceof Options.Value) { - final String module = ((Options.Value) make).get(); + case MAKE: + final Options.Argument make = doMake(); - System.out.println("Making module \"" + module + "\" in project \"" + prj + "\""); - project.load(); - project.makeModule(module, doForce(), doTests()); - project.save(); - saved = true; - } - else if (make instanceof Options.Switch) { - System.out.println("Making project \"" + prj + "\""); - project.load(); - project.make(doForce (), doTests()); - project.save(); - saved = true; + if (make instanceof Options.Value) { + final String module = ((Options.Value) make).get(); + + System.out.println("Making module \"" + module + "\" in project \"" + prj + "\""); + project.load(); + project.makeModule(module, doForce(), doTests()); + project.save(); + saved = true; + } else if (make instanceof Options.Switch) { + System.out.println("Making project \"" + prj + "\""); + project.load(); + project.make(doForce(), doTests()); + project.save(); + saved = true; + }; + break; } final Options.Argument inspect = doInspect(); diff --git a/jps/src/org/jetbrains/ether/ModuleStatus.java b/jps/src/org/jetbrains/ether/ModuleStatus.java index 46b072e4cd25..5435bada9639 100644 --- a/jps/src/org/jetbrains/ether/ModuleStatus.java +++ b/jps/src/org/jetbrains/ether/ModuleStatus.java @@ -50,7 +50,13 @@ public class ModuleStatus { System.err.println("Error converting string \"" + s + "\" to ModuleStatus"); } - public boolean isOutdated(){ - return (myOutputStamp <= mySourceStamp) || (myTestOutputStamp <= myTestSourceStamp); + private static boolean wiseCompare (long input, long output) { + final boolean result = (input > 0 && output == Long.MAX_VALUE) || (output <= input); + return result; + } + + public boolean isOutdated(boolean tests) { + final boolean result = wiseCompare(mySourceStamp, myOutputStamp) || (tests && wiseCompare(myTestSourceStamp, myTestOutputStamp)); + return result; } } diff --git a/jps/src/org/jetbrains/ether/Options.java b/jps/src/org/jetbrains/ether/Options.java index 08430c9de529..cc452bad35a6 100644 --- a/jps/src/org/jetbrains/ether/Options.java +++ b/jps/src/org/jetbrains/ether/Options.java @@ -165,7 +165,7 @@ public class Options { callback.update(myKey, SWITCH); break; case MANDATORY: - callback.report("option \"" + arg + "\" requires an argument, discarding"); + callback.report("option \"" + arg + "\" requires an argument, discarding."); } }; @@ -181,7 +181,7 @@ public class Options { switch (myArgumentSpecifier) { case MANDATORY: if (prm == null) - callback.report("option \"" + arg + "\" requires an argument, discarding"); + callback.report("option \"" + arg + "\" requires an argument, discarding."); else callback.update(myKey, new Value (prm)); break; @@ -190,7 +190,7 @@ public class Options { if (prm == null) callback.update(myKey, SWITCH); else - callback.report("option \"" + arg + "\" does not take an argument, omitting"); + callback.report("option \"" + arg + "\" does not take an argument, omitting."); break; case OPTIONAL: @@ -239,7 +239,7 @@ public class Options { recognized |= myDescriptors[i].proceed(cursor, cb); if (!recognized) { - cb.report("unrecognized option \"" + cursor.look() + "\" omitted"); + cb.report("unrecognized option \"" + cursor.look() + "\" omitted."); cursor.shift(); } } diff --git a/jps/src/org/jetbrains/ether/ProjectSnapshot.java b/jps/src/org/jetbrains/ether/ProjectSnapshot.java index 4f37a4c1de8c..421f298272be 100644 --- a/jps/src/org/jetbrains/ether/ProjectSnapshot.java +++ b/jps/src/org/jetbrains/ether/ProjectSnapshot.java @@ -68,22 +68,6 @@ public class ProjectSnapshot { } public boolean structureChanged (final ProjectSnapshot p) { - /* - try { - Writer fo1 = new BufferedWriter(new FileWriter("/home/db/tmp/1.history")); - Writer fo2 = new BufferedWriter(new FileWriter("/home/db/tmp/2.history")); - - fo1.write(p.myProjectStructure); - fo2.write(myProjectStructure); - - fo1.close(); - fo2.close(); - } - catch (IOException e) { - - } - */ - return ! p.myProjectStructure.equals(myProjectStructure); } } diff --git a/jps/src/org/jetbrains/ether/ProjectWrapper.java b/jps/src/org/jetbrains/ether/ProjectWrapper.java index eaa0885631a2..eeeaaff01707 100644 --- a/jps/src/org/jetbrains/ether/ProjectWrapper.java +++ b/jps/src/org/jetbrains/ether/ProjectWrapper.java @@ -1,5 +1,6 @@ package org.jetbrains.ether; +import com.sun.org.apache.xpath.internal.operations.Mod; import org.codehaus.gant.GantBinding; import org.jetbrains.jps.ClasspathItem; import org.jetbrains.jps.Module; @@ -17,7 +18,7 @@ import java.util.*; * To change this template use File | Settings | File Templates. */ public class ProjectWrapper { - // Home direcroty + // Home directory private static final String myHomeDir = System.getProperty("user.home"); // JPS directory @@ -113,7 +114,8 @@ public class ProjectWrapper { if (m == null) { System.out.println("No module \"" + module + "\" found in project \""); } else { - System.out.println("Module " + m.myName + " " + (m.isOutdated() ? "is outdated" : "is up-to-date")); + System.out.println("Module " + m.myName + " " + (m.isOutdated(false) ? "is outdated" : "is up-to-date")); + System.out.println("Module " + m.myName + " tests " + (m.isOutdated(true) ? "are outdated" : "are up-to-date")); } } @@ -140,7 +142,8 @@ public class ProjectWrapper { if (moduleReport) { for (ModuleStatus mh : myPresent.myModuleHistories.values()) { - System.out.println(" module " + mh.myName + " " + (mh.isOutdated() ? "is outdated" : "is up-to-date")); + System.out.println(" module " + mh.myName + " " + (mh.isOutdated(false) ? "is outdated" : "is up-to-date")); + System.out.println(" module " + mh.myName + " tests " + (mh.isOutdated(true) ? "are outdated" : "are up-to-date")); } } } @@ -167,7 +170,7 @@ public class ProjectWrapper { final List modules = new ArrayList(); for (Map.Entry entry : myPresent.myModuleHistories.entrySet()) { - if (entry.getValue().isOutdated()) + if (entry.getValue().isOutdated(tests)) modules.add(myProject.getModules().get(entry.getKey())); } @@ -186,25 +189,37 @@ public class ProjectWrapper { private void makeModules(final List initial, final boolean tests) { final Set modules = new HashSet(); + final Map> reversedDependencies = new HashMap> (); + + for (Module m : myProject.getModules().values()) { + for (Module.ModuleDependency mdep : m.getDependencies()) { + final ClasspathItem cpi = mdep.getItem(); + + if (cpi instanceof Module) { + Set sm = reversedDependencies.get(cpi); + + if (sm == null) { + sm = new HashSet (); + reversedDependencies.put((Module) cpi, sm); + } + + sm.add(m); + } + } + } new Object() { - public void run(final List initial) { + public void run(final Collection initial) { + if (initial == null) + return; + for (Module module : initial) { if (modules.contains(module)) - return; + continue; modules.add(module); - final List successors = new ArrayList(); - - for (Module.ModuleDependency dep : module.getDependencies()) { - final ClasspathItem cpi = dep.getItem(); - - if (cpi instanceof Module) { - successors.add((Module) cpi); - } - } - run(successors); + run(reversedDependencies.get(module)); } } }.run(initial); @@ -230,7 +245,7 @@ public class ProjectWrapper { } final ModuleStatus h = myPresent.myModuleHistories.get(modName); - if (h != null && !h.isOutdated() && !force) { + if (h != null && !h.isOutdated(tests) && !force) { System.out.println("Module \"" + modName + "\" in project \"" + myRoot + "\" is up-to-date."); return; } diff --git a/jps/src/org/jetbrains/ether/Reporter.java b/jps/src/org/jetbrains/ether/Reporter.java new file mode 100644 index 000000000000..7115847889ee --- /dev/null +++ b/jps/src/org/jetbrains/ether/Reporter.java @@ -0,0 +1,74 @@ +package org.jetbrains.ether; + +import org.jetbrains.jps.Module; + +import java.io.*; + +/** + * Created by IntelliJ IDEA. + * User: db + * Date: 03.12.10 + * Time: 19:39 + * To change this template use File | Settings | File Templates. + */ +public class Reporter { + private static final String myOkFlag = ".jps.ok"; + private static final String myFailFlag = ".jps.fail"; + + private static String getSafePath (final String path) { + final File f = new File(path); + + if (! f.exists()) { + f.mkdir(); + } + + return path; + } + + private static String getOkFlag(final Module m) { + return getSafePath (m.getOutputPath()) + File.separator + myOkFlag; + } + + private static String getFailFlag(final Module m) { + return getSafePath (m.getOutputPath()) + File.separator + myFailFlag; + } + + private static String getOkTestFlag(final Module m) { + return getSafePath (m.getTestOutputPath()) + File.separator + myOkFlag; + } + + private static String getFailTestFlag(final Module m) { + return getSafePath(m.getTestOutputPath()) + File.separator + myFailFlag; + } + + private static void write(final String name, final String contents) { + try { + final BufferedWriter out = new BufferedWriter(new FileWriter(name)); + out.write(contents); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void reportBuildSuccess(final Module m, final boolean tests) { + write(getOkFlag(m), "dummy"); + if (tests) { + write(getOkTestFlag(m), "dummy"); + } + } + + public static void reportBuildFailure(final Module m, final boolean tests, final String reason) { + write(getFailFlag(m), reason); + if (tests) { + write(getFailTestFlag(m), reason); + } + } + + public static boolean failureReported (final Module m, final boolean tests) { + final File o = new File(getFailFlag(m)); + final File t = new File(getFailTestFlag(m)); + + return o.exists() || (tests && t.exists()); + } +} diff --git a/jps/src/org/jetbrains/jps/ProjectBuilder.groovy b/jps/src/org/jetbrains/jps/ProjectBuilder.groovy index 290eb722d444..3c691a405d36 100644 --- a/jps/src/org/jetbrains/jps/ProjectBuilder.groovy +++ b/jps/src/org/jetbrains/jps/ProjectBuilder.groovy @@ -7,8 +7,9 @@ import org.jetbrains.jps.listeners.BuildStatisticsListener import org.jetbrains.jps.listeners.DefaultBuildInfoPrinter import org.jetbrains.jps.listeners.JpsBuildListener import org.jetbrains.jps.builders.* +import org.jetbrains.ether.Reporter - /** +/** * @author max */ class ProjectBuilder { @@ -70,8 +71,8 @@ class ProjectBuilder { buildAllModules(true) } - public def buildSelected (Collection modules, boolean tests) { - buildModules (modules, tests) + public def buildSelected(Collection modules, boolean tests) { + buildModules(modules, tests) } public def buildProduction() { @@ -84,12 +85,12 @@ class ProjectBuilder { listeners*.onBuildFinished(project) } - private def clearChunks (Collection modules) { + private def clearChunks(Collection modules) { getChunks(true).getChunkList().each { - if (!modules.intersect(it.modules).isEmpty()) { - clearChunk(it) - } - } + if (!modules.intersect(it.modules).isEmpty()) { + clearChunk(it) + } + } } private def buildModules(Collection modules, boolean includeTests) { @@ -146,7 +147,7 @@ class ProjectBuilder { buildModules(dependencies, includeTests) } - private def clearChunk (ModuleChunk chunk) { + private def clearChunk(ModuleChunk chunk) { if (!project.dryRun) { project.stage("Cleaning module ${chunk.name}") chunk.modules.each {project.cleanModule it} @@ -194,6 +195,10 @@ class ProjectBuilder { } private def compile(ModuleChunk chunk, boolean tests) { + if (chunk.toString().startsWith("ModuleChunk")) { + final String x = ""; + } + List chunkSources = filterNonExistingFiles(tests ? chunk.testRoots : chunk.sourceRoots, true) if (chunkSources.isEmpty()) return @@ -202,21 +207,21 @@ class ProjectBuilder { List chunkDependenciesSourceRoots = transitiveModuleDependenciesSourcePaths(chunk, tests) Map states = new HashMap() def chunkState = new ModuleBuildState( - sourceRoots: chunkSources, - excludes: chunk.excludes, - classpath: chunkClasspath, - moduleDependenciesSourceRoots: chunkDependenciesSourceRoots, + sourceRoots: chunkSources, + excludes: chunk.excludes, + classpath: chunkClasspath, + moduleDependenciesSourceRoots: chunkDependenciesSourceRoots, ) if (arrangeModuleCyclesOutputs) { chunk.modules.each { List sourceRoots = filterNonExistingFiles(tests ? it.testRoots : it.sourceRoots, false) if (!sourceRoots.isEmpty()) { def state = new ModuleBuildState( - sourceRoots: sourceRoots, - excludes: it.excludes, - classpath: chunkClasspath, - targetFolder: createOutputFolder(it.name, it, tests), - moduleDependenciesSourceRoots: chunkDependenciesSourceRoots + sourceRoots: sourceRoots, + excludes: it.excludes, + classpath: chunkClasspath, + targetFolder: createOutputFolder(it.name, it, tests), + moduleDependenciesSourceRoots: chunkDependenciesSourceRoots ) states[state] = new ModuleChunk(it) } @@ -234,15 +239,27 @@ class ProjectBuilder { } listeners*.onCompilationStarted(chunk) - builders().each {ModuleBuilder builder -> - listeners*.onModuleBuilderStarted(builder, chunk) - if (arrangeModuleCyclesOutputs && chunk.modules.size() > 1 && builder instanceof ModuleCycleBuilder) { - ((ModuleCycleBuilder) builder).preprocessModuleCycle(chunkState, chunk, project) + + try { + builders().each {ModuleBuilder builder -> + listeners*.onModuleBuilderStarted(builder, chunk) + if (arrangeModuleCyclesOutputs && chunk.modules.size() > 1 && builder instanceof ModuleCycleBuilder) { + ((ModuleCycleBuilder) builder).preprocessModuleCycle(chunkState, chunk, project) + } + states.keySet().each { + builder.processModule(it, states[it], project) + } + listeners*.onModuleBuilderFinished(builder, chunk) } - states.keySet().each { - builder.processModule(it, states[it], project) + } + catch (Exception e) { + final String reason = e.toString(); + + chunk.modules.each { + Reporter.reportBuildFailure (it, tests, reason) } - listeners*.onModuleBuilderFinished(builder, chunk) + + throw e; } states.keySet().each { @@ -257,6 +274,7 @@ class ProjectBuilder { } chunk.modules.each { + Reporter.reportBuildSuccess (it, tests) project.exportProperty("module.${it.name}.output.${tests ? "test" : "main"}", getModuleOutputFolder(it, tests)) } } @@ -355,7 +373,7 @@ class ProjectBuilder { private def collectPathTransitively(Object chunkOrModule, boolean collectSources, ClasspathKind classpathKind, Set set, Set processed) { if (processed.contains(chunkOrModule)) return processed << chunkOrModule - + chunkOrModule.getClasspath(classpathKind).each { if (it instanceof Module) { collectPathTransitively(it, collectSources, classpathKind, set, processed)