Rake help required

Hi ..

I am not sure how to create a set of Rake rules to do the following. Can
anyone prove assistance?

Structure:

  Rakefile
  foo
  src_a/
    a_1.c
    a_2.c
  src_b/
    b_1.c
    b_2.c
  obj/
    a_1.o
    a_2.o
    b_1.o
    b_2.o
    libfoo.a ("ar cr libfoo.a obj/*.c; ranlib libfoo.a")

I would like to have a single target

  $ rake build

That would create the .o and the lib, then link them into the executable. I
can do this if everything is in the root directory but I am finding defining
the objects trickier.

I'd rather avoid explicitly putting the directory name in the source list,
like

SRC_A = ["src_a/a_1.c" ...]

Regards,

···

--
-mark. (probertm at acm dot org)

An Rantfile that does this job could look like:

    desc "Build foo."
    file :foo => "obj/libfoo.a" do |t|
        # adjust...
        sys "cc -o #{t.name} #{t.source}"
    end
    
    o_files = sys["**/*.c"].map { |f| "obj/" + File.basename(f).sub_ext("o") }
    file "obj/libfoo.a" => o_files do |t|
        sys "ar cr #{t.name} #{t.prerequisites.arglist}"
        sys "ranlib #{t.name}"
    end
    
    o_to_c = lambda { |t|
        t =~ /^obj\/([^_])_/
        "src_#{$1}/#{File.basename(t)}".sub_ext "c"
    }
    gen Rule, :o => o_to_c do |t|
        sys "cc -c -o #{t.name} #{t.source}"
    end

When invoking rant it will build foo, as its the first task:
    % rant

Stefan

···

On Thursday 28 April 2005 01:38, Mark Probert wrote:

Hi ..

I am not sure how to create a set of Rake rules to do the following. Can
anyone prove assistance?

Structure:

  Rakefile
  foo
  src_a/
    a_1.c
    a_2.c
  src_b/
    b_1.c
    b_2.c
  obj/
    a_1.o
    a_2.o
    b_1.o
    b_2.o
    libfoo.a ("ar cr libfoo.a obj/*.c; ranlib libfoo.a")

I would like to have a single target

  $ rake build

That would create the .o and the lib, then link them into the executable.
I can do this if everything is in the root directory but I am finding
defining the objects trickier.

I'd rather avoid explicitly putting the directory name in the source list,
like

SRC_A = ["src_a/a_1.c" ...]

Regards,

I think the following Rakefile will do what you want. I added extensive
comments to the file rather than do commentary in this message. Let me know
if you have problems.

-- Rakefile ----------------------------------------------------------------
require 'rake/clean'

# Create some constants for later reference.

PROG = "foo"
LIBNAME = PROG
LIBFILE = "lib#{LIBNAME}.a"

# For SRC, we just find all files in the project ending in '.c'. This
# will work with nested source directories too (like might be found in
# a Java project).

SRC = FileList['**/*.c']

# Generate the OBJ list by striping off the directory portion of the
# source file name and flattening the names into a single level obj
# directory. This assumes that all the base file names are unique,
# but that is given by the problem statement. (Note that the +ext+
# function is available in Rake 0.5.3 or later).

OBJDIR = 'obj'
OBJ = SRC.collect { |fn| File.join(OBJDIR, File.basename(fn).ext('o')) }

# Define our clean and clobber targets.

CLEAN.include(OBJ, OBJDIR, LIBFILE)
CLOBBER.include(PROG)

# The default task builds, then runs the program

task :default => [:build, :run]

# The build task just depends upon the program being present.

task :build => [PROG]

# The run task requires the program to be present, and then runs that
# program.

task :run => [PROG] do
  sh "./#{PROG}"
end

# The program depends only on the library file.

file PROG => [LIBFILE] do
  sh "cc -o #{PROG} -L . -l#{LIBNAME}"
end

# The library file depends only on the list of objects we constructed
# at the top of the file.

file LIBFILE => OBJ do
  sh "ar cr #{LIBFILE} #{OBJ}"
  sh "ranlib #{LIBFILE}"
end

# The directory directive defines the directory OBJDIR as a task that
# can be used in a prerequisites list in a task declaration.

directory OBJDIR

# Ok, this is the tricky part. This rule generates .o files from a
# source file described by a lambda function. Finding the source file
# is tricky because the object file does not indicate /which/ source
# directory contains the matching .c file. We punt by writing a
# special function to locate the source. (Originally I did this
# inline in the lambda, but I think it reads better split out into a
# separate functions).

···

On Wednesday 27 April 2005 07:38 pm, Mark Probert wrote:

Hi ..

I am not sure how to create a set of Rake rules to do the following. Can
anyone prove assistance?

