Rake Tutorial
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:
./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
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:
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:
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:
Then:
And then:
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
requirethem:In simple cases, it will work like a charm. You can even refer to tasks defined in external rakefile:
However, external file gets loaded exactly when you put the
requirecommand. Perhaps you would like to refer thedefaulttask inanother_rakefile. To achieve this, use theimportmethod provided by Rake:Then you will be able to refer to the
defaulttask inanother_rakefile, like this: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!:)
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
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.
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:
Or create a task depending on them:
You can also run them inside another task's definition:
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
-fparameter:You have either to define the
:defaulttask in such a rakefile or pass task name explicitly:The easiest way to run multiple rake files is to combine
rakecommand invocations:&&symbol means that the next file will be executed only if the previous one has finished its job successfully.You can use Markdown in your comments if you wish. Examples:
inline code# use 4 spaces to indent # a block of code def my_method(x) x = x + 1 endI don't agree with you.