Cxxproject

Simple,flexible,C/C++Buildsystem

Fork me on GitHub

Tutorial

In this little tutorial we're going to walk through an example of setting up cxx-build support for a c++ project. This is our basic scenario: * we have a library that we want to compile and link (e.g. gtest) * we have some source files that we need to compile and link with the gtest-library into an executable

Basic Setup

Let's start by downloading the library that we want to build and link with:

  $ wget http://googletest.googlecode.com/files/gtest-1.6.0.zip
  $ unzip gtest-1.6.0.zip && rm gtest-1.6.0.zip

Now we want to turn the library into a source-building-block. To do this we need a building block description for it.

  $ cxx .
  $ mv project.rb gtest

In order to use the gtest library we need to compile it into 2 static libs. Here is what the building-block description looks like after we adapted it to match our needs:

cxx_configuration do
  source_lib "gtest",
    :sources => FileList['src/gtest-all.cc'],
    :includes => ['include','.'],
    :dependencies => []
  source_lib "gtest_main",
    :sources => FileList['src/gtest_main.cc'],
    :includes => ['include'],
    :dependencies => ["gtest"]
end

Good. Note that we included both source libraries in the configuration file.

Now we also need to tweek the Rakefile.rb a little. We want to * use the gcc-toolchain * add some additional flags

require 'cxxproject'
BUILD_DIR = "BuildDir"
TC="clang"
unittest_flags = {
  :DEFINES => ['UNIT_TEST','CPPUNIT_MAIN=main'],
  :FLAGS => ["-O0","-g3","-std=c++11","-stdlib=libc++","-Wall"]
}

cxx(FileList['**/project.rb'], BUILD_DIR, TC, './') do
  Provider.modify_cpp_compiler(TC, unittest_flags)
end

Well...looks good! Now we can check what rake tasks are generated for us:

  $ rake -T
  rake clean           # Remove any temporary products.
  rake clobber         # Remove any generated file.
  rake lib:gtest       # BuildDir/libgtest.a
  rake lib:gtest_main  # BuildDir/libgtest_main.a

Let's try to build the gtest_main lib. Note that this should also build the gtest lib:

  $ rake lib:gtest_main
  g++ -c  -MMD -MF BuildDir/gtest/src/gtest-all.cc.o.d -O0 -g3 -Wall -Igtest-1.6.0/include -Igtest-1.6.0 -DUNIT_TEST -o BuildDir/gtest/src/gtest-all.cc.o gtest-1.6.0/src/gtest-all.cc

  ar -r BuildDir/libgtest.a BuildDir/gtest/src/gtest-all.cc.o
  ar: creating BuildDir/libgtest.a
  mkdir -p BuildDir/gtest_main/src
  g++ -c  -MMD -MF BuildDir/gtest_main/src/gtest_main.cc.o.d -O0 -g3 -Wall -Igtest-1.6.0/include -Igtest-1.6.0 -DUNIT_TEST -o BuildDir/gtest_main/src/gtest_main.cc.o gtest-1.6.0/src/gtest_main.cc

  ar -r BuildDir/libgtest_main.a BuildDir/gtest_main/src/gtest_main.cc.o
  ar: creating BuildDir/libgtest_main.a

Nice! we got ourselfs both libraries. That was painless! Next we want to add some code that we will end up testing using the gtest-framework.

libA/libA.h

int a(int);

libA/libA.cpp

#include "libA.h"

int a(int x)
{
    return 8*x;
}

And of course we need a building block description for our little library: libA/project.rb

cxx_configuration do
  source_lib "A",
    :sources => FileList['*.cpp'],
    :includes => ['.'],
    :dependencies => []
end

Let's see what rake will allow us to do now:

  $ rake -T
  rake clean           # Remove any temporary products.
  rake clobber         # Remove any generated file.
  rake lib:A           # BuildDir/libA.a
  rake lib:gtest       # BuildDir/libgtest.a
  rake lib:gtest_main  # BuildDir/libgtest_main.a

Just a quick check if we can build our lib:

  $ rake lib:A
  mkdir -p BuildDir/A
  g++ -c  -MMD -MF BuildDir/A/libA.cpp.o.d -O0 -g3 -Wall -IlibA -DUNIT_TEST -o BuildDir/A/libA.cpp.o libA/libA.cpp
  ar -r BuildDir/libA.a BuildDir/A/libA.cpp.o
  ar: creating BuildDir/libA.a

Seems to work. Now let's get down with the testing. this will be our basic test: libA/tests/testLibA.cpp

#include "gtest/gtest.h"
#include "libA.h"

TEST(liba, function_a){
    EXPECT_EQ(8, a(1));
}

and add a building-block-description for our executable:

cxx_configuration do
  deps = [BinaryLibrary.new('pthread'),BinaryLibrary.new('dl')]
  exe "testme",
    :sources => FileList['**/*.cpp'],
    :includes => ['.'],
    :dependencies => ['A','gtest_main'] + deps,
    :libpath => ['../../lib']
end

now rake will give us even more tasks:

    $ rake -T
    rake clean           # Remove any temporary products.
    rake clobber         # Remove any generated file.
    rake exe:testme      # BuildDir/testme.exe
    rake lib:A           # BuildDir/libA.a
    rake lib:gtest       # BuildDir/libgtest.a
    rake lib:gtest_main  # BuildDir/libgtest_main.a
    rake run:testme      # run executable BuildDir/testme.exe

And finally we can try to build, link and run our test:

  $ rake run:testme
  g++ -c  -MMD -MF BuildDir/testme/testLibA.cpp.o.d -O0 -g3 -Wall -IlibA/tests -IlibA -Igtest-1.6.0/include -Igtest-1.6.0 -DUNIT_TEST -o BuildDir/testme/testLibA.cpp.o libA/tests/testLibA.cpp

  g++ -all_load -o BuildDir/testme.exe BuildDir/testme/testLibA.cpp.o -Wl,--whole-archive -Llib -LBuildDir -lA -lgtest_main -lpthread -ldl -lgtest -Wl,--no-whole-archive

  BuildDir/testme.exe
  Running main() from gtest_main.cc
  [==========] Running 1 test from 1 test case.
  [----------] Global test environment set-up.
  [----------] 1 test from liba
  [ RUN      ] liba.function_a
  [       OK ] liba.function_a (0 ms)
  [----------] 1 test from liba (0 ms total)

  [----------] Global test environment tear-down
  [==========] 1 test from 1 test case ran. (1 ms total)
  [  PASSED  ] 1 test.

Done! now we have a test application that consists of

with the following dependencies:

testme => [gtest_main,A]

gtest_main => gtest