hgiesel
hgiesel

Reputation: 5648

Makefile equivalent behavior in Rakefile

So I'm learning ruby at the moment and discovered rake. I like to learn new tools by implementing things I already know with them, so I try to convert a Makefile I have to rake.

Let's say it looks like this:

main: build/*.o
    clang -c $^ -o $@

build/%.o: src/%.c | build
    clang -c $< -o $@

build:
    mkdir build

What's special about this Makefile is:

  1. Pattern matching with %
  2. Order only dependency with | build

Is there some way to implement this logic using rake or do I have to use ruby itself? E.g.

task :default => "main"

file "main" => "build/%.o" do
  sh "clang -o 'main' ??"
end

file 'build/%.o' => "src/%.c" do # order only dependency on `build`
  sh "clang -c ?? ??"
end

Upvotes: 4

Views: 1052

Answers (1)

Matthias Winkelmann
Matthias Winkelmann

Reputation: 16394

This is something that rake is quite good at, and that is tragically underused:

task :default => "main"

# This assumes that your "main" is created by linking 
# all *.o files, each of which is the product of compiling a *.c file

# FileList[] creates a list of all *.c source files. The pathmap then
# changes the directory from /src/ to /out/ and the extension to .o

file "main" => FileList["src/**/*.c"].pathmap("%{^src,out}d/%n.o") do |t| 
   sh "ld #{t.sources.join(" ")} #{t.name}"
end

# This is the rule that says: if you need a 
# file out/bla.o, this will create it from /src/bla.c.    
rule /out\/.+.o/ => ->(target) { target.pathmap("%{^out,src}d/%n.c") } do |t|
  sh "cp #{t.source} #{t.name}"
end

Some notes:

  • Rule names can be regex. I believe glob-style patterns are also possible, but find it easier to just always use regex
  • If a rule's target already exists, and is newer than all its sources, it will not be executed again (like Make)
  • pathmap and file tasks are Rake extensions for filenames. There's nothing similar for other entities such as database entries, but you can usually create them yourself

Upvotes: 2

Related Questions