Wednesday, December 9, 2009

How CMake simplifies the build process: Part 2 (Advanced build system)

Hello everyone,
My previous post explained how CMake could manage a simple project (containing only one definition and implementation file).  Now we can have a look at a complex project, containing a common shared library, examples and unit tests. In this case, instead of a dumb HelloWorld project, I've chosen the Itemviews-NG project, which is an experimental project hosted by the Qt Labs and currently uses qmake as build system. The project itself consists of the following:
  • A common shared library containing the base classes;
  • A collection of examples for each class from the library;
  • A collection of unit tests for each class from the library;
NOTE: Documentation won't be created for now - Itemview-NG uses qdoc to generate documentation, which AFAIK isn't well integrated with CMake yet. There will be another post about using doxygen with CMake, together with lcov coverage and gcov HTML output integrated.
As a starting point, the project itself contains various files which needs to be processed by the MOC. To simplify this process, CMake can use the automoc tool which automagically finds the required files to be preprocessed and includes them at the target build. Qt libraries and includes are also provided by the Qt4 cmake package (built-in). To use Qt4 plus automoc cmake packages, you can include the following code on the CMakeFiles.txt located at the project's root directory:
# Qt4 package is mandatory
find_package(Qt4 REQUIRED)
include(${QT_USE_FILE})

# Automoc4 is also mandatory
find_package(Automoc4 REQUIRED)
Now let's see how the CMakeFiles.txt from the src/ directory changes:
set(CMAKE_INCLUDE_CURRENT_DIR ON)

# Create a variable containing a list of all implementation files
file(GLOB itemviews-ng_SOURCES *.cpp)

# The same applies for headers which generates MOC files (excluding private implementation ones)
file(GLOB itemviews-ng HEADERS *[^_p].h)

# Now the magic happens: The function below is responsible for generating the MOC files)
automoc4_moc_headers(itemviews-ng ${itemviews-ng_HEADERS})

# Creates a target itemviews-ng which creates a shard library with the given sources
automoc4_add_library(itemviews-ng SHARED ${itemviews-ng_SOURCES})

# Tells the shared library to be linked against Qt ones
target_link_libraries(itemviews-ng ${QT_LIBRARIES})

# Installs the header files into the {build_dir}/include/itemviews-ng directory
install(FILES ${itemviews-ng_HEADERS} DESTINATION include/itemviews-ng)

# Installs the target file (libitemviews-ng.so) into the {build_dir}/lib directory
install(TARGETS itemviews-ng LIBRARY DESTINATION lib)
Now every example or test from the project which uses that library could use the target itemviews-ng as a link dependency. As each examples and tests are compiled in a similar way, we can use functions which eases the compilation process. Below is an example of those functions:
function(example example_NAME example_SOURCES example_HEADERS)
    automoc4_moc_headers(${example_NAME}_example ${examples_HEADERS})

    # The third argument (resource files) is optional:
    set(example_RESOURCES ${ARGV3})
    if(example_RESOURCES)
        automoc4_add_executable(${example_NAME}_example
            ${example_SOURCES} ${example_RESOURCES})
    else(example_RESOURCES)
        automoc4_add_executable(${example_NAME}_example
            ${example_SOURCES})
    endif(example_RESOURCES)

    target_link_libraries(${example_NAME}_example
        itemviews-ng ${QT_LIBRARIES})
endfunction(example)

function(test test_NAME test_SOURCES)

    set(test_RESOURCES ${ARGV2})
    if(test_RESOURCES)
        automoc4_add_executable(${test_NAME}_test
            {test_SOURCES} ${test_RESOURCES})
    else(test_RESOURCES)
        automoc4_add_executable(${test_NAME}_test
            ${test_SOURCES})
    endif(test_RESOURCES)

    target_link_libraries(${test_NAME}_test
        itemviews-ng ${QT_LIBRARIES})
    include_directories(${itemviews-ng_BINARY_DIR}/tests/${test_NAME})

    add_test(${test_NAME} ${test_NAME}_test)
    add_dependencies(check ${test_NAME}_test)
endfunction(test)
The functions above can be inserted into a .cmake file (eg: InternalMacros.cmake) that goes into cmake/modules directory. To load it, put the following line inside CMakeLists.txt from the root directory:
include(InternalMacros)
Now you can create a CMakeLists.txt for each example like this:
set(mycustomexample_SOURCES
    mycustomexample.cpp
    main.cpp
    )

set(mycustomexample_HEADERS
    mycustomexample.h
    )

example(mycustomexample "${mycustomexample_SOURCES}" "${mycustomexample_HEADERS}")
The same applies for tests:
test(mycustomtest "${mycustomtest_SOURCES}")
I've made a branch cmake at my personal gitorious itemviews-ng clone. You can have a look at it to see how does the code explained above behaves.

Monday, December 7, 2009

How CMake simplifies the build process: Part 1 (Basic build system)

Hi!

After a long period of abstinence from blogging (mainly because I was freaking my head out with internal work), I'm going to share my achievements on learning how to use CMake. I must confess as a previous autotools (aka. auto-hell) and qmake user I got a little bit biased about CMake, but after looking at a few examples and reading some documentations I've found that it is way easier to learn and use than the previous build systems I've used. Basically, CMake uses a series of macros and functions which finds the required components for you, in a simple and precise way - this means no unnecessary dependencies!

