Musings of a code junkie

Generate Makefile with Rake

Tagged make, rake, makefile, rakefile, and ruby

WTF?

Anyone who knows what Rake is must be wondering to themselves, what the hell is this guy thinking?!

But Wait! It’s not what you think! I can explain…

There’s a class that I had this semester called Segurança de Redes de Informática e de Sistemas (aka. SIRS). Roughly translated from portuguese it’s: IT Network and System Security.

In this subject we had to propose and execute a project of our choosing. It could be anything we wanted, using any technology we wished, as long as it was something related to computer security. I’ll talk about this project in another post.

Continuing… we developed our project in C++ and the build system that we chose (actually, that I chose as I’m the Ruby fanatic) was Rake. It’s great! And simple! Plus, it’s in Ruby – which I know (and remember), unlike make’s syntax!

So why do you need a Makefile?

Unfortunately, Rake isn’t that commonly used at IST. Neither is Ruby, for that matter. The norm is to use make to build the projects.

So I had a problem: I wanted the build system to be in Rake, but I also needed the teachers to be able to build the project for themselves without installing Ruby and Rake.

So I just made Rake spit out a Makefile. It wasn’t that hard, you’ll see…

Dude, I haven’t got all day!

(If you’re really in a hurry and want to skip all the explanations to go directly to the solution, you can have a look at the complete Rakefile example).

All I did to get this done was add 2 global variables to the beginning of the Rakefile:

$gen_makefile = false
$makefile_data = ""

And then I redefined the sh method, that I used to call g++

def sh(args)
  if $gen_makefile
    $makefile_data << "t#{args}n"
  else
    super(args)
  end
end

Now, to actually generate the Makefile, I created a task called makefile:

desc "Generates a Makefile to compile all the code"
task :makefile do
  $gen_makefile = true
  $makefile_data << "all: server client\n"

  $makefile_data << "server:\n"
  Rake::Task['server'].invoke

  $makefile_data << "client:\n"
  Rake::Task['client'].invoke

  File.open('Generated_Makefile', 'w') { |f| f.write $makefile_data}
end

It’s function is to add an all task to the Makefile that depends on the server and the client tasks.

Then to define the server/client tasks, all that has to be done is set the variable $gen_makefile to true and call the server/client rake task and voila, we have the Makefile code in $makefile_data ready to be written to a file.

There’s still something missing

Yep, you’re right. Every makefile needs a clean task! But of course!

I actually didn’t use this during the development of the project, but I thought it would be more complete if I added it.

So, to create the clean task for the Makefile all that is required is this:

$makefile_data << "clean:\n"
CLEAN.each do |f|
  $makefile_data << "\trm -rf #{f}\n"
end

Now, clean in a rake task should only remove the files that were used to help compile the final program and not the resulting executables. That’s what clobber does, it removes everything, leaving the directory as if the compilation had never happened.

Following these naming conventions:

$makefile_data << "clobber:\n"
CLEAN.each do |f|
  $makefile_data << "\trm -rf #{f}\n"
end
CLOBBER.each do |f|
  $makefile_data << "\trm -rf #{f}\n"
end

And there you have it! You’re own Makefile! I wouldn’t recommend editing it manually as, at least in my case, the commands that were executed are humongous! At least now anyone with make can compile the code.

(complete Rakefile example)

Yuck

I know, the code is terrible. Using global variables and all that. There’s lots of room for improvement. But at least it works.

If I find the time and have the inclination, I’ll try and improve the code and make it easier to use.

Or maybe you’d like to take it further? If so, please drop me a line so I can check it out!

Posted on 04 February 2009 under Programming
blog comments powered by Disqus