1. Command-line options conflict check.

2. Fixed bugs in exclude processing.
3. Fixed bug in dependency processing.
4. Empty output folder use case workaround implemented.
This commit is contained in:
Dmitri Boulytchev
2010-12-03 20:46:04 +03:00
parent 7fca8c5d36
commit 8e7180e6f8
9 changed files with 268 additions and 107 deletions
+9 -1
View File
@@ -14,7 +14,15 @@
<orderEntry type="library" name="Javac2" level="project" />
<orderEntry type="module" module-name="antlayout" />
<orderEntry type="library" scope="TEST" name="JUnit" level="project" />
<orderEntry type="library" exported="" name="Gant" level="application" />
<orderEntry type="module-library">
<library name="Gant">
<CLASSES>
<root url="jar:///usr/share/groovy/lib/gant-1.8.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>
@@ -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;
}
};
}
+86 -34
View File
@@ -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<String> 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: ??? <options> <project-specifier>\n");
System.out.println("Options are:");
@@ -81,7 +129,7 @@ public class Main {
final List<String> 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();
@@ -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;
}
}
+4 -4
View File
@@ -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();
}
}
@@ -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);
}
}
+32 -17
View File
@@ -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<Module> modules = new ArrayList<Module>();
for (Map.Entry<String, ModuleStatus> 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<Module> initial, final boolean tests) {
final Set<Module> modules = new HashSet<Module>();
final Map<Module, Set<Module>> reversedDependencies = new HashMap<Module, Set<Module>> ();
for (Module m : myProject.getModules().values()) {
for (Module.ModuleDependency mdep : m.getDependencies()) {
final ClasspathItem cpi = mdep.getItem();
if (cpi instanceof Module) {
Set<Module> sm = reversedDependencies.get(cpi);
if (sm == null) {
sm = new HashSet<Module> ();
reversedDependencies.put((Module) cpi, sm);
}
sm.add(m);
}
}
}
new Object() {
public void run(final List<Module> initial) {
public void run(final Collection<Module> initial) {
if (initial == null)
return;
for (Module module : initial) {
if (modules.contains(module))
return;
continue;
modules.add(module);
final List<Module> successors = new ArrayList<Module>();
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;
}
+74
View File
@@ -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());
}
}
+44 -26
View File
@@ -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<Module> modules, boolean tests) {
buildModules (modules, tests)
public def buildSelected(Collection<Module> modules, boolean tests) {
buildModules(modules, tests)
}
public def buildProduction() {
@@ -84,12 +85,12 @@ class ProjectBuilder {
listeners*.onBuildFinished(project)
}
private def clearChunks (Collection<Module> modules) {
private def clearChunks(Collection<Module> 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<Module> 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<String> chunkSources = filterNonExistingFiles(tests ? chunk.testRoots : chunk.sourceRoots, true)
if (chunkSources.isEmpty()) return
@@ -202,21 +207,21 @@ class ProjectBuilder {
List chunkDependenciesSourceRoots = transitiveModuleDependenciesSourcePaths(chunk, tests)
Map<ModuleBuildState, ModuleChunk> states = new HashMap<ModuleBuildState, ModuleChunk>()
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<String> 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<String> set, Set<Object> processed) {
if (processed.contains(chunkOrModule)) return
processed << chunkOrModule
chunkOrModule.getClasspath(classpathKind).each {
if (it instanceof Module) {
collectPathTransitively(it, collectSources, classpathKind, set, processed)