Quoth the raven, Nevermore.

I felt this classic line from Edgar Allen Poe’s The Raven was appropriate to start off my review of Raven “the build tool for Java programs based on Rake and Ruby Gems”. Now that my eyes have been opened to the world of using dynamic scripting languages to build Java projects I can safely say that I want to use classic Java build tools namely Ant, and Maven…Nevermore.

Raven, as I stated above, is a build tool for Java programs written in the Ruby language. It is essentially a set of RakeTasks that helps you build Java projects. If you understand Rake you should be able to use Raven. To handle dependencies Raven has chosen to use gem repositories. Much like Maven has ibiblio.org, Raven has it’s own public gem repository. This is important because it allows you to look in a central place to get common libraries (e.g. log4j, apache commons). If you don’t understand Rake or Ruby don’t worry, the syntax is super simple and you should be able to pick it up very quickly. Before I go on I would also like to discourage people from treating this as learning a new language, like learning a new Java, or C++. It is simply learning a new Domain Special Language, Although you get the full benefit of Ruby if you need it. The basic tasks require little to no understanding of how the Ruby language itself works.

Now for the review. I will cover each topic I mentioned in my previous post then go into any further detail I believe is necessary to show the strengths or weaknesses of Raven. In each section I will briefly describe how to accomplish the task, followed by any code necessary to make the test work.

Compile Java

Compiling Java is a trivial task, and Raven keeps it trivial. If your project follows an apache structure, meaning your Java source is under project_root/src/main/java then you simple have to add and name the command to your Rakefile like so:

javac :compile

Where javac is the command and compile is the name. That seems pretty simple, but lets say you don’t follow the standard folder structure, maybe you place your source in project_root/src/java. Then you code would look something like this


javac :compile do |task|
  task.build_path << 'src/java'
end

For those of you unfamiliar with Ruby I just passed a block to the task. In that block I append a directory to the build_path variable. When you pass a block to a function you typically define some sort of variable. In this case I defined task, this is basically an input to the block, and for this input I will be receiving a handle to the RakeTask that will be performing the javac task. If you look at the RDocs for Raven you will see that build_path is a attribute of JavacTask.

With simplicity and early versions usually comes loss of power, and unfortunately Raven seems a little immature in this area. Meaning, all the javac compiler options are not exposed through the task. This became a problem for me in my tests because I wanted to have Java 1.5 set as my default SDK but for a specific project I needed java to compile using source and target of 1.4.

Before I move on to Packaging I want to make one mention of how you include associate dependencies with a compile task. I will cover defining dependencies a little bit later. To associate a set of dependencies with a compile task you simple add the dependency task as a Rake dependency of your compile task.


javac :compile => :compile_dep do |task|
  task.build_path << 'src/java'
end

In the above example compile_dep is the name of the dependency task I defined elsewhere in my build file. In that dependency task you can define local libraries like jars in a lib directory or remote dependencies from your remote/local gem repository.

To run this task you simple need to type a simple two words at the command line


rake compile

That’s it, that is how easy it is to compile Java code using Raven.

Package compiled Java code into jar

There isn’t much to say about packaging a jar except how easy it is. You simply define the jar task, give the task the name you want the jar to be, and finally set any compile tasks as dependencies that you want included in the jar.


jar 'myJarName.jar' => :compile

Note here that instead of using the a symbol like :compile I choose to just pass a string. Either way is acceptable, I just wanted to show that you could use either.

The only thing I would change about this task myself would be to allow a block to define the name of the jar instead of the task itself. I like using the task name, but I would prefer a shorter syntax at the command line. Using the above example I would have to type something like


rake myJarName.jar

With long jar names like com.danielroop.project1.jar the command gets longer to type and a little more unwieldy. I would propose a syntax like this


jar :package => :compile do |task|
  task.jar_name = 'com.danielroop.project1.jar'
end

You have to write a little more in the build file, but it is a little more self explanatory and the command line syntax looks cleaner. If you look at the source for the flex tasks I define later you will see I allowed for both task definitions.

Compile Flex Source Code into Flash Library (swc)