#
# Note that we invoke the OBJDIR task directly in this rule. Because
# it is a rule, there is no opportunity to list OBJDIR as a
# dependency. By invoking it directly, we will build that directory
# if it is needed (but only if it is needed).

rule '.o' => lambda{ |objfile| find_source(objfile) } do |t|
  Task[OBJDIR].invoke
  sh "cc -c -o #{t.name} #{t.source}"
end

# It turns out that finding the source file is not that difficult. We
# just search the list of source files for a match on the basename of
# the object file. Obviously this might have a performance problem
# with a _large_ list of source files. In that case, I would build a
# hash at the top of the Rakefile and use that hash here when looking
# up the source.

def find_source(objfile)
  base = File.basename(objfile, '.o')
  SRC.find { |s| File.basename(s, '.c') == base }
end
-----------------------------------------------------------------

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Hi ..

I am not sure how to create a set of Rake rules to do the following. Can
anyone prove assistance?

I think the following Rakefile will do what you want. I added extensive
comments to the file rather than do commentary in this message. Let me know
if you have problems.

-- Rakefile ----------------------------------------------------------------
require 'rake/clean'

# Create some constants for later reference.

PROG = "foo"
LIBNAME = PROG
LIBFILE = "lib#{LIBNAME}.a"

# For SRC, we just find all files in the project ending in '.c'. This
# will work with nested source directories too (like might be found in
# a Java project).

SRC = FileList['**/*.c']

# Generate the OBJ list by striping off the directory portion of the
# source file name and flattening the names into a single level obj
# directory. This assumes that all the base file names are unique,
# but that is given by the problem statement. (Note that the +ext+
# function is available in Rake 0.5.3 or later).

OBJDIR = 'obj'
OBJ = SRC.collect { |fn| File.join(OBJDIR, File.basename(fn).ext('o')) }

# Define our clean and clobber targets.

CLEAN.include(OBJ, OBJDIR, LIBFILE)
CLOBBER.include(PROG)

# The default task builds, then runs the program

task :default => [:build, :run]

# The build task just depends upon the program being present.

task :build => [PROG]

# The run task requires the program to be present, and then runs that
# program.

task :run => [PROG] do
sh "./#{PROG}"
end

# The program depends only on the library file.

file PROG => [LIBFILE] do
sh "cc -o #{PROG} -L . -l#{LIBNAME}"
end

# The library file depends only on the list of objects we constructed
# at the top of the file.

file LIBFILE => OBJ do
sh "ar cr #{LIBFILE} #{OBJ}"
sh "ranlib #{LIBFILE}"
end

# The directory directive defines the directory OBJDIR as a task that
# can be used in a prerequisites list in a task declaration.

directory OBJDIR

# Ok, this is the tricky part. This rule generates .o files from a
# source file described by a lambda function. Finding the source file
# is tricky because the object file does not indicate /which/ source
# directory contains the matching .c file. We punt by writing a
# special function to locate the source. (Originally I did this
# inline in the lambda, but I think it reads better split out into a
# separate functions).
#
# Note that we invoke the OBJDIR task directly in this rule. Because
# it is a rule, there is no opportunity to list OBJDIR as a
# dependency. By invoking it directly, we will build that directory
# if it is needed (but only if it is needed).

rule '.o' => lambda{ |objfile| find_source(objfile) } do |t|
Task[OBJDIR].invoke
sh "cc -c -o #{t.name} #{t.source}"
end

# It turns out that finding the source file is not that difficult. We
# just search the list of source files for a match on the basename of
# the object file. Obviously this might have a performance problem
# with a _large_ list of source files. In that case, I would build a
# hash at the top of the Rakefile and use that hash here when looking
# up the source.

def find_source(objfile)
base = File.basename(objfile, '.o')
SRC.find { |s| File.basename(s, '.c') == base }
end
-----------------------------------------------------------------

This subfolder-sourcing seems like a common usage pattern,
perhaps worthy of some sort of an abstraction? Is there any
sort of a 'common Rake task idioms' collection anywhere?

-- Jim Weirich

E

···

Le 28/4/2005, "Jim Weirich" <jim@weirichhouse.org> a écrit:

On Wednesday 27 April 2005 07:38 pm, Mark Probert wrote:

--
template<typename duck>
void quack(duck& d) { d.quack(); }

Hi ..

···

On Wednesday 27 April 2005 19:47, Jim Weirich wrote:

On Wednesday 27 April 2005 07:38 pm, Mark Probert wrote:
> Hi ..
>
> I am not sure how to create a set of Rake rules to do the following. Can
> anyone prove assistance?

I think the following Rakefile will do what you want. I added extensive
comments to the file rather than do commentary in this message. Let me
know if you have problems.

Many thanks, Jim.

Maybe I should start putting together a newbies guide to using Rake for C
development from the posts you have sent...

Regards,

--
-mark. (probertm at acm dot org)