Rake Tutorial

July 14, 2009 / category: Ruby / 17 comments

Rake is a build tool written in Ruby, similar to make, Ant and Phing. There is a major difference between Rake and the others, though. Unlike the rest of the tools, Rake does not provide an external DSL (like XML build file in Ant). Instead, all the task are written in pure Ruby. Therefore you gain full flexibility and can take advantage of some nice Ruby features.

What Are The Build Tools?

If you've ever tried to install software from source in the Linux or Unix system, there is a high probability that you have already had contact with make. Installation process usually looks the same. First you change current directory to that containing uncompressed source code, then you type in following commands:

My eBook: “Memoirs of a Software Team Leader”
Read more »


./configure
make
make install

Second and third lines are simply make program invocations. When launched, make first looks for the Makefile. The latter contains information about source files and dependencies between them. make sorts the dependencies topologically and tries to resolve them in proper order. So it goes like this: first, software developer specifies dependencies and then the build tool is responsible for handling them.

make also allows to spare time. If the source code hasn't changed since last compilation, it won't be processed again as it would be pure waste of time.

Build tools can be used not only for source code compilation, but that was infact the task they were created for. In general, build tools are being used to automate tiresome and repeating tasks.

Difference Between Rake and Other Build Tools

As I have already mentioned, there is one major difference between Rake and other build tools. Instead of writing Makefile for make or build.xml for Ant and Phing, you just write lines of Ruby code. You don't have to learn new complicated build tool syntax, inessential if you change one build tool for another.

But that's enough of theory, let's get to practice!

Install Rake

Rake installation is easy as pie, provided that you have Ruby gems in your system. To install Rake, type in following command:

gem install rake

In most cases you're going to need root privileges in order to do that, so you have either to switch to superuser or precede the given command with "sudo ":

sudo gem install rake

Once the Rake gem is installed, we can write our first simple Rake task.

First Rake Task

The most simple way to define a Rake task is to write the following Ruby code:

task :default do
  puts "Hello World!"
end

Rake tasks should always be located in file named rakefile, Rakefile, rakefile.rb or Rakefile.rb. First two forms are most commonly used.

Once the rakefile is saved, you should change current directory to one containing it and run the rake command:

$ rake
(in /home/lukasz/ruby/rake_examples)
Hello World!

Our first Rake task is working!

So, what actually happened? Well, when given rakefile, Rake is looking for tasks which are simply task method invocations. There may be many tasks located in one rakefile. When running Rake from the command line, you can pass name of the task that you want to be executed. If there is no task given, Rake is looking for the default task. That is why our Rake invocation did the job without passing any extra parameters.

Let's try to put many tasks into one rakefile:

task :ring do
  puts "Bell is ringing."
end

task :enter do
  puts "Entering home!"
end

When we try to simply run rake command without passing any parameters, it will end up with an error:

$ rake
(in /home/lukasz/ruby/rake_examples)
rake aborted!
Don't know how to build task 'default'

(See full trace by running task with --trace)

There is no default task in this rakefile, so we have to explicitly pass the task name:

$ rake enter
(in /home/lukasz/rake_examples)
Entering home!
$ rake ring
(in /home/lukasz/rake_examples)
Bell is ringing.

Although new rakefile seems to work, something went wrong. You cannot step into somebody other's house without ringing the bell first (at least - you should not)! And that's where the dependencies come in.

Expressing Dependencies

Let's introduce a slightly modified rakefile:

task :ring do
  puts "Bell is ringing."
end

task :enter => :ring do
  puts "Entering home!"
end

When we try to enter home now, the bell is being rang first:

$ rake enter
(in /home/lukasz/ruby/rake_examples)
Bell is ringing.
Entering home!

Now Rake is responsible for ringing the bell before walking in. You can define way more complicated dependencies and it is Rake that is responsible for solving them. You tell what your needs are and the build tool does the donkey work.

The good news is that dependencies can be specified not only when defining the task, but also later, depending on the run time conditions. After all, we write tasks in Ruby programming language, not a static XML file, remember?

To achieve the same effect with ringing the bell, we could write:

task :ring do
  puts "Bell is ringing."
end

task :enter do
  puts "Entering home!"
end

task :enter => :ring

Effect will be the same.

Describing Tasks

Each task you write may be described in few simple words. Description will not only serve you as an inline comment, it will also appear on the list of available tasks. Let's add descriptions first:

desc 'Ring the bell'
task :ring do
  puts "Bell is ringing."
end

desc 'Enter home'
task :enter => :ring do
  puts "Entering home!"
end

To view list of available tasks, run rake with either -T or --tasks parameter:

$ rake -T
(in /home/lukasz/ruby/rake_examples)
rake enter  # Enter home
rake ring   # Ring the bell

