views:

132

answers:

4
for n in `cd src; find . -name "*.java"; cd -`;
  do a=`echo $n | cut -d '.' -f2`;
  if [[ src/$a.java -nt build/$a.class ]];
    then echo src/$a.java;
  fi;
done

It lists all the java files in the src tree; then for each one, it removes the suffix ".java" (cut -d '.' -f2 because find . output is prefixed with .). It then uses -nt to test if the java file in the src tree is newer than the corresponding class file in the build tree; if it is newer, it is output. [javac can then use this to compile only the needed src files, instead of using ant or make]

The problem is that it is too slow, taking about 275ms. How to make it faster?

Or is there a faster way to do this in bash? I can't see how to do it with find, diff, rsync nor make (which doesn't seem to traverse trees automatically, and needs explicitly listed source files).

+1  A: 

try this:

for f in src/*.java; do 
  file=`basename $f`; 
  if [[ src/$file -nt build/${file//.java/} ]]; then 
     echo src/$file; 
  fi; 
done
kon
That won't recursively process subdirs of src though.
martin clayton
I think you mean ${file/.java/.class} in the substitution?
Douglas Leeder
Thanks, I like the `basename $f` and `${file//.java/}` - I didn't know them! In the latter, I think you meant `build/${file/.java/.class}`. But `src/*.java` and `basename` only cover one level, not a src tree (e.g. nested packages).
13ren
Sorry @martin and @Douglas, I didn't refresh before commenting. At least we're in agreement. :-)
13ren
A: 

Adopting kon's approach to the filename munging, the speed improves from 320ms to 45ms on average. Thanks kon!

for n in `cd src; find . -name "*.java"; cd -`; do
  if [[ src/$n -nt build/${n/.java/.class} ]];
     then echo src/$n;
  fi;
done

The original is a bit slower now (was 275ms; now 320ms); I don't know why. I'm using the same line. Maybe different system sources after playing a video.

EDIT re rst's comment: mangling the "src/" away instead of cd src; ... cd -;. Note that both $n and $n2 are used [you can't nest the ${var/A/B} construct, can you?]

for n in `find src -name "*.java"`; do
  n2=${n/src/}
  if [[ $n -nt build/${n2/.java/.class} ]];
     then echo $n;
  fi;
done
13ren
I don't think the `; cd -` is needed here.
rsp
@rsp I agree. The `; cd -` reverses the `cd src;`, which was needed so the same path fragment could appear in both src and build trees. I'll update this answer with name mangling instead.
13ren
The reason the `; cd -` is not needed is that the `cd src; find...` are executed in a subshell. When that exits you're still in the parent of `src`.
Dennis Williamson
@Dennis oh, I see now. Thanks. :-)
13ren
A: 

ant performs smart building logic, it will not build a class unless the source was modified.

You might also like to look into scons which is not a native Java build tool but is very handy at compiling efficient complex build trees and does have java builder as well.

Maxim Veksler
I used to use ant, but it's too slow. I've hacked together my own build tool, which cuts compile time from 5000ms to 250ms (on average). This question is about a refinement that should only add a few ms to that. I just looked at scons, but it doesn't have performance figures in the faq. I'll keep an eye on it though; thanks.
13ren
I just tried scons; it generates javac commands, which isn't what I need. Also, I had a lot of trouble configuring it: the examples from the manual and `man scons` don't work as is, and in the end it couldn't find javac to compile. Even though I configured the Environment with the tool 'javac' and passed in the JAVA_HOME env variable. I mention this because I'm guessing it doesn't get a lot of use as a java tool, considering ant, IDE's and it's written in python. Finally, the fastest run I got was 1121ms (that's generating the javac command, not running it). Faster than ant, but not for me.
13ren
+1  A: 

I don't know if this structure would be any faster, but it might be worth a try:

while read n
do
     # capture the basename all at once
     [[ $n =~ src/(.*)\.java ]]   # escape the parentheses for Bash < 3.2 (or thereabouts)
     [[ $n -nt "build/${BASH_REMATCH[1]}.class" ]] && echo $n
done < <(find src -name "*.java")

The while form probably won't provide any boost. The collapsed if may. The regex might provide a small contribution.

As you've found out, eliminating a call to cut has made the biggest difference.

Dennis Williamson
thanks, though I'm getting `syntax error near `src/(.'`.
13ren
what bash version are you using? maybe it doesn't support the binary operator =~
kon
`=~` was introduced in 3.0 and there were changes that affect compatibility somewhere around 3.1 or 3.2
Dennis Williamson
Please note that I had the wrong index to $BASH_REMATCH. It's now fixed.
Dennis Williamson
@13ren: Try escaping the parantheses: `[[ $n =~ src/\(.*\)\.java ]]` if you have Bash < 3.2
Dennis Williamson
@Dennis bash version 3.1.17(1) here. Escaping the parentheses fixed the syntax error. It's not listing the newer files though... it's now working with added `.class` to `"build/${BASH_REMATCH[1]}.class"` and the `cd src` moved into the `find`, as in: `(find src -name "*.java")`. It's averaging around 65ms - much faster than the original; but not as fast as the 45ms one above.
13ren
I edited my answer to match your comment.
Dennis Williamson