Raven does not support Flex out of the box, so I implement two flex tasks that make it possible to compile your Flex source using Raven. Dependencies work just like in java, except I only include swc files in the command to the Flex instead of .class files.
The syntax for compiling a swc looks like this


compc :swc => :swc_dep do |task|
  task.src = 'src/flex'
  task.output = 'com.danielroop.project1.swc'
end

Let me break this down piece by piece. The task you are running is compc, I name it this because that is what flex calls there component compiler which is how you build swc files. The task name can be either a custom name like I defined here swc or it can be the name of the swc you want to create. If you have not defined a output variable in your block then the name of the task will be used by default. swc_dep would be the name of a dependency task that that will contain swc files to be included with the compc call. Finally the src value is not necessary if you place your source in src/main/flex. The short syntax would look like this


compc 'com.danielroop.project1.swc' => :swc_dep

Compile Flex Source Code into Flash Application (swf)

This task was another task I have defined in the flex_tasks.rb file. It works almost exactly like compc but you are using the mxmlc compiler instead. The key differences are src should now point to your main mxml file (e.g. Main.mxml) and if output isn’t defined the main mxml file has it’s extention changed to .swf by default. So if your main file was MyApplication.mxml, then output by default would be MyApplication.swf. Your syntax ends up looking like this


mxmlc :swf => :swf_dep do |task|
  task.src = 'Main.mxml'
end

Retrieve and/or Build Dependencies from Other Projects/Third Party Libraries

The final piece of the puzzle is defining your dependencies. Raven does this by creating dependency tasks that are set as dependencies on other tasks as you have seen in the above examples. The dependency task has two collections that can be manipulated to define dependencies libs and deps. The libs property should be used to include local libraries, this would be used if you have a lib directory in your project that you stored all your dependencies in. The deps property is used to define remote depndencies that should be retrieved from a gem repository and unpacked. The syntax ends up looking like so


dependency :compile_dep do |task|
  task.deps << [{'Log4J' => '1.0'}, {'jdom' => '0.9'}]
  task.libs << Dir.glob('../lib/**/*.jar')
end

Here I have requested that log4j.jar version 1.0 and jdom.jar version 0.9 be retrieved from a gem repository. The version number is optional and the string is the project name. I have also told the build file to include all jar files in my projects lib directory as dependencies. I could have been more specific but for this example I wanted to show how easy it was to include a large group of files.

If this were a Flex task the syntax for remote dependencies would be the same, because the swc would just be bundled in the gem instead of the jar, but for the libs section you would change the line to look like this:


task.libs << Dir.glob('../lib/**/*.swc')

That is it, pretty simple. Now for the remote repositories. You can tell Raven where to look for these remote jars/swcs by defining sources at the top of your build file.


set_sources(["http://localhost:2233", "http://gems.rubyraven.org/"])

And finally you can have Raven deploy a remote jar that you have build by using the gem_wrap_inst task. The way this works is you call the command give it a name, and the dependencies should be the items you want included in the gem, lastly you should set the version of the gem. So you end up with something like this


gem_wrap_inst :gem => ['com.danielroop.project1.jar', :swc] do |task|
  task.version = '1.0'
end

Conclusion

That wraps the individual tasks I laid out to discuss. I was a breathe of fresh are from using xml syntax to define this stuff. Although I do believe Raven is a little immature at this point, and the community doesn’t seem very active. Which is not true about the Buildr community which I will be going over next. All in all I think Raven is a better solution than Ant or Maven, and I like how they created libraries to work with Rake instead of changing Rake as you will see Buildr has done. Do not let gem repositories scare you it is essentially a webbrick server that hosts gems, which are just a tar (.tar) that contains two tar balls (tar.gz). So if you rename mygem.gem to mygem.tar you can use winzip or zipgenuis or whatever your favorite unzipper is and see the contents just like you could any other tar file.

This post turned out a lot larger than I anticipated, and I hope the size did not scare you off before you read this line. I apologize if my grammar is bad, I am sure I will read over it later tonight to make some grammatical updates. I will try to get the Buildr post turned around a little quicker than this post. I had a lot of stuff going on lately like playing God of War and attending the inaugural Adogo Meeting.

This entry was posted in build and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *