Adding code and creating Buildbot Slaves for CMake

Current repository revision: 2 (Main)

Code highlight key
Grey: Code unchanged
Red: Code removed
Green: Code added
Blue: Code not shown

→ Denotes the current line and the next are really one line

The last post described how to create a Buildbot Master for compiling the MereIdea libraries. CMake is to be used to create the makefiles to build the code. This post will describe how some simple code was created and committed to the Main Subversion repository (http://www.mereidea.com/svn/main/trunk/), including the initial CMake files. Then a Linux Slave will be set up to check out and build the code. This will lead to an iterative process to correct the Builder commands in the Master to work with the Linux platform, before moving onto Windows and setting up a Windows based Slave.

An out-of-source build is preferred, as it avoids cluttering up the source tree and accidental commits of binary files to the code repository. The general directory structure for such code will be:

code/
+-- build/
+-- src/
|    +-- CMakeLists.txt
|    +-- mi_lib1/
|    |    +-- CMakeLists.txt
|    |    +-- foo.h
|    |    +-- foo.cxx
|    |    +-- bar/
|    |    |    +-- CMakeLists.txt
|    |    |    +-- bar.h
|    |    |    +-- bar.cxx
|    |    |    +-- tests/
|    |    |    |    +-- CMakeLists.txt
|    |    |    |    +-- test_bar.cxx
|    |    +-- tests/
|    |    |    +-- CMakeLists.txt
|    |    |    +-- test_foo.cxx
|    +--  mi_lib2/
|    +--  mi_lib3/

Everything within the src/ part of the tree (not including the src/ directory itself) will be committed to the repository.


Initial Code Tree

With this in mind, a src/ directory was created and a library directory (mi_hello) was added inside src/. The mi_hello library will always contain simple code (pretty much a C++ ‘Hello, World’) that can be used to test the compiler. It will be the first library built (with the exception of mi_test that will set up the testing framework) with at least one test to be linked. The files in here should be amended if C++ features are used in the other libraries which may or may not work on some compilers. Having this small library means you can get quick feedback as to whether the rest of the libraries should build (though not guaranteed) with your system.

The initial code that will be used to test the Buildbot set up was created in the actual directory structure:

code/
+-- src/
|    +-- CMakeLists.txt
|    +-- LICENSE.txt
|    +-- mi_hello/
|    |    +-- CMakeLists.txt
|    |    +-- hello.cxx
|    |    +-- hello.h
|    |    +-- tests/
|    |    |    +-- CMakeLists.txt
|    |    |    +-- test_hello.cxx

You can check out the code for yourself from http://svn.mereidea.com/main/trunk/ as revision 2, and you should get this structure. This can be done with:

svn co -r 2 http://www.mereidea.com/svn/main/trunk src

The LICENSE.txt file simply contains a copy of the GNU General Public License under which the code is available.

The files hello.h and hello.cxx contain the definition of a class (mi_hello::Hello) with a single method:

void display(const std::string &disp_str = "Hello, World!") const;

This method simply displays the string passed to it, or Hello,  World! if no other string is passed. The test file test_hello.cxx contains a main() function to call the display() method of the mi_hello::Hello class. The code for this is:

#include <mi_hello/hello.h>
int main()
{
  mi_hello::Hello h;
  h.display();
  h.display("Goodbye, World!");

  return 0;
}

This should compile to an executable and be run when the Buildbot Slave runs. Later this code will be placed in a proper testing framework, but until the Buildbot code is working correctly it’s not necessary to complicate things! The more things added at any testing stage, the more places things could go wrong. Putting things together one at a time makes it easier to locate where any problem is.


Initial CMake files

Now that there is some code to compile, the CMake files to help compile it can be added. The first file to add is the top-level CMakeLists.txt file, which defines the project and lists the sub-directories in which further CMakeLists.txt files exist to recursively create the make system. The CMakeLists.txt top-level file in the MereIdea Main code base is initially written as follows:

# Set the minimum version to >= 2.6
cmake_minimum_required(VERSION 2.6)

# The project is MI_MAIN, so variables like ${MI_MAIN_SOURCE_DIR} and
# ${MI_MAIN_BINARY_DIR} can be used
project(MI_MAIN)

# Set up the top-level as an include directory so we can use
# #include <library_name/optional_sub_dirs/file.h>
# in the source to include a file
include_directories( ${MI_MAIN_SOURCE_DIR} )

# Now set the subdirectories to work through. Ensure these are done
# in the correct order
add_subdirectory (mi_hello)

This will set up the project, the include directory and call each library sub-directory to build in turn. The only library so far is the mi_hello compiler test library. To build the mi_hello library requires a CMakeLists.txt file in the mi_hello sub-directory:

# Give this library its own project name so we can
# refer to its build and source structures
project(MI_HELLO)

# Add the source files here
set (MI_HELLO_SOURCES
     hello.h
     hello.cxx
    )

# And add the library
add_library(mihello ${MI_HELLO_SOURCES})

# Put the library in a lib sub-directory
# Do for any shared, static and module libraries
set_target_properties(mihello PROPERTIES
                      ARCHIVE_OUTPUT_DIRECTORY →
${MI_HELLO_BINARY_DIR}/lib
                      LIBRARY_OUTPUT_DIRECTORY →
${MI_HELLO_BINARY_DIR}/lib
                     )

# And build the tests
add_subdirectory(tests)

This builds the mi_hello library, places it in a lib directory and descends into the tests directory. The tests directory then has the final CMakeLists.txt file for this little test:

# Add each test as an executable
# This may change to add_test later when the testing
# framework is added
add_executable(test_hello test_hello.cxx)

# Set the libraries each executable needs to link against
target_link_libraries(test_hello mihello)

Now the makefiles can be generated by changing directory to the build directory and typing:

> cmake ../src
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /use/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: →
/home/mi_user/code/mereidea/mi_main/build

You should see something similar to the above on a Linux box – Windows is covered later. For more control over the settings of the build, run ccmake instead of cmake and the CMake interface will appear enabling you to alter the settings, such as the build type. The libraries and tests can now be built by simply typing make at the command line. The mi_hello library will end up in build/mi_hello/lib, and the test executable will end up in build/mi_hello/tests. Change to this tests directory and type:

> ./test_hello
Hello, World!
Goodbye, World!

It all appears to have worked! Now we can commit the code to the repository, and test with Buildbot installation.


Committing code to Subversion Repository

The code is to be committed to the MereIdea Main repository. Before checking it in it is worth making a small note about Subversion and cross platform code.

One of the annoyances of transferring text files (such as code files) from one platform to another has been the different line ending codes. For example, where Windows uses the carriage return and line feed ending (\r\n), Linux uses just the line feed (\n). Subversion, however, can solve this problem for you to some extend by enabling you to set the eol-style property to native on the files being committed. The good thing is that, rather than having to set this property on every file you commit, you can set the eol-style property (and any other property) automatically for different types (or specific names) of files. This is set in the Subversion conf file. On Linux this can be found in your home directory inside a sub-directory named .subversion, on Windows Vista it is in somewhere like C:\Users\mi_user\AppData\Roaming\Subversion, and on Windows XP it is somewhere like C:\Documents And Settings\Chris\Application Data\Subversion. Near the end of the configuration file is the line:

# enable-auto-props = yes

This is commented out by default, so simply remove the # to turn on the automatic properties. Then just set up the properties required on the specific file types:

### Section for configuring automatic properties.
[auto-props]
### The format of the entries is:
###   file-name-pattern = propname[=value][;propname[=value]...]
### The file-name-pattern can contain wildcards (such as '*' and
### '?').  All entries which match will be applied to the file.
### Note that auto-props functionality must be enabled, which
### is typically done by setting the 'enable-auto-props' option.
*.c = svn:eol-style=native
*.cpp = svn:eol-style=native
*.cxx = svn:eol-style=native
*.h = svn:eol-style=native
*.txt = svn:eol-style=native
SConstruct = svn:eol-style=native
SConscript = svn:eol-style=native
SConfig = svn:eol-style=native
*.cmake = svn:eol-style=native
*.py = svn:eol-style=native
*.sh = svn:eol-style=native;svn:executable
Makefile = svn:eol-style=native
*.png = svn:mime-type=image/png
*.jpg = svn:mime-type=image/jpeg

This is an example that includes SCons files used in other MereIdea projects. These changes only need to be made if you are planning to submit code to the repository.

Now the code can be committed easily with the command (assuming that the files are in a directory named src):

> svn import src http://www.mereidea.com/svn/main/trunk →
-m "Initial import"

Then this can be checked out again (importing doesn’t alter the src tree to become a working directory) by removing the existing source directory and typing:

> svn co http://www.mereidea.com/svn/main/trunk src

After checking it still builds and runs the code is now ready to set up with Buildbot.

Setting up a Buildbot Slave

After committing the code and waiting a few minutes (the Buildbot Master SVNPoller has a pollinterval of an hour) it can be seen that the Master knows it has to do some builds:

The builders after an initial commit

It can also be seen that there are five nightly builds queued up as the Master was started a few days ago! Now the Linux Slave can be started and an iterative process of getting the Builder commands correct can begin.

Using the same Linux user as used to set up the Master (and after checking this user can check out and build the code), a directory name slave is created alongside the one named master. This was configured as a Slave with the command:

> buildslave create-slave ~/Buildbot/slave/ www.mereidea.com:9989 →
oSUSE_11.2_x86_64_gcc password1

This creates a directory (info) containing two configuration files that needed editing. These were admin which was edited to include an administrator name and email address for this Slave, and host in which some system information was entered (this can be seen at http://builds.mereidea.com/buildslaves/oSUSE_11.2_x86_64_gcc – the details under the heading Administrator were entered in the admin file, and the details under Slave Information were entered in the host file). Then, inside the slave directory, the Slave can be started with:

> buildslave start

This ran the pending builds on the new Slave and put the results on the web pages.


Fixing the Linux Build

The Linux build succeeded in checking out the code, but the Configure step of running CMake failed. This is because when the code is checked out from the repository it is put in a directory named build.  This can be fixed by amending the source.SVN by adding the workdir option:

...
svnup = source.SVN(mode='update',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk'),
                   workdir='src')
svnco = source.SVN(mode='clobber',
                   baseURL=svnbaseurl,
                   defaultBranch='trunk'),
                   workdir='src')
...

This will change the default directory to be src for the SVN checkout, but the configure step will still use build by default. The Master is then restarted to take account of the change, and a build forced (forceBuild was set temporarily to True on the WebStatus – set this in the authz_cfg variable in the master.cfg).

The whole thing now compiles, but the test fails. That is because a place holder (run_tests) was given as the command to execute for the test phase. This was changed to:

...
rtest = shell.Test(command='run_tests')
rtest = shell.Test(command='mi_hello/tests/test_hello')
...

The Linux build is now clean, and the Web Status shows green for both the Linux-change-build and the Linux-nightly-build.


Setting up the Windows Slave

Now the Linux build works with the simple code it is time to set up a Slave on the Windows machine and fix any problems with it.

The first step was to install all of the code necessary for Buildbot to work. This was done by simply following the instructions on this page with a couple of changes:

  • Only the buildslave code is needed, so only buildbot-slave-0.8.1.zip was downloaded and extracted.
  • The installed Python was 2.6.5, so in step 2 it was C:\Python26 and C:\Python26\scripts that were added to the path variable
  • After installing Twisted and typing trial –version an ImportError occurred stating that the zope interface was missing. The Zope website didn’t seem to have an installer for Python 2.6, but luckily  a Python egg was available for 2.6 here.
  • As the Zope interface was in egg form, setuptool (or distribute) needed to be installed so that easy_install could be used with the egg. Distribute was found here along with installation instructions.
  • Once the Zope egg was installed using easy_install then the command trial –version returned the Twisted version number.
  • It wasn’t necessary to alter the buildslave.bat file as detailed in step 9.

Once the buildslave code was installed, it was time to try a checkout and build of the code on the command line prior to setting up the Slave. First a batch file was created to call the appropriate vcvars file. This file just had the single line:

"c:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86

in it and was named setvcvars.bat (the name in the master.cfg file). It was placed in the directory C:\bin which is on the executable path so it can be executed by the Slave. The binary CMake directory was also placed in the path variable.

TortoiseSVN is usually used for handling Subversion on MereIdea Windows boxes, but this doesn’t come with a command line client, so SlikSVN was installed. Now the test could begin with the sequence of commands the Slave would be asked to execute on the Windows machine:

> setvcvars.bat
Setting environment for using Microsoft Visual Studio 2010 x86 tools.
> svn co http://www.mereidea.com/svn/main/trunk src
A    src\LICENSE.txt
A    src\mi_hello
...
> cd build
> cmake -G "NMake Makefiles" ../src
-- The C compiler identification is MSVC
-- The CXX compiler identification is MSVC
...
-- Configuring done
-- Generating done
-- Build files have been written to: C:/code/mi_main/build
> nmake
...
> mi_hello\tests\test_hello
Hello, World!
Goodbye, World!

So the command set works fine! Two concerns are whether the commands are all run in the same environment (i.e. will the setvcvars call be persistent), and will the forward slashes in the shell.Test function be converted by Python?
The only way to find out is to set up the Slave and see what happens. A directory was created to house the Slave, and then the same buildslave command was run as on Linux:

> buildslave create-slave C:\code\Buildbot\slave\ →
www.mereidea.com:9989 WinXP_VS2010 password2

The created files in the slave\info sub-directory (admin and host) were edited as with the Linux Slave, and then the Slave was started by running:

> buildslave start

in the Slave directory.

Note: The command ‘buildslave start’ on Windows never returns to the command prompt as it runs in the foreground.

Once the slave was started, the queued builds were run. The result was that the configure step (CMake) failed. When looking at the output from the configure it was seen that the CMake error produced was:

CMake Error: Could not create named generator "NMake

This shows that the command wasn’t being passed correctly to the command line. The -G “NMake Makefiles” was being split at the space after NMake. The first thing tried was to change the configure step in the master.cfg to escape the quotes:

wconfg = shell.Configure(command='cmake -G \"NMake Makefiles\" →
../src')

This made no difference, and using a double set of quotes (“”NMake Makefiles”") only changed the error to:

CMake Error: Could not create named generator ""NMake

Next the command was split up into command and arguments:

...
wconfg = shell.Configure(command='cmake -G "NMake Makefiles" ../src')
wconfg = shell.Configure(command=['cmake', '-G "NMake Makefiles"', →
'../src'])
...

This changed the error to:

CMake Error: Could not create named generator  "NMake Makefiles"

So now it became apparent that the quotes were being passed to CMake as part of the argument. The quotes were removed altogether, and the error changed to:

CMake Error: Could not create named generator  NMake Makefiles

This looks correct now, so what was the problem? The answer is there are two spaces between generator and NMake in the error message! A space seems to be passed to CMake as part of the generator name! A final change was made to the master.cfg to fix this:

...
wconfg = shell.Configure(command=['cmake', '-G NMake Makefiles', →
'../src'])
wconfg = shell.Configure(command=['cmake', '-GNMake Makefiles', →
'../src'])
...

This seems strange, but fixes the problem. There must be a reason for this, and at least part of the blame must go to CMake for thinking that using generator names containing spaces was a good idea! :-)

When the Master was restarted, and the build was forced again, the configure and compile passed, but the test failed. The reason for this was simple, and was easily fixed. Windows paths used backslashes, so the test execution requires backslashes. The change was made to the master.cfg:

...
rtest = shell.Test(command='mi_hello/tests/test_hello')
wrtest = shell.Test(command='mi_hello\\tests\\test_hello')
...
fwchg.addStep(rtest)
fwchg.addStep(wrtest)
...
fwngt.addStep(rtest)
fwngt.addStep(wrtest)
...

This fixes the test, and all builders now work!

All builders fully working!


Summary

This post describes the basics of setting up Buildbot Slaves on both Linux and Windows machines, using CMake as part of the build system. Some example code was created, along with the CMake files to generate the Makefiles necessary to build it. This was then committed to the MereIdea Main repository as Revision 2.

A Buildbot Slave was then created on the Linux box and errors in the build which occurred when it was started were fixed. The Windows Slave was then set up, which included installing all the necessary components, and errors resulting from this Slave were fixed.

There are still some outstanding issues to address in the next post:

  • A proper testing framework needs to be set up and integrated into the configure/compile/test cycle.
  • As the Windows Slave never returns from running, the environment under which the Slave is run should be used for running subsequent commands. This means if the Slave was run under a Visual Studio 2010 command prompt setvcvars may not be needed.
  • The Windows Slave could be set up as a service so that it will be run in the background. This is likely to require the setvcvars step to remain.

All of these issues will be covered in the next post, which should complete the initial set up of the Buildbot system.

Leave a Reply

You must be logged in to post a comment.