Incremental rebuild with Rake

I am using Rake for testing c++ code.

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

···

--
Simon Strandgaard

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

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 Strandgaard <neoneye@gmail.com> wrote:

I am using Rake for testing c++ code.

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

--
Simon Strandgaard

Anything smarter than converting makedepend to ruby, like this?

···

On 3/17/06, Simon Strandgaard <neoneye@gmail.com> wrote:

On 3/17/06, Simon Strandgaard <neoneye@gmail.com> 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.

--
Simon Strandgaard

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 Strandgaard 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).

···

On 3/17/06, Simon Strandgaard <neoneye@gmail.com> wrote:

--
-- Jim Weirich

--
Posted via http://www.ruby-forum.com/\.

[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.)

ls

Rakefile a.cpp a.h main.cpp

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

./test.exe

test a

touch a.h
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

touch a.cpp
rake

(in /Users/simonstrandgaard/rake)

Below are my files

cat .depend.mf

# DO NOT DELETE

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

cat a.h

#ifndef __A_H__
#define __A_H__

#include <string>

std::string test_a();

#endif // __A_H__

cat a.cpp

#include "a.h"

std::string test_a()
{
        return std::string("a");
}

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;
}

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

···

On 3/17/06, Jim Weirich <jim@weirichhouse.org> wrote:

Simon Strandgaard 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

···

On 3/17/06, Jim Weirich <jim@weirichhouse.org> wrote:

--
-- Jim Weirich

--
Posted via http://www.ruby-forum.com/\.

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/18/06, Jim Weirich <jim@weirichhouse.org> wrote:

Simon Strandgaard wrote:
>
> 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

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?)

touch a.h
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

touch a.h
rake

(in /Users/simonstrandgaard/rake)
g++ -c -o a.o a.cpp
g++ -c -o main.o main.cpp

touch a.cpp
rake

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

touch a.cpp
rake

(in /Users/simonstrandgaard/rake)
g++ -c -o a.o a.cpp

touch main.cpp
rake

(in /Users/simonstrandgaard/rake)
g++ -c -o main.o main.cpp

touch main.cpp
rake

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

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

rake --version

rake, version 0.5.4

ruby -v

ruby 1.8.2 (2004-11-03) [powerpc-darwin7.5.0]

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

cat .depend.mf

# DO NOT DELETE

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

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

cat a.h

#ifndef __A_H__
#define __A_H__

#include <string>

std::string test_a();

#endif // __A_H__

cat a.cpp

#include "a.h"

std::string test_a()
{
        return std::string("a");
}

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;
}

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

···

On 3/19/06, Simon Strandgaard <neoneye@gmail.com> 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?)

--
Simon Strandgaard

Simon Strandgaard 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 Weirich

···

--
Posted via http://www.ruby-forum.com/\.

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

···

On 3/19/06, Jim Weirich <jim@weirichhouse.org> wrote:

Simon Strandgaard 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.

--
Simon Strandgaard