1. Command-line help.

2. Better serialization (no Serializable)
3. Project snapshot saving in home directory.
4. Excludes supported (not tested yet).
This commit is contained in:
Dmitri Boulytchev
2010-11-26 03:25:22 +03:00
parent d62b4369a5
commit 7fca8c5d36
9 changed files with 386 additions and 141 deletions
@@ -5,6 +5,8 @@ import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by IntelliJ IDEA.
@@ -43,13 +45,81 @@ public class DirectoryScanner {
public long myLatest = 0;
}
public static Result getFiles (final String root) {
private static FileFilter myTrueFilter = new FileFilter() {
public boolean accept (File s) {
return true;
}
};
private static FileFilter createFilter (final List<String> excludes) {
if (excludes == null) {
return myTrueFilter;
}
StringBuffer buf = new StringBuffer();
for (String exclude : excludes) {
StringBuffer alternative = new StringBuffer();
if (exclude != null) {
for (int i = 0; i<exclude.length(); i++) {
final char c = exclude.charAt(i);
switch (c) {
case '.':
case '\\':
case '*':
case '^':
case '$':
case '[':
case '(':
case '|':
alternative.append("\\");
alternative.append(c);
break;
case '?':
alternative.append(".");
break;
default:
alternative.append(c);
}
}
}
if (alternative.length() > 0) {
if (buf.length() > 0) {
buf.append("|(");
buf.append(alternative);
buf.append(')');
}
}
}
if (buf.length() > 0) {
final Pattern patt = Pattern.compile(buf.toString());
return new FileFilter() {
public boolean accept (File f) {
final Matcher m = patt.matcher(f.getName());
return !m.matches();
}
};
}
return myTrueFilter;
}
public static Result getFiles (final String root, final List<String> excludes) {
final Result result = new Result ();
final FileFilter ff = createFilter(excludes);
new Object(){
public void run (File root) {
if (root.exists()) {
final File[] files = root.listFiles();
final File[] files = root.listFiles(ff);
for (int i = 0; i<files.length; i++) {
long t = files[i].lastModified();
+43 -7
View File
@@ -18,18 +18,24 @@ public class Main {
static {
final Options.Descriptor[] descrs = {
new Options.Descriptor("clean", "clean", "c", Options.ArgumentSpecifier.NONE),
new Options.Descriptor("rebuild", "rebuild", "r", Options.ArgumentSpecifier.NONE),
new Options.Descriptor("make", "make", "m", Options.ArgumentSpecifier.OPTIONAL),
new Options.Descriptor("inspect", "inspect", "i", Options.ArgumentSpecifier.OPTIONAL),
new Options.Descriptor("force", "force", "f", Options.ArgumentSpecifier.NONE),
new Options.Descriptor("help", "help", "h", Options.ArgumentSpecifier.NONE),
new Options.Descriptor("tests", "tests", "t", Options.ArgumentSpecifier.NONE)
new Options.Descriptor("inspect", "inspect", "i", Options.ArgumentSpecifier.OPTIONAL, "list relevant information for the whole project or specified module"),
new Options.Descriptor("save", "save", "s", Options.ArgumentSpecifier.NONE, "collect and save project information"),
new Options.Descriptor("clean", "clean", "c", Options.ArgumentSpecifier.NONE, "clean project"),
new Options.Descriptor("rebuild", "rebuild", "r", Options.ArgumentSpecifier.NONE, "rebuild project"),
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")
};
myOptions = new Options(descrs);
}
private static boolean doSave () {
return myOptions.get("save") instanceof Options.Switch;
}
private static boolean doTests() {
return myOptions.get("tests") instanceof Options.Switch;
}
@@ -59,22 +65,36 @@ public class Main {
}
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);
}
if (doHelp()) {
System.out.println("Usage: ??? <options> <project-specifier>\n");
System.out.println("Options are:");
System.out.println(myOptions.memo());
}
final List<String> projects = myOptions.getFree();
if (projects.isEmpty() && ! doHelp()) {
System.out.println("Nothing to do; use --help or -h option to see the help.\n");
}
for (String prj : projects) {
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;
}
if (doRebuild()) {
@@ -82,6 +102,7 @@ public class Main {
project.load();
project.rebuild();
project.save();
saved = true;
}
final Options.Argument make = doMake();
@@ -93,12 +114,14 @@ public class Main {
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;
}
final Options.Argument inspect = doInspect();
@@ -106,9 +129,22 @@ public class Main {
if (inspect instanceof Options.Switch) {
project.load();
project.report();
if (doSave()) {
project.save();
saved = true;
}
} else if (inspect instanceof Options.Value) {
project.load();
project.report(((Options.Value) inspect).get());
if (doSave()) {
project.save();
saved = true;
}
}
if (doSave() && !saved) {
project.load();
project.save();
}
}
}
@@ -1,30 +0,0 @@
package org.jetbrains.ether;
import java.io.Serializable;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: 19.11.10
* Time: 3:40
* To change this template use File | Settings | File Templates.
*/
public class ModuleHistory implements Serializable {
String myName;
long mySourceStamp;
long myOutputStamp;
long myTestSourceStamp;
long myTestOutputStamp;
public ModuleHistory (String name, long ss, long os, long tss, long tos) {
myName = name;
mySourceStamp = ss;
myOutputStamp = os;
myTestSourceStamp = tss;
myTestOutputStamp = tos;
}
public boolean isDirty (){
return (myOutputStamp <= mySourceStamp) || (myTestOutputStamp <= myTestSourceStamp);
}
}
@@ -0,0 +1,56 @@
package org.jetbrains.ether;
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: 19.11.10
* Time: 3:40
* To change this template use File | Settings | File Templates.
*/
public class ModuleStatus {
private static Pattern myPattern = Pattern.compile("([^ ]+) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)");
String myName;
long mySourceStamp;
long myOutputStamp;
long myTestSourceStamp;
long myTestOutputStamp;
public ModuleStatus(String name, long ss, long os, long tss, long tos) {
myName = name;
mySourceStamp = ss;
myOutputStamp = os;
myTestSourceStamp = tss;
myTestOutputStamp = tos;
}
public String getName () {
return myName;
}
public String toString () {
return myName + " " + mySourceStamp + " " + myOutputStamp + " " + myTestSourceStamp + " " + myTestOutputStamp;
}
public ModuleStatus(final String s) {
final Matcher m = myPattern.matcher(s);
if (m.matches()) {
myName = m.group(1);
mySourceStamp = Long.parseLong(m.group(2));
myOutputStamp = Long.parseLong(m.group(3));
myTestSourceStamp = Long.parseLong(m.group(4));
myTestOutputStamp = Long.parseLong(m.group(5));
}
else
System.err.println("Error converting string \"" + s + "\" to ModuleStatus");
}
public boolean isOutdated(){
return (myOutputStamp <= mySourceStamp) || (myTestOutputStamp <= myTestSourceStamp);
}
}
+42 -2
View File
@@ -78,7 +78,7 @@ public class Options {
}
public boolean nextIsArg () {
return (! endOf()) && ! myArgs[myPos+1].startsWith("-");
return (myPos < myLength - 1 && ! myArgs[myPos+1].startsWith("-"));
}
public boolean endOf () {
@@ -97,18 +97,47 @@ public class Options {
private final String myKey;
private final String myLong;
private final String myShort;
private final String myMemo;
private final Pattern myLongPattern;
Descriptor (final String key, final String longForm, final String shortForm, final ArgumentSpecifier as) {
Descriptor (final String key, final String longForm, final String shortForm, final ArgumentSpecifier as, final String memo) {
myArgumentSpecifier = as;
myKey = key;
myLong = longForm;
myShort = shortForm;
myMemo = memo;
myLongPattern = myLong == null ? null : Pattern.compile("^--" + myLong + "(=(.*))?$");
}
public String memo () {
final StringBuffer buf = new StringBuffer();
String longArg = "", shortArg = "";
switch (myArgumentSpecifier) {
case OPTIONAL:
longArg = "[=<argument>]";
shortArg = " [<argument>]";
break;
case NONE:
longArg = "";
shortArg = "";
break;
case MANDATORY:
longArg = "=<argument>";
shortArg = " <argument>";
break;
}
buf.append(" --" + myLong + longArg + ", -" + myShort + shortArg + "\n");
buf.append(" " + myMemo + "\n");
return buf.toString();
}
public boolean proceed (final Cursor cursor, final Callback callback) {
if (cursor.endOf())
return false;
@@ -133,6 +162,7 @@ public class Options {
else
switch (myArgumentSpecifier) {
case OPTIONAL:
callback.update(myKey, SWITCH);
break;
case MANDATORY:
callback.report("option \"" + arg + "\" requires an argument, discarding");
@@ -224,4 +254,14 @@ public class Options {
public List<String> getFree () {
return myFree;
}
public String memo () {
StringBuffer buf = new StringBuffer ();
for (int i = 0; i<myDescriptors.length; i++) {
buf.append(myDescriptors[i].memo() + "\n");
}
return buf.toString();
}
}
@@ -1,42 +0,0 @@
package org.jetbrains.ether;
import java.io.*;
import java.util.List;
import java.util.Map;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: 19.11.10
* Time: 3:05
* To change this template use File | Settings | File Templates.
*/
public class ProjectHistory implements Serializable {
String myProjectStructure;
Map<String, ModuleHistory> myModuleHistories;
public ProjectHistory (final String prjStruct, final Map<String, ModuleHistory> moduleHistories) {
myProjectStructure = prjStruct;
myModuleHistories = moduleHistories;
}
public boolean structureChanged (final ProjectHistory 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);
}
}
@@ -0,0 +1,89 @@
package org.jetbrains.ether;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* Created by IntelliJ IDEA.
* User: db
* Date: 19.11.10
* Time: 3:05
* To change this template use File | Settings | File Templates.
*/
public class ProjectSnapshot {
String myProjectStructure;
Map<String, ModuleStatus> myModuleHistories;
public ProjectSnapshot(final String prjStruct, final Map<String, ModuleStatus> moduleHistories) {
myProjectStructure = prjStruct;
myModuleHistories = moduleHistories;
}
public String toString () {
StringBuffer buf = new StringBuffer();
buf.append(myModuleHistories.size() + "\n");
for (ModuleStatus h : myModuleHistories.values()) {
buf.append(h.toString() + "\n");
}
buf.append(myProjectStructure);
return buf.toString();
}
public ProjectSnapshot(final String s) {
BufferedReader rd = new BufferedReader(new StringReader(s));
try {
final int n = Integer.parseInt(rd.readLine());
myModuleHistories = new HashMap<String, ModuleStatus>();
for (int i = 0; i<n; i++) {
ModuleStatus h = new ModuleStatus(rd.readLine());
myModuleHistories.put(h.getName(), h);
}
StringBuffer buf = new StringBuffer();
while (true) {
final String str = rd.readLine();
if (str == null)
break;
buf.append(str);
buf.append("\n");
}
myProjectStructure = buf.toString();
}
catch (IOException e) {
e.printStackTrace();
}
}
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);
}
}
+70 -41
View File
@@ -17,82 +17,111 @@ import java.util.*;
* To change this template use File | Settings | File Templates.
*/
public class ProjectWrapper {
// Home direcroty
private static final String myHomeDir = System.getProperty("user.home");
// JPS directory
private static final String myJPSDir = ".jps";
// JPS directory initialization
private static void initJPSDirectory () {
final File f = new File(myHomeDir + File.separator + myJPSDir);
if (! f.exists())
f.mkdir();
}
// File separator replacement
private static final char myFileSeparatorReplacement = '-';
// Original JPS Project
private Project myProject;
private final Project myProject;
// Project directory
private String myRoot;
private final String myRoot;
// Some predefined belongings
private final String myHistoryName = ".history";
// Project snapshot file
private final String myProjectSnapshot;
// Project history
private ProjectHistory myHistory;
private ProjectHistory myPresent;
private ProjectSnapshot mySnapshot;
private ProjectSnapshot myPresent;
public ProjectWrapper(final String prjDir) {
myProject = new Project(new GantBinding());
myRoot = prjDir;
myRoot = new File (prjDir).getAbsolutePath();
myProjectSnapshot = myHomeDir + File.separator + myJPSDir + File.separator + myRoot.replace(File.separatorChar, myFileSeparatorReplacement);
}
private String getProjectHistoryFileName() {
return myRoot + File.separator + myHistoryName;
private String getProjectSnapshotFileName() {
return myProjectSnapshot;
}
private ProjectHistory loadHistory() {
ProjectHistory result = null;
private ProjectSnapshot loadSnapshot() {
initJPSDirectory();
ProjectSnapshot result = null;
try {
FileInputStream fis = new FileInputStream(getProjectHistoryFileName());
ObjectInputStream in = new ObjectInputStream(fis);
final String path = getProjectSnapshotFileName();
result = (ProjectHistory) in.readObject();
byte[] buffer = new byte[(int) new File(path).length()];
in.close();
} catch (IOException e) {
//e.printStackTrace();
} catch (ClassNotFoundException c) {
c.printStackTrace();
BufferedInputStream f = new BufferedInputStream(new FileInputStream(path));
f.read(buffer);
f.close();
result = new ProjectSnapshot(new String(buffer));
}
catch (FileNotFoundException e) {
}
catch (IOException e) {
e.printStackTrace();
}
return result;
}
private void saveHistory() {
final ProjectHistory history = HistoryCollector.collectHistory(myProject);
private void saveSnapshot() {
initJPSDirectory();
final ProjectSnapshot snapshot = StatusCollector.collectHistory(myProject);
try {
FileOutputStream fos = new FileOutputStream(getProjectHistoryFileName());
ObjectOutputStream out = new ObjectOutputStream(fos);
BufferedWriter bw = new BufferedWriter(new FileWriter(getProjectSnapshotFileName()));
out.writeObject(history);
out.close();
bw.write(snapshot.toString());
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void load() {
IdeaProjectLoader.loadFromPath(myProject, myRoot);
myHistory = loadHistory();
myPresent = HistoryCollector.collectHistory(myProject);
mySnapshot = loadSnapshot();
myPresent = StatusCollector.collectHistory(myProject);
}
public void report(final String module) {
final ModuleHistory m = myPresent.myModuleHistories.get(module);
final ModuleStatus m = myPresent.myModuleHistories.get(module);
if (m == null) {
System.out.println("No module \"" + module + "\" found in project \"");
} else {
System.out.println("Module " + m.myName + " " + (m.isDirty() ? "is outdated" : "is up-to-date"));
System.out.println("Module " + m.myName + " " + (m.isOutdated() ? "is outdated" : "is up-to-date"));
}
}
private boolean structureChanged() {
if (myHistory == null)
if (mySnapshot == null)
return true;
return myPresent.structureChanged(myHistory);
return myPresent.structureChanged(mySnapshot);
}
public void report() {
@@ -100,7 +129,7 @@ public class ProjectWrapper {
System.out.println("Project \"" + myRoot + "\" report:");
if (myHistory == null) {
if (mySnapshot == null) {
System.out.println(" no project history found");
} else {
if (structureChanged()) {
@@ -110,14 +139,14 @@ public class ProjectWrapper {
}
if (moduleReport) {
for (ModuleHistory mh : myPresent.myModuleHistories.values()) {
System.out.println(" module " + mh.myName + " " + (mh.isDirty() ? "is outdated" : "is up-to-date"));
for (ModuleStatus mh : myPresent.myModuleHistories.values()) {
System.out.println(" module " + mh.myName + " " + (mh.isOutdated() ? "is outdated" : "is up-to-date"));
}
}
}
public void save() {
saveHistory();
saveSnapshot();
}
public void clean() {
@@ -137,13 +166,13 @@ public class ProjectWrapper {
final List<Module> modules = new ArrayList<Module>();
for (Map.Entry<String, ModuleHistory> entry : myPresent.myModuleHistories.entrySet()) {
if (entry.getValue().isDirty())
for (Map.Entry<String, ModuleStatus> entry : myPresent.myModuleHistories.entrySet()) {
if (entry.getValue().isOutdated())
modules.add(myProject.getModules().get(entry.getKey()));
}
if (modules.isEmpty() && !force) {
System.out.println("All module are up-to-date.");
System.out.println("All modules are up-to-date.");
return;
}
@@ -200,8 +229,8 @@ public class ProjectWrapper {
return;
}
final ModuleHistory h = myPresent.myModuleHistories.get(modName);
if (h != null && !h.isDirty() && !force) {
final ModuleStatus h = myPresent.myModuleHistories.get(modName);
if (h != null && !h.isOutdated() && !force) {
System.out.println("Module \"" + modName + "\" in project \"" + myRoot + "\" is up-to-date.");
return;
}
@@ -1,9 +1,6 @@
package org.jetbrains.ether;
import com.sun.tools.javac.util.Pair;
import org.jetbrains.ether.DirectoryScanner;
import org.jetbrains.ether.ModuleHistory;
import org.jetbrains.ether.ProjectHistory;
import org.jetbrains.jps.*;
import org.jetbrains.jps.resolvers.PathEntry;
@@ -16,7 +13,7 @@ import java.util.*;
* Time: 19:57
* To change this template use File | Settings | File Templates.
*/
public class HistoryCollector {
public class StatusCollector {
private static Pair<Long, Long> myDefaultPair = new Pair<Long, Long> (Long.MAX_VALUE, 0l);
private static Pair<Long, Long> join (final Pair<Long, Long> a, final Pair<Long, Long> b) {
@@ -79,9 +76,9 @@ public class HistoryCollector {
listToBuffer(buf, list);
}
private static Pair<Long, Long> directoryToBuffer (StringBuffer buf, final String dir) {
private static Pair<Long, Long> directoryToBuffer (StringBuffer buf, final String dir, final List<String> excludes) {
if (dir != null) {
final DirectoryScanner.Result result = DirectoryScanner.getFiles(dir);
final DirectoryScanner.Result result = DirectoryScanner.getFiles(dir, excludes);
for (String name : prepare (result.myFiles)) {
buf.append(name + "\n");
@@ -93,7 +90,7 @@ public class HistoryCollector {
return myDefaultPair;
}
private static Pair<Long, Long> sourceRootToBuffer (StringBuffer buf, final String name, final List<String> dir) {
private static Pair<Long, Long> sourceRootToBuffer (StringBuffer buf, final String name, final List<String> dir, final List<String> excludes) {
Pair<Long, Long> result = myDefaultPair;
buf.append(name + ":\n");
@@ -101,7 +98,7 @@ public class HistoryCollector {
for (String d : prepare (dir)) {
if (dir != null) {
buf.append(d + ":\n");
result = join (result, directoryToBuffer(buf, d));
result = join (result, directoryToBuffer(buf, d, excludes));
}
}
@@ -127,7 +124,7 @@ public class HistoryCollector {
classPathItemToBuffer(buf, library, false);
}
public static ModuleHistory moduleToBuffer (StringBuffer buf, final Module module) {
public static ModuleStatus moduleToBuffer (StringBuffer buf, final Module module) {
buf.append("Module: " + module.getName() + "\n");
classPathItemToBuffer(buf, module, true);
@@ -138,13 +135,13 @@ public class HistoryCollector {
libraryToBuffer(buf, lib);
}
long ss = sourceRootToBuffer(buf, "SourceRoots", module.getSourceRoots()).snd;
long ss = sourceRootToBuffer(buf, "SourceRoots", module.getSourceRoots(), module.getExcludes()).snd;
buf.append("OutputPath: " + module.getOutputPath() + "\n");
long os = directoryToBuffer(buf, module.getOutputPath()).fst;
long os = directoryToBuffer(buf, module.getOutputPath(), null).fst;
long tss = sourceRootToBuffer(buf, "TestRoots", module.getTestRoots()).snd;
long tss = sourceRootToBuffer(buf, "TestRoots", module.getTestRoots(), module.getExcludes()).snd;
buf.append("TestOutputPath: " + module.getTestOutputPath() + "\n");
long tos = directoryToBuffer(buf, module.getTestOutputPath()).fst;
long tos = directoryToBuffer(buf, module.getTestOutputPath(), null).fst;
buf.append("Dependencies:\n");
for (Module.ModuleDependency dep : module.getDependencies()){
@@ -169,12 +166,12 @@ public class HistoryCollector {
}
}
return new ModuleHistory(module.getName(), ss, os, tss, tos);
return new ModuleStatus(module.getName(), ss, os, tss, tos);
}
public static ProjectHistory collectHistory (final Project prj) {
public static ProjectSnapshot collectHistory (final Project prj) {
StringBuffer buf = new StringBuffer();
Map<String, ModuleHistory> moduleHistories = new HashMap<String, ModuleHistory> ();
Map<String, ModuleStatus> moduleHistories = new HashMap<String, ModuleStatus> ();
for (Library lib : prepare (prj.getLibraries().values(), myLibraryComparator)) {
libraryToBuffer(buf, lib);
@@ -184,6 +181,6 @@ public class HistoryCollector {
moduleHistories.put(mod.getName(), moduleToBuffer(buf, mod));
}
return new ProjectHistory(buf.toString(), moduleHistories);
return new ProjectSnapshot(buf.toString(), moduleHistories);
}
}