As an example, I've found a very useful macro called MacroOutOfSourceBuild, which requires the user to build the source code outside the source code directory (build directory != source directory). This ensures the user uses a shadow build directory, which is a very good practice, by the way. Enough of talking, let's have a look at some code examples. Suppose you have a small project "Hello World" (download source code here) which has its source files arranged like the diagram below:

helloworld/
  \_ CMakeLists.txt
  \_ cmake/
    \_ modules/
      \_ MacroOutOfSourceBuild.cmake
  \_ src/
    \_ CMakeLists.txt
    \_ main.cpp
    \_ helloworld.cpp
    \_ helloworld.h

In the case above, the files helloworld.cpp, main.cpp and hello.h are located inside the src/ directory, which is itself located on the project's root directory. CMake states that each project directory (including recursive sub-directories) which contains source code should have a fille called CMakeLists.txt, which contains the build instructions for each directory. As the "Hello World" project does have two directories (root directory plus src/ directory), then two CMakeLists.txt files are created (shown below):
  • CMakeLists.txt - root directory:
# Project name is not mandatory, but you should use it
project(helloworld)

# States that CMake required version must be >= 2.6
cmake_minimum_required(VERSION 2.6)

# Appends the cmake/modules path inside the MAKE_MODULE_PATH variable which stores the
# directories of additional CMake modules (eg MacroOutOfSourceBuild.cmake):
set(CMAKE_MODULE_PATH ${helloworld_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})

# The macro below forces the build directory to be different from source directory:
include(MacroOutOfSourceBuild)

macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out of source build.")

add_subdirectory(src)
  • CMakeLists.txt - src/ directory:
# Include the directory itself as a path to include directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)

# Create a variable called helloworld_SOURCES containing all .cpp files:
set(helloworld_SOURCES helloworld.cpp main.cpp)

# For a large number of source files you can create it in a simpler way
# using file() function:
# file(GLOB hellworld_SOURCES *.cpp)

# Create an executable file called helloworld from sources:
add_executable(helloworld ${helloworld_SOURCES})
Now you create a shadow build directory (eg. build/) and build your project, as shown with the commands below:
$ mkdir build
$ cd build
$ cmake ..
$ make
That's it! This finishes the first part of a series of useful tips about using CMake as the build system from basic to complex projects. If you need further information, please have a look at the tutorial website.

Monday, August 17, 2009

Introduction to Qt @ Debian Day '09 Amazonas

Hi again!
I'm pleased to announce that me and Frederico Duarte will be giving an "Introduction to Qt" mini-course at Debian Day '09 Amazonas, which will be placed this 22th August @ UniLaSalle, Manaus. The course will start at 9am and will proceed until 11am. There we'll try to explain some Qt basic together with a fancy animated example using Qt's newest frameworks. There shall be a plenty of other interesting courses given by my colleagues Éverton Arruda (BrDesktop installation), Adenilson Cavalcanti (PyS60: Python para S60 Nokia) and Francisco Alecrim (Kernel Linux development for newcomers), among others. More info about the Debian day conference at Amazonas can be found on Br-Linux.org and the official site. Hope to see you there!

Friday, June 26, 2009

Python-purple at FISL 10

(Me, Ricardo Salveti and Anderson Briglia - thanks to Henry Vieira for the picture!)


Yesterday me and Anderson Briglia spoke about python-purple (a libpurple binding for Python written using Cython) at FISL 10, the most valuable free software event in Latin America. Actually, we had to compete with Richard Stallman's presentation together with the brazilliam soccer team game (which was happening on the same time!). Public acceptance was good and we hope to get new developers to join the team :) Anyway, we made available the presentation (sorry, brazillian portuguese only) at the project's documentation section.

[]s!

Friday, June 5, 2009

Qt's Animation framework API updates

Hi again,
Since my previous post, we've been using the QAnimationState class together with the convenience method addAnimateTransition() to get animated transitions between states. Now the API's got even cleaner and easier to understand, you don't have to use a special state in order to associate animated transitions to it any longer ;) I've updated the Qt's documentation about the animation framework, you can read it below:

Animations and States

When using a state machine, we can associate an animation to a transition between states using a QSignalTransition or QEventTransition class. These classes are both derived from QAbstractTransition, which defines the convenience function addAnimation() that enables the appending of one or more animations triggered when the transition occurs.

We also have the possibility to associate properties with the states rather than setting the start and end values ourselves. Below is a complete code example that animates the geometry of a QPushButton.
QPushButton *button = new QPushButton("Animated Button");
button->show();

QStateMachine *machine = new QStateMachine;

QState *state1 = new QState(machine->rootState());
state1->assignProperty(button, "geometry", QRect(0, 0, 100, 30));

machine->setInitialState(state1);

QState *state2 = new QState(machine->rootState());
state2->assignProperty(button, "geometry", QRect(250, 250, 100, 30));

QSignalTransition *transition1 = state1->addTransition(button,
SIGNAL(clicked()), state2);
transition1->addAnimation(new QPropertyAnimation(button, "geometry"));

QSignalTransition *transition2 = state2->addTransition(button,
SIGNAL(clicked()), state1);

transition2->addAnimation(new QPropertyAnimation(button, "geometry"));

machine->start();
You can find the most up-to-date documentation about the Animation Framework and State Machine framework here: http://doc.trolltech.com/4.6-snapshot/index.html