Incremental rebuild with Rake

I am using Rake for testing c++ code.

Incremental rebuild does not work for .h files,
Any ideas?


Simon S.

require ‘rake/clean’

APPLICATION = ‘test_code.exe’

CC = ‘g++’
LD = CC

CFLAGS = [
‘-pedantic’,
‘-fprofile-arcs -ftest-coverage’,
cppunit-config --cflags.strip
].join(’ ')

LIBS = [
cppunit-config --libs.strip
].join(’ ')

CPP_SOURCES = FileList[‘test_code/.cpp’] + FileList['code/.cpp’]

O_FILES = CPP_SOURCES.sub(/.cpp$/, ‘.o’)

file APPLICATION => O_FILES do |t|
sh “#{LD} #{LIBS} #{O_FILES} -o #{t.name}”
end

rule “.o” => [".cpp"] do |t|
sh “#{CC} #{CFLAGS} -c -o #{t.name} #{t.source}”
end

CLEAN.include("/*.o")
CLEAN.include("
/.bb")
CLEAN.include("**/
.bbg")
CLEAN.include("/*.da")
CLOBBER.include(APPLICATION)
CLOBBER.include("
/*.gcov")

desc “compile the test executable.”
task :compile => APPLICATION

desc “run all the tests.”
task :test => APPLICATION do
sh “./#{APPLICATION}”
end

rule “.gcov” => [".cpp"] do |t|
path = t.source.sub(//[^/]+.cpp$/, ‘’)
sh “gcov -p --object-directory #{path} #{t.source}”
end

GCOV_FILES = CPP_SOURCES.sub(/.cpp$/, ‘.gcov’)

desc “output coverage info.”
task :coverage => GCOV_FILES

task :default => :test

On 3/17/06, Simon S. [email protected] wrote:

I am using Rake for testing c++ code.

Incremental rebuild does not work for .h files,
Any ideas?

I have made some changes to my .h files
and nothing happens when I type ‘rake’!

I wonder how to describe, that the application also
depends on the .h files.

On 3/17/06, Simon S. [email protected] wrote:

depends on the .h files.
Anything smarter than converting makedepend to ruby, like this?


Simon S.

task :depend do
sh “makedepend -f- – #{CFLAGS} – #{CPP_SOURCES} > .depend”
end

=begin
input:
code.o: code.h morecode.h

output:
{‘code.o’ => [‘code.h’, ‘morecode.h’]}
=end
def convert_makedepend_to_hash(filename)
lines1 = IO.readlines(filename)

get rid of comments and empty lines

lines2 = lines1.reject{|line| line =~ /^#|^\s*$/ }

deps = {}
lines2.each do |line|
o_file, h_files_str = line.strip.split(': ‘)
h_files = h_files_str.split(’ ')
deps[o_file] = h_files
end

deps
end

task :rakedepend do
deps = convert_makedepend_to_hash(‘.depend’)
#p deps

ary = []
deps.each do |o_file, deps|
s = ‘file "’ + o_file + ‘" => ’ + deps.inspect
ary << s
end
result = ary.join(“\n”)
result += “\n\n”
File.open(’.rakedepends’, ‘w+’) {|f| f.write(result)}
end

load ‘.rakedepends’

Simon S. wrote:

On 3/17/06, Simon S. [email protected] wrote:

depends on the .h files.
Anything smarter than converting makedepend to ruby, like this?

Actually, its a bit easier than that … but its a poorly documented
area of Rake.

Rake supports dependency files by importing them, like this:

require ‘rake/loaders/makefile’ # Load the makefile dependency
loader
import ‘.depend.mf’ # Import the dependencies.

(notice the .mf extension, that tells Rake that the dependencies are in
makefile format).

So just create a task that builds the dependency files, similar to the
above.

file ‘.depend.mf’ do
sh “makedepend -f- – #{CFLAGS} – #{CPP_SOURCES} > .depend.mf”
end

After the Rakefile has been read, but before the user requested targets
are built, Rake will check to see if any imported dependencies have been
requested. If they have, rake will run any build targets for the
dependencies (i.e. if the dependency file is out of date, it will
rebuild it). It will then load the dependencies (no need to convert
makefile style dependencies, rake will parse the file if the makefile
loader has been required as shown above).

The only gotcha in the scenario is that the dependency file rebuild
cannot be triggered by any of the dependencies defined in the file
(since the that file has not been loaded yet).


– Jim W.

Simon S. wrote:

On 3/17/06, Jim W. [email protected] wrote:
[snip]

Rake supports dependency files by importing them, like this:
[snip]

Touching ‘a.cpp’ does not result in a compile, what am I doing wrong?
(Touching ‘a.h’ works fine.)

Ahh, evidently, makedepend does not create an explicit dependency
between a.o and a.cpp. Make probably deduces this implicitly. however,
rake does not. So just make the relationship explicit. Add the
following to your Rakefile:

CPP_FILES.each do |src|
file src.ext(“.o”) => src
end


– Jim W.

On 3/18/06, Jim W. [email protected] wrote:

CPP_FILES.each do |src|
file src.ext(“.o”) => src
end

Excellent… Now incremental rebuild is working
for both .h files and .cpp files.

Thanks Jim.

Below are the final rakefile, maybe handy for others.

require ‘rake/clean’
require ‘rake/loaders/makefile’

APPLICATION = ‘test.exe’
CPP_FILES = FileList[‘*.cpp’]
O_FILES = CPP_FILES.sub(/.cpp$/, ‘.o’)

file ‘.depend.mf’ do
sh “makedepend -f- – – #{CPP_FILES} > .depend.mf”
end

import “.depend.mf”

file APPLICATION => O_FILES do |t|
sh “g++ #{O_FILES} -o #{t.name}”
end

rule “.o” => [“.cpp”] do |t|
sh “g++ -c -o #{t.name} #{t.source}”
end

CPP_FILES.each do |src|
file src.ext(“.o”) => src
end

CLEAN.include(“**/*.o”)
CLEAN.include(APPLICATION)
CLEAN.include(“.depend.mf”)

task :default => APPLICATION

On 3/17/06, Jim W. [email protected] wrote:
[snip]

Rake supports dependency files by importing them, like this:
[snip]

Touching ‘a.cpp’ does not result in a compile, what am I doing wrong?
(Touching ‘a.h’ works fine.)

prompt> ls
Rakefile a.cpp a.h main.cpp
prompt> rake
(in /Users/simonstrandgaard/rake)
makedepend -f- – – a.cpp main.cpp > .depend.mf
makedepend: warning: a.cpp (reading a.h, line 4): cannot find include
file “string”
not in /usr/local/lib/gcc-include/string
not in /usr/include/string
g++ -c -o a.o a.cpp
g++ -c -o main.o main.cpp
g++ a.o main.o -o test.exe
prompt> ./test.exe
test a
prompt> touch a.h
prompt> rake
(in /Users/simonstrandgaard/rake)
g++ -c -o a.o a.cpp
g++ -c -o main.o main.cpp
g++ a.o main.o -o test.exe
prompt> touch a.cpp
prompt> rake
(in /Users/simonstrandgaard/rake)
prompt>

Below are my files

prompt> cat .depend.mf

DO NOT DELETE

a.o: a.h
main.o: a.h

prompt> cat a.h
#ifndef A_H
#define A_H

#include

std::string test_a();

#endif // A_H

prompt> cat a.cpp
#include “a.h”

std::string test_a()
{
return std::string(“a”);
}

prompt> cat main.cpp
#include “a.h”

int main(int argc, char **argv)
{
std::string s = test_a();
printf(“test %s\n”, s.c_str());
return 0;
}

prompt> cat Rakefile
require ‘rake/clean’
require ‘rake/loaders/makefile’

APPLICATION = ‘test.exe’
CPP_FILES = FileList[‘*.cpp’]
O_FILES = CPP_FILES.sub(/.cpp$/, ‘.o’)

file ‘.depend.mf’ do
sh “makedepend -f- – – #{CPP_FILES} > .depend.mf”
end

import “.depend.mf”

file APPLICATION => O_FILES do |t|
sh “g++ #{O_FILES} -o #{t.name}”
end

rule “.o” => [“.cpp”] do |t|
sh “g++ -c -o #{t.name} #{t.source}”
end

CLEAN.include(“**/*.o”)
CLEAN.include(APPLICATION)
CLEAN.include(“.depend.mf”)

task :default => APPLICATION

I have noticed that the ‘.exe’ file sometimes is’nt being linked.
What could be the reason for this?

Does’nt matter if I touch .h files or .cpp files.
Pretty odd. Any ideas? (maybe osx issue?)

prompt> touch a.h
prompt> rake
(in /Users/simonstrandgaard/rake)
g++ -c -o a.o a.cpp
g++ -c -o main.o main.cpp
g++ a.o main.o -o test.exe
prompt> touch a.h
prompt> rake
(in /Users/simonstrandgaard/rake)
g++ -c -o a.o a.cpp
g++ -c -o main.o main.cpp
prompt>

prompt> touch a.cpp
prompt> rake
(in /Users/simonstrandgaard/rake)
g++ -c -o a.o a.cpp
g++ a.o main.o -o test.exe
prompt> touch a.cpp
prompt> rake
(in /Users/simonstrandgaard/rake)
g++ -c -o a.o a.cpp
prompt>

prompt> touch main.cpp
prompt> rake
(in /Users/simonstrandgaard/rake)
g++ -c -o main.o main.cpp
prompt> touch main.cpp
prompt> rake
(in /Users/simonstrandgaard/rake)
g++ -c -o main.o main.cpp
g++ a.o main.o -o test.exe
prompt>

prompt> uname -a
Darwin case.local 7.9.0 Darwin Kernel Version 7.9.0: Wed Mar 30
20:11:17 PST 2005; root:xnu/xnu-517.12.7.obj~1/RELEASE_PPC Power
Macintosh powerpc
prompt> rake --version
rake, version 0.5.4
prompt> ruby -v
ruby 1.8.2 (2004-11-03) [powerpc-darwin7.5.0]
prompt>

prompt> rake -P
(in /Users/simonstrandgaard/rake)
rake .depend.mf
rake a.o
a.cpp
a.h
rake clean
rake clobber
clean
rake default
test.exe
rake main.o
main.cpp
a.h
rake test.exe
a.o
main.o
prompt>

prompt> cat .depend.mf

DO NOT DELETE

a.o: a.h
main.o: a.h

prompt> cat Rakefile
require ‘rake/clean’
require ‘rake/loaders/makefile’

APPLICATION = ‘test.exe’
CPP_FILES = FileList[’*.cpp’]
O_FILES = CPP_FILES.sub(/.cpp$/, ‘.o’)

file ‘.depend.mf’ do
sh “makedepend -f- – – #{CPP_FILES} > .depend.mf”
end

import “.depend.mf”

file APPLICATION => O_FILES do |t|
sh “g++ #{O_FILES} -o #{t.name}”
end

rule “.o” => [".cpp"] do |t|
sh “g++ -c -o #{t.name} #{t.source}”
end

CPP_FILES.each do |src|
file src.ext(".o") => src
end

CLEAN.include("**/*.o")
CLEAN.include(APPLICATION)
CLEAN.include(".depend.mf")

task :default => APPLICATION

prompt> cat a.h
#ifndef A_H
#define A_H

#include

std::string test_a();

#endif // A_H

prompt> cat a.cpp
#include “a.h”

std::string test_a()
{
return std::string(“a”);
}

prompt> cat main.cpp
#include “a.h”

int main(int argc, char **argv)
{
std::string s = test_a();
printf(“test %s\n”, s.c_str());
return 0;
}

On 3/19/06, Simon S. [email protected] wrote:

I have noticed that the ‘.exe’ file sometimes is’nt being linked.
What could be the reason for this?

Does’nt matter if I touch .h files or .cpp files.
Pretty odd. Any ideas? (maybe osx issue?)

seems to be an osx issue with disk caching.

http://groups.google.com/group/comp.sys.mac.system/browse_thread/thread/757aa464a524e8df/66e7b93cd8464833#msg_b1345e404c2d7104

Simon S. wrote:

I have noticed that the ‘.exe’ file sometimes is’nt being linked.
What could be the reason for this?

I’m not able to reproduce this. Hmmmm.

Check the time stamps on the .o and .exe files. If the .exe is older
than the .o files, rake should attempt to rebuild it.

– Jim W.

On 3/19/06, Jim W. [email protected] wrote:

Simon S. wrote:

I have noticed that the ‘.exe’ file sometimes is’nt being linked.
What could be the reason for this?

I’m not able to reproduce this. Hmmmm.

Check the time stamps on the .o and .exe files. If the .exe is older
than the .o files, rake should attempt to rebuild it.

After reading on the mac groups… the solution seems to be
doing “ls #{t.name} > /dev/null” after each operation.
I would prefer a cleaner solution to this!

Anyways thanks for the help (and for rake).

file APPLICATION => O_FILES do |t|
sh “g++ #{O_FILES} -o #{t.name}”
system(“ls #{t.name} > /dev/null”)
end

rule “.o” => [“.cpp”] do |t|
sh “g++ -c -o #{t.name} #{t.source}”
system(“ls #{t.name} > /dev/null”)
end