Wednesday, October 15, 2008

CMake (Part 1)

autoreconf, autoconf, automake, libtool... We are talking about the autotools. Everybody (almost) uses autotools, and it seems that everybody has to use them for good.

Luckily enough, this is not the case. There are alternatives. There are many alternatives. scons and cmake just to name two of the most used.

The problem the autotools solve is not an easy one. Unfortunately the solution is unnecessarily complex. Using autotools for simple projects is just a mess and often their full power is not needed. Consequently I looked for alternatives.

Even though I’m quite a Python enthusiast, I chose to learn cmake first (over scons).

Basically a cmake build script can be as simple as:

project(project_name)
add_executable(foo foo.c bar.h bar.c)

And building a library (shared or static) is not any more difficult:

project(mylib) 
add_library(mylib SHARED foo.c bar.h bar.c) 

Put the lines above in a CMakeLists.txt and run cmake. You can generate unix makefiles, code::blocks projects, xcode projects, visual studio projects, etc.

Nice. Moreover, writing a CMakeLists.txt is quite easier than writing the corresponding Makefile (and the repetitive stages of writing install and clean targets is automated as well).

What about more detailed instructions? Ok, ok, you are right. So...

Suppose you have this project (directories are followed by ‘:’, while files... well, you should recognize them ;) ):

foo:
    bar:
        bar.c
        bar.h
        barbar.c
        barbar.h
    foo.c
    opts.h
    opts.h

If we run ls -R, we get:

% ls -R
CMakeLists.txt bar            foo.c          opts.c         opts.h
./bar:
CMakeLists.txt bar.c          bar.h          barbar.c       barbar.h

Actually you want to create a libbar shared library and the main foo executable (which links libbar).

% cat foo.c
#include <bar/bar.h>
#include <bar/barbar.h>
#include "opts.h"

int main () {
    bar(1);
    barbar();
    opts("ciao");
    return 0;
}

Notice that <bar/bar.h> and <bar/barbar.h> are not local includes. Now we write the CMakeLists.txt for the foo directory. We start examining the main CMakeLists.txt:

% cat CMakeLists.txtPROJECT(foo) 
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)# bar must be processed 
ADD_SUBDIRECTORY(bar)# sets variable SOURCES to the project source files. 
SET(SOURCES foo.c opts.c opts.h) 
# puts the bar directory in the headers search path 
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) 
# puts the bar directory in the dynamic libraries search path 
LINK_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/bar) 

# links with libbar 
LINK_LIBRARIES(bar) 
# creates the executable 
ADD_EXECUTABLE(foo ${SOURCES}) 

I added comments in order to explain statements as you read them. Basically the ADD_SUBDIRECTORY command tells cmake to look for another CMakeLists.txt in the specified directory. Then we set the SOURCES variable. SET sets variables. If you need the value of a variable ${VARIABLE} is the way to get it (remember Make?).

${CMAKE_CURRENT_SOURCE_DIR} and ${CMAKE_CURRENT_BINARY_DIR} are variables set by cmake in order to simplify configuration.

Another useful command is MESSAGE (a ‘print’ command which can be used for debugging purposes).

Now, it is time for the CMakeLists.txt in bar:

% cat bar/CMakeLists.txtPROJECT(libbar) 
SET(SOURCES bar.c bar.h barbar.c barbar.h) 
ADD_LIBRARY(bar SHARED${SOURCES}) 

We are done!

% cmake . 
-- 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: /usr/bin/c++ -- works 
-- Detecting CXX compiler ABI info 
-- Detecting CXX compiler ABI info - done 
/Users/riko/src/foo/bar 
-- Configuring done 
-- Generating done 
-- Build files have been written to: /Users/riko/src/foo 

% make 
Scanning dependencies of target bar 
[ 25%] Building C object bar/CMakeFiles/bar.dir/bar.c.o 
[ 50%] Building C object bar/CMakeFiles/bar.dir/barbar.c.o 
Linking C shared library libbar.dylib 
[ 50%] Built target bar 
Scanning dependencies of target foo 
[ 75%] Building C object CMakeFiles/foo.dir/foo.c.o 
[100%] Building C object CMakeFiles/foo.dir/opts.c.o 
Linking C executable foo 
[100%] Built target foo 

More info on CMake can be found in these posts:
  1. CMake and Libraries

No comments: