Section 21.3. Using irb


21.2. Using Rake

The rake utility is like a Rubyesque version of the UNIX make utility. Instead of the bizarre "make" syntax that we know and hate, it uses only pure Ruby code. This utility is the work of Jim Weirich and illustrates (perhaps) the first formal instance of a DSL (domain-specific language) in Ruby.

You may see the name spelled "Rake" or "rake." The former is the name of the tool, and the latter is the actual name of the executable itself. It's not worth nitpicking, in my opinion.

The Rake tool is definitely inspired by make, so the terminology used is much the same. We still talk about targets, actions, dependencies, and rules.

The uses of Rake are numerous. If you are working with C, C++, or Java code, you can use it to build your projects. (It will work for other languages too, of course.) You can also use it for generating documentation with RDoc, deploying software, updating a RubyForge project, and many other such tasks.

Not surprisingly, Rake operates on a file of instructions called a rakefile. The rakefile will have a literal filename of Rakefile or rakefile by default. If you want to name it something else, you can use the -f or --rakefile option:

$ rake             # look for 'rakefile' first, then 'Rakefile' $ rake -f myfile   # use 'myfile' instead


The basic "unit of work" in Rake is the task; these are named with Ruby symbols. Every rakefile is understood to have a default task called :default, which will be run if you don't specify a task name.

$ rake           # execute the default task $ rake mytask    # execute 'mytask'


Inside a rakefile, we specify a task by using the task method, passing it a symbol and a block:

task :mytask do   # ... end


The contents of the block are omitted in the preceding code. The "stuff" that goes here we refer to as actions.

An action can be anything and can involve arbitrary Ruby code. Some convenience methods are available for common operations. The sh method (meant to remind us of the UNIX sh executable) will run a system command.

The methods cp, mv, and rm are respectively for copying, moving, and deleting files. (Like the make utility itself, Rake has an unabashed UNIX flavor about it.) There are other such commands; consult online documentation for more information (http://docs.rubyrake.org).

If you prefer to use braces to delimit a block, you can, but the Ruby parser will typically force you to use parentheses around the parameter in that case:

task(:mytask) { do_something }


To continue, let's take a more concrete example. Imagine we have a C program named myprog.c with two other C files associated with it (each with its own header file). In other words, we have these five source files:

myprog.c sub1.c sub1.h sub2.c sub2.h


We want to compile all this together into the executable myprog. This is a multistep process: First we will compile all the .c files; then we will link the resulting .o files together.

Let's begin by using the file method to specify file dependencies:

file "myprog.o" => ["myprog.c"] file "sub1.o" => ["sub1.c", "sub1.h"] file "sub2.o" => ["sub2.c", "sub2.h"] file "myprog" => ["sub1.o", "sub2.o"]


Notice how the file method just takes a hash. It associates a filename with an array of dependent filenames.

Now let's look at building the binary files. We'll take the code we just wrote and extend it a little. If we put a block on the file call, we can associate with the file a set of actions that will produce that file:

file "myprog.o" => ["myprog.c"] do   sh "cc -c -o myprog.o myprog.c" end file "sub1.o" => ["sub1.c", "sub1.h"] do   sh "cc -c -o sub1.o sub1.c" end file "sub2.o" => ["sub2.c", "sub2.h"] do   sh "cc -c -o sub2.o sub2.c" end file "myprog" => ["sub1.o", "sub2.o"] do   sh "cc -o myprog myprog.o sub1.o sub2.o" end


There is some duplication here, but we can get rid of it. As it turns out, Rake has a special facility called a FileList; it understands wildcards (glob patterns) and allows us to work with multiple files at once. Here we find all the .c files and assign the list to a constant SRC. This FileList constant acts much like an array:

SRC = FileList["*.c"]


So we could use a loop to specify our actions; see the following code fragment. (And note that the dependencies are not specified here; Rake is smart enough to combine this information internally if we specify the dependencies elsewhere.)

SRC.each do |src|    obj = src.sub(/.c$/,".o")    file(obj) { sh "cc -c -o #{obj} #{src}" } end


However, it's simpler to use rules, which are another Rake feature (naturally lifted from make):

rule '.o' => '.c' do |target|   sh "cc -c -o #{target.name} #{target.source}" end


A small bit of magic happens here. The source attribute gets set internally, substituting the file extension from the hash key/value information (changing .o to .c in this case).

Now let's do a little more magic. If we require the rake/clean library, the constants CLEAN and CLOBBER (initially empty) and tasks :clean and :clobber are defined for us. These are traditionally named targets; clean will remove the temporary files, and clobber will remove all these and the final executable also.

These arraylike constants have an include method that takes a file glob. This is like an implicit use of FileList.

So our rakefile now looks like this:

require 'rake/clean' CLEAN.include("*.o") CLOBBER.include("myprog") SRC = FileList['*.c'] OBJ = SRC.ext('o') rule '.o' => '.c' do |t|   sh "cc -c -o #{t.name} #{t.source}" end file "hello" => OBJ do   sh "cc -o hello #{OBJ}" end file "myprog.o" => ["myprog.c"] file "sub1.o" => ["sub1.c", "sub1.h"] file "sub2.o" => ["sub2.c", "sub2.h"] task :default => ["myprog"]


Notice how we don't have to specify "clean" and "clobber" tasks explicitly. Also note that a "clobber" implicitly includes a "clean" operation. Finally, note that we specified a default task for the convenience of the person running the rakefile; it's now unnecessary to specify a task in order to compile.

Rake has several useful command-line options. Sometimes you want to test a rakefile without actually doing any (potentially dangerous) operations; the -n or --dry-run options will allow this. The -T option will list all the targets in a rakefile. There are also options controlling library path searching, tracing and logging, and more.

Rake is more complex than I've hinted at here (especially the rules). It's also still evolving. As always, consult the online documentation for the latest information (http://docs.rubyrake.org).




The Ruby Way(c) Solutions and Techniques in Ruby Programming
The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition)
ISBN: 0672328844
EAN: 2147483647
Year: 2004
Pages: 269
Authors: Hal Fulton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net