I prefer the shorter form (-T) because it saves me keystrokes, but the decision is up to you.

File Tasks

We told that the build tools were made in order to simplify project compiling process. There are many dependecies between specific files and build tools support them, so does Rake. It allows to define a special type of task - a file task:

file 'products.sql' => 'products.xml' do
  # build SQL INSERT clause and save it in products.sql file,
  # basing on products.xml datafile
end

In order to run a file task, simply type in the output file name:

$ rake products.sql

File task is - in general - not different than a regular task. The point is that it won't run if the input file (products.xml in this case) is not present. It also won't run if the result file (products.sql) is not older than the input file. If you don't accept this behaviour, i.e. you need to regenerate the output file every time the task is being run, use a regular task instead.

FileUtils

Rake is including the FileUtils module, which provides a set of Unix-like file operating methods including mkdir, rmdir, cp, mv, chmod and touch.

Because FileUtils module is already included, you can call its methods directly, without using the scope operator:

task :manipulate_files do
  mkdir 'new_dir'
  mv 'new_dir', 'lukasz'
  chmod 0777, 'lukasz'
  touch 'lukasz/wrobel.txt'
  rm_rf 'lukasz'
end

If you are familiar with Linux/Unix command shell, you will learn how to use the FileUtils module in no time.

By the way, do you remember file task behaviour? If the output file is not older than the input file (i.e. it is up to date), the file task will not run. In case you want to perform such a check in a regular task, you can use the FileUtils method named (surprisingly!) uptodate?

task :check do
  # ...

  unless uptodate?(output_file, ['input_file.xml'])
    # regenerate output_file
  end
end

FileList

Imagine a file task, where many input files are combined to give only one output file as a result. The most obvious way to define file dependencies would be:

one_file_to_rule_them_all = 'database.sql'
tables_sql = ['orders.sql', 'payments.sql', 'categories.sql']

file one_file_to_rule_them_all => tables_sql

It will work, but what when a new input SQL file is created? Well, we have to remember to manually add it to the tables_sql variable.

I don't know how about you, but I feel a little bit confused. We told that build tools were designed to automate boring and repeating tasks. And manually adding files to the list IS a boring task!

Fortunately, Rake's author knew that, too. There is the FileList class available which can be helpful in such situations. Let's use it now:

one_file_to_rule_them_all = 'database.sql'

FileList['*.sql'].each {|table| file one_file_to_rule_them_all => table}

Forget about adding every single SQL file to the list; Rake will do it for you.

Since dependencies are already described using the FileList, all you need to do is to add the task body:

file one_file_to_rule_them_all do
  puts 'I require the SQL files'
end

Then you can invoke the task by running:

$ rake database.sql
(in /home/lukasz/ruby/rake_examples)
I require the SQL files

Summary

There are many Rake functions that I didn't cover in this tutorial, including clean, clobber, rdoc and gem tasks, pathmap, rules and namespaces. You will have a chance to get to know them later.

I hope that this article is a good starting point to get intimate with Rake. Rake has proved its worth and now it is widely used in the Ruby world, so writing Rake tasks is a must-have skill. Good luck in your own experiments with Rake!

If you want to know how to use Rake to run RSpec tests, read the RSpec Rake Task article.

Comments

There are 17 comments / Submit your comment

shoja
July 29, 2009 04:42 AM

can you send Rake software to me.

Lukasz Wrobel
July 29, 2009 06:58 PM

Well, you can download Rake from the Internet for free. If you have Ruby gems installed, simply type in following command:

gem install rake

If you don't have a working Internet connection on your machine, you can download the latest version of Rake from http://rubyforge.org/frs/?group_id=50 using other machine. Save the file (e.g. rake-0.8.7.gem) in your filesystem, open the terminal and change current directory to the one containing rake-0.8.7.gem file. Then type in:

gem install rake-0.8.7.gem

This one should work. However, if you don't have Ruby gems installed, you can install Rake from source. Download rake-0.8.7.tgz file from http://rubyforge.org/frs/?group_id=50, then save it in your local filesystem. Open the terminal and change current directory to the one containing rake-0.8.7.tgz file. Then type in:

tar xzvf rake-0.8.7.tgz

Then:

cd rake-0.8.7

And then:

ruby install.rb

That's all you should know about Rake installation.

Mike Blyth
December 15, 2010 08:08 AM

Thanks for the nice tutorial! It's a clear and simple way to start understanding this Rake business which has always seemed so mysterious to me.

crazyDiamond
March 18, 2011 01:25 PM

This is by far the best rake tutorial I've seen for a beginner to understand the basics. I do hope you write more. Thank you!

Lukasz Wrobel
March 19, 2011 10:31 AM

