Today there was a near-total eclipse. Glasgow was overcast and the only evidence was a slight darkness. Just as we thought we were going to miss it the maximum-eclipse (or thereabouts) struggled through, pale and moon-like through a veil of cloud. Then a few minutes later we caught a brighter glimpse.
But this article is about Eclipse the Java IDE or rather my reliance on it.
A couple of weeks ago I posted a strong opinion that the text editor should be a triviality, entirely decoupled from the project, easily replaced on a whim. I put my own practice to the test by not using Eclipse for at least a day.
The first thing I would have to sort out is compiling and running my program. I consider myself proficient in Java so it should be no problem, right? Wrong! After a while playing about with various switches and options, it became clear I had bitten off too much. It was time to follow my own advice and simplify things. I wrote a Hello World program, compiled it, ran it. Easy. I took baby steps building up the complexity of the script until I eventually had something that compiled my java files and ran all the tests. Here are the baby steps:
- Compile and run Hello World.
- Specify an output folder for class files.
- Add a JUnit test to HelloWorld.java.
- Move HelloWorld.java to a package in my application.
- Add a class from my application.
- Add a test suite that knows which classes to run.
- Add classes individually (up to about ten), testing the build after each.
- Add all the libraries I need to the classpath.
- Delete all the existing class files before compilation.
At this point, I had a working build script but every java file was declared explicitly. Javac expands wildcards (but not recursively) so I was able to replace a list of Java files with a much shorter list of directories. Later that was replaced that with a call to the Linux command find, to find all the Java files.
The test suite was more of a problem. I spent a long time googling how to automatically find and run all tests. The long and short of it is JUnit doesn't have this built in, and it's difficult to find all your own classes with reflection. Libraries exist but I'd rather not add another dependency. I've left it as a hard-coded list of classes. When it gets too annoying I'll write a shell script to auto-generate the class list.
Here's the build script in all its glory. I think it's pretty nice and simple for what it does.
set -e ClassPath=bin/:lib/* JavaFiles=`find java/ -name '*.java' -printf '%p '` rm -R --force bin/* javac -cp $ClassPath -d bin/ $JavaFiles java -cp $ClassPath org.junit.runner.JUnitCore cfoley.AllTests
And here's a description of each line:
Tells BASH to stop running the script if any command fails (i.e. a non-zero exit code)
puts my classpath in a variable. It's just the bin and lib directories.
JavaFiles=`find java/ -name '*.java' -printf '%p '`
Puts a list of all the Java files in a variable. This is the ugliest part of the script and I'd like to replace it with something simpler.
rm -R --force bin/*
Cleans the project by deleting all the old class files.
javac -cp $ClassPath -d bin/ $JavaFiles
Compiles all the java files, putting the class files in the bin folder.
java -cp $ClassPath org.junit.runner.JUnitCore cfoley.AllTests
Runs my test suite
A Java developer might wonder why I didn't use Ant or Maven. Freeing the project from an IDE only to shackle it to another tool would be an ironic solution. Ant and Maven do offer other benefits so if the project grows in scope it may be worth making the switch. For now the JDK and the shell do everything I need.
Final thoughts: It was a lot of effort to write this build script but it will be easier next time. I even think that growing a build script alongside the project could be simpler than fiddling with the build path in Eclipse. Even better, I can now easily use any text editor I want which was the original goal of this exercise.
20 March 2015