Let’s learn the basics of build automation with the help of Rake

Date: 04/05/2015

Any software development project is always associated with the automation of related routine tasks. Initially, IDE and a pair of manual operations will be enough for you. Then, the number of body movements begins to grow: you need to perform multiple sets of tests, embed various certificates, execute scripts in the database, generate documentation on the code, and so on. You also need to perform these and other operations on the Continuous Integration server. In addition, you may need to deploy applications on production servers (if we’re talking about a client-server solution). To automate such tasks, programmers sometimes create sets of batch or shell scripts, but more often, the team of developers comes to some consolidated decision.

For every single platform, there is a well-established set of automation tools that are quite diverse, and each set has its own syntax and philosophy. However, they use configuration files in one form or another in order to describe the tasks, relationships between them and order of execution. In terms of the language that is used to describe the tasks, such tools can be divided into declarative, which describe the necessary actions (just like the rules in XML), and imperative that describe the task as a fragment of code being developed in accordance with specific agreements.

At first glance, the declarative approach is more advantageous as it allows you to achieve the desired results by describing the task using a relatively simple syntax (XML or other). On the other hand, sooner or later you will face restrictions of the implementation of the tool itself (for example, if you need to copy/delete files according to some complex mask). Besides, if you need to implement a complex workflow (for example, depending on the version of the application and the platform for which the proper configuration files and scripts must be provided), then you will have to write your own extensions, which, in turn, also need technical support, testing and so on. It turns out that you have to write a code in any case, so why not to implement the necessary actions in the code itself, and in a comfortable format?

And today, I’ll tell you about one of these tools. Rake is widely used in the famous Ruby on Rails web framework and other Ruby projects. In addition, I am quite successful in combining it with other technologies such as .NET, and such integration will be our example today.

Rake is a build automation tool that was written in Ruby. This is an open source project, now-deceased Jim Weirich was its author. His purpose was to create a tool similar to the popular Make, Ant and MSBuild, but the new tool had to be simpler and more flexible. The author focused on the following features of Rake:

  • Simple Ruby-based DSL allows you to use the full power of the language without complex XML or other configuration files.
  • Parallel execution of multiple tasks.
  • Rule templates allowing to generate implicit tasks.
  • A library of typical tasks.
  • Possibility to specify the initial conditions for tasks.

Installation

Rake is available as a library for Ruby. Ruby 1.9 (and more recent versions) already has embedded Rake. If you use Ruby 1.8 or there is a need to install a more recent version of Rake, you can run the following command in the package management system RubyGems:

gem install rake

Tasks

The configuration file in Rake is called Rakefile. Tasks are its main “building blocks.” Each task has a name, a set of initial conditions and a list of actions to be performed.

task name: [:prereq1, :prereq2]

Actions are transmitted to a structure which is called “block” in Ruby.

task name: [:prereq1, :prereq2] do |t|
  # actions
end

Tasks in Rake can be divided into two types: conventional and file. Conventional task is a set of actions to be performed. Such tasks are declared using the “task” method.

A file task is assigned in the form of a file that is usually created on the basis of one or more existing ones. If such a file already exists, the corresponding task is not executed and is skipped. File tasks are declared using the “file” method.

The name of the task to be performed is sent to the “rake” tool as a parameter.

rake task_name

If you execute “rake” without parameters, it will try to find a task named “default” in the Rakefile. If the “default” task is not found, “rake” will generate an error message.

>rake
rake aborted!
Don’t know how to build task 'default'

Comments

Although Rake allows you to use Ruby-like comments prefixed with #, it is recommended to use the keyword “desc” to comment your tasks.

In this case, the “rake -T” command will help us get the list of tasks along with descriptions. Well thought-out descriptions eliminate the need to document the Rake scripts.

Sample output of the

Namespaces

Rake offers the concept of namespaces to group the tasks. For example, anyone who has written code in Ruby on Rails previously, knows the “rake db: migrate” command; in this case, “db” is the namespace, and “migrate” is a task that belongs to it. A namespace is declared using the “namespace” keyword.

namespace :namespace_name do
     # tasks
end

Managing the Rake files

The “Rake” utility is searching for tasks in the “Rakefile” file. This file is growing very fast, so there is a need to split it into several files according to their functions. To do this, the following agreement should be used: create “rakelib” directory and “.rake” files in it. The main Rakefile may contain links to all the tasks and namespaces declared in other Rake files.

Building a .NET project with the help of Rake

Not surprisingly, Rake is most commonly used in Ruby/Rails projects. Nevertheless, we can freely use it in other languages. Let’s take a simple “Hello world” build in C# as an example. It is advisable to use Rake in .NET projects, especially when it is necessary to implement complex logic to build/test/deploy an application, as it is quite non-trivial, if XML declarations of NAnt or MSBuild are used.

The source will be a file with a simple code and a project file (in order to use MSBuild). The code is too simple, but the approach itself is the same as that when assembling large-scale solutions.

using System;

public class HelloWorld
{
    static void Main()
    {
        Console.WriteLine("Hello world!");
        Console.ReadLine();
    }
}

The project file would look something like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <Compile Include="hello.cs" />
    </ItemGroup>
    <Target Name="Build">
        <Csc Sources="@(Compile)"/>
    </Target>