Thanks, I'm really glad that you enjoy this Rake tutorial so much.

I was really surprised when I found out that it was even translated into Russian: http://habrahabr.ru/blogs/htranslations/111331/

Rosario
May 11, 2011 11:52 PM

How can I join some scripts in the same rakefile, is there some special sintaxis for that_, do you have an example, please. thanks

Lukasz Wrobel
May 13, 2011 05:30 PM

@Rosario

I guess you want to split your rakefile into many smaller files and include them when necessary?

In general, rakefiles are regular Ruby files, so you can just require them:

require 'another_rakefile'

task :default do
  puts 'Default task'
end

In simple cases, it will work like a charm. You can even refer to tasks defined in external rakefile:

task :default => :external do
  ...

However, external file gets loaded exactly when you put the require command. Perhaps you would like to refer the default task in another_rakefile. To achieve this, use the import method provided by Rake:

import 'another_rakefile'

task :default do
  puts 'Default task'
end

Then you will be able to refer to the default task in another_rakefile, like this:

# This is another_rakefile

task :another => :default do
  puts 'Default first!'
end

I hope this is the answer you were looking for.

gezope
June 16, 2011 08:59 PM

Nice tutorial, excellent to start with!

Many thanks for it, highly appreciated!:)

task :my_first_question
  if uptodate?(:your_blog_entry, :rake_version_in_2011)
         tell(:me).please!
  end
end

Can you give me links how to continue my studies on Rake please?

Thanks again,

gezope

Lukasz Wrobel
June 17, 2011 07:46 PM

@gezope

uptodate?

Yes, it seems that all examples present in the tutorial still work. Rake is under development and it has changed a little bit (changelist) since I wrote the article, though most of the concepts remain the same.

Can you give me links how to continue my studies on Rake please?

  • Take a look at Jim Weirich's (Rake's author) presentation
  • I also learned a lot from Martin Fowler's article

Tom H
October 24, 2011 10:34 AM

Thanks for a great intro to Rakefiles, saved me a lot of head scratching!

Ian W
April 25, 2012 02:48 PM

Thanks for the tutorial - the Rake documentation is not so clear for newbies to Rake. Good job.

John W
June 14, 2012 09:33 AM

Great tutorial - Explains everything i needed for a novis. Good Job :)

sas gho
September 06, 2012 07:20 PM

Hi all

i have a question about "how can run the n tasks defined into .rake file in sequential order ?"

thanks

Lukasz Wrobel
September 06, 2012 08:55 PM

@sas gho: As far as I'm concerned, there's no straightforward way to achieve this. At least, I'll try to give you a few hints.

If you know the task list in advance, you can chain their execution:

$ rake first && rake second && rake third

Or create a task depending on them:

task :grouping => [:first, :second, :third]

You can also run them inside another task's definition:

['first', 'second', 'third'].each do |task|
  Rake::Task[task].invoke
end

Keep in mind that invoke runs a task only if it wasn't run before. If you want to force running it, use execute instead or combine invoke with the reenable method. execute also has a drawback, it runs the task regardless of dependencies.

If you rather want to build the task list dynamically, then Rake.application.tasks may be of use. It's accessible from the Rakefile. Remember to use the .scope task's property in case you're interested in tasks belonging to the specific namespace.

Deeez
September 17, 2012 05:57 AM

Hi, I have multiple rake files(like xyz.rake, abc.rake)..i want to execute them in order (like 1.xvz.rake 2.abc.rake)...how do i achieve this??...using shell script???...then how???

Lukasz Wrobel
September 17, 2012 05:57 PM

@Deeez: If you want to execute a rakefile with an unusual name, you can do it using the -f parameter:

$ rake -f first.rake

You have either to define the :default task in such a rakefile or pass task name explicitly:

$ rake -f first.rake task_name

The easiest way to run multiple rake files is to combine rake command invocations:

$ rake -f first.rake && rake -f second.rake

&& symbol means that the next file will be executed only if the previous one has finished its job successfully.

oliver
August 21, 2013 02:33 PM

thanks for this nice tutorial. really made my day.

You can use Markdown in your comments if you wish. Examples:

*emphasis*
emphasis
**strong**
strong
`inline code`
inline code
[My blog](http://lukaszwrobel.pl)
My blog
# use 4 spaces to indent
# a block of code
    def my_method(x)
      x = x + 1
    end
def my_method(x)
  x = x + 1
end

* First.
* Second.
  • First.
  • Second.

> This is a citation.
> Even more citation.

I don't agree with you.

This is a citation. Even more citation.

I don't agree with you.


Submit your comment

(required)

(optional)

(required, Markdown supported)


Preview:

My eBook: “Memoirs of a Software Team Leader”

Read more »