</Project>

Since the Rake code is rather simple, I’ll give comments later.

require "fileutils"

task :default => [:clean, :build, :pkg]

msbuild = "#{ENV['WINDIR']}\\Microsoft.NET\\Framework\\v3.5\\msbuild.exe"
proj_root = File.dirname(__FILE__)
out_dir = "#{proj_root}/out"

desc "Clean the artefacts from previous build"
task :clean do
    rm Dir.glob('*.exe')
    rm_rf(out_dir) if Dir.exists?(out_dir)
end

desc "Compile project with MSBuild"
task :build do
    mkdir_p(out_dir) if !Dir.exists?(out_dir)
    project = "#{proj_root}/hello.proj"
    cmd = "\"#{msbuild}\" #{project}"

    sh cmd do |ok, res|
        raise "*** BUILD FAILED! ***" if !ok
    end
end

desc "Prepare deploy package"
task :pkg do
    artefacts = ["#{proj_root}/hello.exe",
                "#{proj_root}/readme.txt"]
    cp_r(artefacts, out_dir)
end

The following build stages are defined in our project:

  • Clean — artifacts of the previous build are deleted at this stage. In this case, all the “*.exe” files and the “out” directory are deleted.
  • Build — everything associated with compilation of the application. In this case, MSBuild gets the path to the .proj file as a parameter, and we execute this command.
  • Package — the .exe file that has been built is moved to the “out” directory, a “Read Me” is also copied there.

The “default” task does nothing, but it depends on the rest of the declared tasks. Thus, “rake” without parameters will execute the “clean,” “build,” and “package” tasks in sequence. It should also be mentioned that Rakefile, as any conventional Ruby script, allows to use the “require” operator in order to upload and use any libraries of this language.

Sample output of the Rake script from the example

Albacore

I’m not the only one who wants to use Rake to build .NET projects. To do this, we can use quite a popular project called Albacore, which extends DSL Rake by adding special keywords to automate common tasks faced by developers of software for the Microsoft platform.

Set by the following command:

gem install albacore

Let’s look at a build task declared using Albacore:

require "albacore"

desc "Compile project with MSBuild using Albacore"
build :alba_build do |b|
  b.file = "#{proj_root}/hello.proj"
end

The code has become much neater, hasn’t it? For detailed information on Albacore, please see the project’s Wiki on GitHub.

Conclusion

Rake is a great, flexible tool for creating various scripts to build and maintain your projects. Its use is not limited by the Ruby ecosystem, and due to this article, it’s obvious that it can be used even in the .NET infrastructure.

But of course, it’s inappropriate to use Ruby in small projects that can be implemented with the help of the tools provided by the platform itself. However, Rake can save a lot of time and minimize the amount of code in complex projects, particularly those that include support for legacy systems, when you have to deal with complex dependencies, prepare test environments (for example, in order to register COM objects in the system according to a set of specific conditions), etc.

Related posts:
2022.02.09 — Kernel exploitation for newbies: from compilation to privilege escalation

Theory is nothing without practice. Today, I will explain the nature of Linux kernel vulnerabilities and will shown how to exploit them. Get ready for an exciting journey:…

Full article →
2023.02.21 — SIGMAlarity jump. How to use Sigma rules in Timesketch

Information security specialists use multiple tools to detect and track system events. In 2016, a new utility called Sigma appeared in their arsenal. Its numerous functions will…

Full article →
2022.01.12 — Post-quantum VPN. Understanding quantum computers and installing OpenVPN to protect them against future threats

Quantum computers have been widely discussed since the 1980s. Even though very few people have dealt with them by now, such devices steadily…

Full article →
2023.03.26 — Attacks on the DHCP protocol: DHCP starvation, DHCP spoofing, and protection against these techniques

Chances are high that you had dealt with DHCP when configuring a router. But are you aware of risks arising if this protocol is misconfigured on a…

Full article →
2022.04.04 — Elephants and their vulnerabilities. Most epic CVEs in PostgreSQL

Once a quarter, PostgreSQL publishes minor releases containing vulnerabilities. Sometimes, such bugs make it possible to make an unprivileged user a local king superuser. To fix them,…

Full article →
2023.02.21 — Herpaderping and Ghosting. Two new ways to hide processes from antiviruses

The primary objective of virus writers (as well as pentesters and Red Team members) is to hide their payloads from antiviruses and avoid their detection. Various…

Full article →
2022.06.01 — Log4HELL! Everything you must know about Log4Shell

Up until recently, just a few people (aside from specialists) were aware of the Log4j logging utility. However, a vulnerability found in this library attracted to it…

Full article →
2022.12.15 — What Challenges To Overcome with the Help of Automated e2e Testing?

This is an external third-party advertising publication. Every good developer will tell you that software development is a complex task. It's a tricky process requiring…

Full article →
2022.06.01 — Routing nightmare. How to pentest OSPF and EIGRP dynamic routing protocols

The magic and charm of dynamic routing protocols can be deceptive: admins trust them implicitly and often forget to properly configure security systems embedded in these protocols. In this…

Full article →
2023.02.21 — Pivoting District: GRE Pivoting over network equipment

Too bad, security admins often don't pay due attention to network equipment, which enables malefactors to hack such devices and gain control over them. What…

Full article →