This website uses browsing/session and functional cookies to ensure you get the best experience. Learn More

Better Repository

From Wiki for iCub and Friends
Jump to: navigation, search

These instructions only apply to YARP > 2.3.23 and iCub > 1.1.13 software versions

See Better Repository old for older instructions

Author: Lorenzo Natale

For historical reasons the iCub repository and the build system have been designed in a way that make it quite difficult to provide the functionalities of a mature package.

In parallel cmake has evolved quite a lot since version 2.4 and offers much more robust and useful features than the ones we relayed upon when we started the project.

Contents

Repository organization

The root of the repository contains various directories.

  • admin: contains scripts for maintainance, of no interest for most developers
  • tutorials: some tutorials on the software
  • pc104: pc104 startup scripts
  • license: keep information about licensing

Code is now contained in two different directories main and contrib.

  • contrib stores modules that can be compiled individually. This is a good place to upload new modules that are still under test. No automatic tests are executed on these directories.
  • main this is the directory that contains the main build. This structure contains code that respects iCub standards (dependencies, supported operating systems and compilers, etc.). To avoid causing troubles to other users place here only modules you are reasonably confident will compile correctly. To help you check this automatic tests run periodically on this build.
  • main/conf contains cmake helper code, including FIND* scripts
  • main/src contains source code. This is further organized in examples, libraries, simulators and tools with obvious meaning. In addition the modules directory contains iCub modules (notice that this is different from the old modules directory which is now called libraries/icubmod). The tools directory contains programs of general use including graphic interfaces. Finally inside core we place modules that are required to operate the robot (like iCubInterface)
  • main/app: stores applications scripts and configuration files.

The philosophy

We still would like to keep at the minimum the amount of work required to maintain the repository. Users should be allowed to add new modules to the repository or the main build without much effort. However we also would like to give the possibility to use the software from other packages or libraries, in particular we would like this to be possible in two configurations:

  • out-of-source or in-source builds
  • from installation

We do a first distinction between modules that are "visible" only within the iCub repository and modules that are "exported", or in other words, can be used externally with respect to the big iCub build. Another distinction is between executables and libraries. Executables are easy to export: they do not provide header files or other resources. It is enough to put them in the system path. This can be done at install time, or by appending the binary directory where executables are built to the system path (something like ICUB_DIR/bin). Libraries are a bit more complicated because they define symbols, have header files and dependencies.

It is also important to stress that the iCub build is not just a normal package with a set of libraries and executables. We like to see the build more like a collection of modules, made of executables and libraries. This is how external packages view the iCub software.

Executables

Adding an executable to the build is simple, it does not take more than a few lines of cmake code:

 project(mymodule)
 set(folder_source main.cpp myclass.cpp)
 set(folder_header myclass.h)
 source_group("Source Files" FILES ${folder_source})
 source_group("Header Files" FILES ${folder_header})
 add_executable(mymodule ${folder_source} ${folder_header})

To cope with intallation this is enough:

 install(TARGETS mymodule DESTINATION bin)

See also template files at the end of the page.

Using YARP or other packages

If you are using YARP or other packages you do not have to call the macro find_pacakge(). This was done already for you at the beginning of the build.

You have, however, to add the include directory and link the library. For YARP before calling add_executable:

 include_directories(${YARP_INCLUDE_DIRS})

and afterwards:

 target_link_libraries(mymodule ${YARP_LIBRARIES})

Important with respect to the previous repository this has changed, in the old style build system calling find_package(YARP) was enough.

See also template files at the end of the page.

Libraries

You should be aware that a library has a public and a private interface. The public interface is a set of header files that users of the library will be offered to include; the private interface is a set of header files that are included only by code inside the library. This distinction is important. Header files that are the public interface of the library should be clean; they should define only the minimum set of symbols required by the user, and avoid polluting the main scope with unnecessary symbols (ideally all symbols should be hidden in a separate namespace, but for now let's not be too picky about this). Private header files are only required when you compile the library and, in principle, could be destroyed afterwards (ok don't do that).

It is a good idea to maintain the distinction between public and private interface at the file system level (public header files in one directory, private header files in another one).

This is because at install time only public header files are copied to the target destination (maybe with the exception of inline files/templates which should also be visible).

Files structure

Everyone is free to organize files in any way they like, clearly there is not a single solution that works.

How you organize header files is the combined result of how you include them in your code and how you configure the compiler.

The only restriction in this case is that public files are shared between external code and the code of the library, so, whatever you do, make sure you include them in the same way. This is the point where you need to be careful and follow normal practices.

When external users include a public file of a library they expect to do it like:

 #include <library-name/header-file.h>

For many reasons this is preferable than just:

 #include <header-file.h>

In our case this becomes:

 #include <iCub/header-file.h>

Even better, in case you define a namespace:

 #include <iCub/namespace/header-file.h>

Usually you instruct your compiler to know where to start looking for header files, so you do not have to wonder how the compiler will be able to fine the iCub directory. However, this means that the local directory structure of the files in your library should match the one above.

In short, all public header files should be placed in a directory called iCub. Since you want to be clean, this is also inside a directory called include (to keep it separate from the src directory where you put source files).

If you define a namespace, make a new directory there (in include/iCub).

Now private header files can go in a separate directory of your choice, e.g. include/private and included as:

#include "include/private/local.h"

We really do not care because it will be your job, as a maintainer of the library, to make sure everything compiles.

Using a library

We make a difference between using the library within the repository and importing it as an external package.

Both cases involve (obviously) linking against the library, and, sometimes, adding the appropriate path to header files.

Within the repository

CMake knows what the targets in the build are. Linking a library is as easy as linking against the corresponding target/project.

If lib1 is a library compiled in the main build, then it is enough to do something like:

 include_directories(${lib1_INCLUDE_DIRS})
 add_executable(example main.cpp)
 target_link_libraries(example lib1)

where example is the new project/executable and lib1 is the name of the project that compiles lib1 (often the two are coincident).

Adding the path to header files is a bit more complicated. Since you are within the iCub main build you can rely on a set of cmake variables that are already been filled for you. We enforce the standard by which each library should define the variable libname_INCLUDE_DIRS.

This involves obviously linking against the library, and, in some cases, including the appropriate header files.

We view the iCub repository as a collection of libraries, so we give the possibility to link against each individual library. This might turn our useful in the future to avoid conflicts. As far as header files are concerned it is enough to instruct the compiler to point to the root installation or build directory.

Externally to the repository

In cmake:

 find_package(ICUB)
 
 include_directories(${ICUB_INCLUDE_DIRS})
 add_executable(example main.cpp)
 target_link_libraries(example lib1)

This creates a project called example and links the library lib1 -- supposedly from the iCub package.

We organized the iCub software so that the code above can work in cases the iCub software is installed or exported from the build.

As anticipated above, path to header files, on the other hand, follows this convention:

All libraries header files are located in a single root directory. In case you export the build tree this is $ICUB_DIR/include, otherwise it is where you installed the library (this can be one of the system directories like C:/Program Files/iCub/include or /usr/lib/include).

Inside the root include directory each namespace or library will create its own directory.

For example:

 $ICUB_DIR/include/iCub/iKin
 $ICUB_DIR/include/iCub/contrib
 ...

Since header files are included with the prefix iCub/namespace (e.g. #include<iCub/iKin/iKin.h>) it is enough to instruct the compiler to know the root of the header files (i.e. where the "/include/iCub" directory is) to work. This is taken care of, by the ICUB_INCLUDE_DIRS cmake variable.

Writing a library

We distinguish two types of libraries.

  1. libraries that are used locally by a few modules, we call these internal libraries
  2. libraries that are exported to be used by users of the iCub package, we call these exported libraries

Internal libraries

Nothing special has to be done in this case. You add the library to the build as you normally would in cmake.

For example:

 project(mylib)
 file(GLOB folder_source *.cpp)
 file(GLOB folder_public_headers include/iCub/*.h)
 file(GLOB folder_private_headers include/private/*.h)
 set(folder_header ${folder_public_headers} ${folder_private_headers})
 # Declare groups of source and header files -- makes things pretty in MSVC.
 source_group("Source Files" FILES ${folder_source})
 source_group("Header Files" FILES ${folder_header})
 # need to define a cache variable to keep track of include directories
 set(mylib_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include/iCub CACHE STRING "mylib 
     include directory")  
 include_directories(${PROJECT_SOURCE_DIR}/include/private)
 include_directories(${PROJECT_SOURCE_DIR}/include/iCub)
 add_library(mylib ${folder_source} ${folder_header})

You can now use this library in other projects within the repository. As pointed out above this can be done simply by:

 project(mymodule)
 include_directories(${mylib_INCLUDE_DIRS})
 add_executable(mymodule main.cpp)
 target_link_libraries(mymodule mylib)

Important note: here lib1 is the name of the project and not a cmake variable (as opposed to ${mylib _LIBRARIES})

External libraries

Things here become a bit more complicated because you want to export the library so that it can be used from external packages/users. Of course this is normally trivial, what is interesting is to have cmake do that.

The cmake code we have written above is still valid. In addition you have to instruct cmake to produce code that will be used afterwards to find and link the library, and add the correct directory that contain the public interface of the library.

Of course you can write your own cmake code to do this. We provide the macro icub_export_libary to automate the process. The work of this macro is to:

  1. generate cmake code for importing the library
  2. keep track of include directories required to use the library
  3. install the library public header files

The macro has the following parameters:

 icub_export_library (libraryname
                      [INTERNAL_INCLUDE_DIRS dir1 dir2 ...] 
                      [DESTINATION dest]
                      [VERBOSE]
                      [FILES file1 file2 ...])
  • libraryname: the name of the target.
  • INTERNAL_INCLUDE_DIRS a list of directories that contain header files required when building in-source.
  • VERBOSE: ask to print parameters (for debugging).
  • DESTINATION: destination directory to which header files will be copied (relative path w.r.t. CMAKE_INSTALL_PREFIX). If your library does not define a namespace it is sufficient to specify here include/iCub; all header files specified in folder_header will be copied to ${CMAKE_INSTALL_PREFIX}/include/iCub.
  • FILES: a list of header files that will be copied to destination.

Some examples.

Common usage of this functions are:

  icub_export_library(libname)

In case your libraries does not have header files.

  icub_export_library(libname, 
                      INTERNAL_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/include/
                      DESTINATION include/iCub
                      FILES ${folder_header})

If the header files of your library are self-contained this is all you need to do (remember to tell cmake about library dependencies by calling target_link_libraries, as explained below). In some cases, however, the header files of your library directly include those of other packages/libraries. We sort of strongly discourage but support this. In this case you can use the following options:

 icub_export_library (libraryname
                      [INTERNAL_INCLUDE_DIRS dir1 dir2 ...] 
                      [EXTERNAL_INCLUDE_DIRS dir1 dir2 ...]
                      [DEPENDS target1 target2 ...]
                      [DESTINATION dest]
                      [VERBOSE]
                      [FILES file1 file2 ...])
  • EXTERNAL_INCLUDE_DIRS a list of directories externals to the iCub build that contain header files needed by users of the library, like for example a package you depend upon (e.g. OPENCV_INCLUDE_DIRS).
  • DEPENDS a list of dependencies; these are targets built within the repository. Important CMake should parse these targets *before* the current target (check sub_directories(...)).


Important: the macro icub_export_library does not substitute calls to target_link_libraries, you have to do this manually in your cmake code.

To see what is going on under the hood of the call icub_export_library() see below.

Adding dependencies to libraries

To work properly CMake needs to know the dependencies of all libraries. If lib1 depends on lib0 you have to tell CMake, like this:

 target_link_libraries(lib1 lib0) #target lib1 depends on target lib0

Notice that this is not useless as it might seem (you do not link static libraries when building a static library). This tells cmake that lib1 depends on lib0 (lib1 inherits dependencies of lib0, if any) and is actually fundamental to avoid strange linking errors (especially in Linux where the link order does matter).

For adding dependencies that are external to the build you have to rely on the corresponding variables defined by the Find script, e.g.:

 target_link_libraries(lib1 ${OpenCV_LIBRARIES} ${YARP_LIBRARIES})

Under the hood

What is going on under the hood (some technical details, can be skipped).

Basic notion. CMake has to generate a file called icub-config.cmake, which includes the code to import all the libraries built (and exported) by the project. This files is used by CMake to locate and import the package when you call find_package(iCub).

There are two such files, one goes to the build directory and is used when you "export the build tree" (i.e. you link the libraries from the location where they have been built). The other one is copied at install time to ${CMAKE_INSTALL_PREFIX), and it is used when you important the library after it has been installed. The two files have the same name, CMake will use the one it finds (CMake uses certain rules, in short: $ICUB_DIR env variable, some system directories...).

What does icub-config.cmake do? Broadly speaking this file is responsible for creating cmake variables that allows using the project in users code, namely:

 ICUB_FOUND TRUE/FALSE, when the package is found
 ICUB_INCLUDE_DIRS a directory (or a list) that should be added to the compiler include path
 ICUB_LIBRARIES exhaustive list of all the libraries exported by the ICUB build

It also imports all the targets (libraries) that have been compiled and exported by the package (we use the export mechanism available in CMake 2.6). The export command generates a file that contains all the code that needs to be executed to import the package.

This is implemented in different ways at install time and when exporting the build tree.

Installation

This situation is a bit easier because the main build knows where all the header files will go. Some libraries might require header files that are external to the repository. ICUB_INCLUDE_DIRS needs to be generated by scanning a list of targets and the property:

   * EXTERNAL_INCLUDE_DIRS list of header files external to the repository

This list of targets (and property) is generated by subsequent calls to icub_export_library.

The code that set the ICUB_INCLUDE_DIRS variable and relative variable for each library is automatically generated (see conf/ExportForInstall.cmake) and goes into icub-export-build-includes.cmake.

The list of targets that need to be imported is generated by calling export() and is called icub-export-install.cmake.

Both files are merged in icub-config.cmake, which creates ICUB_FOUND and includes icub-export-install.cmake and icub-export-build-includes.cmake.

Main code is in conf/ExportForInstall.cmake.

icub-config.cmake is first generated in the build directory with the name icub-config-for-install.cmake and then copied and renamed at install time to ${CMAKE_INSTALL_PREFIX}/lib/ICUB; the installer also copies icub-export-install.cmake and icub-export-build-includes.cmake.

Exporting the build

In this case header files are scattered through the source tree, ICUB_INCLUDE_DIRS needs to be generated by scanning a list of targets and the following properties:

  • INTERNAL_INCLUDE_DIRS list of header files internal to the repository, propagated when exporting the build tree
  • EXTERNAL_INCLUDE_DIRS list of header files external to the repository, propagated then build tree is exported or installed
  • Both properties are merged in the INCLUDE_DIRS property

These properties are populated by subsequent calls to icub_export_library().

The code that set the ICUB_INCLUDE_DIRS variable is automatically generated (see conf/iCub/ExportBuildTree.cmake) and goes into icub-export-build-includes.cmake.

The list of targets that need to be imported is generated by calling export() and is called icub-export-build.cmake.

Both files are merged in icub-config.cmake, which creates ICUB_FOUND and includes icub-export-build-includes.cmake and icub-build-export.cmake.

Main code is in conf/ExportBuildTree.cmake

CMake options and dependencies

The build is organized so that dependencies are checked one at the beginning of the build. For each package the following variables are set:

 ICUB_HAS_<package> read only, true if the package was found or false otherwise
 ICUB_USE_<package> this variable can be set to true or false if we want to 
 add this package and dependent modules to the build

in addition, each Find* script will create variables for using the package. We try to enforce the following standard:

 <package>_INCLUDE_DIRS include directories
 <package>_LIBRARIES libraries
 <package>_FOUND true/false (redundant with ICUB_HAS_<package>)

where <package> is the name of the package (respecting capitalization), see the list of iCub dependencies as package names.

There are some packages that, however, are required and cannot be disabled in the build. For example this is the case for YARP.

Modules are compiled only if the available dependencies are found and activated by the user. Please do not use conditionals in each module's cmake code; dependencies are checked externally to modules.

CMake scripts

Useful scripts are provided in the YARP and ICUB repositories:

  • scripts available in yarp:
 FindAtlas
 FindGtkPlus
 FindGThread
 FindACE
 FindBoost
 FindOpenCV
 FindPortAudio
  • scripts available in iCub:
 FindGtkMM
 FindODE
 FindIPP
 FindIPOPT
 FindQt3.cmake
 FindOpenGL.cmake
 FindGLUT.cmake
 FindESDCANAPI
 FindCFW2CANAPI
 FinPLXCANAPI


To have access to these scripts you need to configure cmake. Since the location of CMake scripts change at install time, iCub and YARP define convenience variables ICUB_MODULE_PATH and YARP_MODULE_PATH that can be appended to CMAKE_MODULE_PATH:

list(APPEND CMAKE_MODULE_PATH ${YARP_MODULE_PATH})
list(APPEND CMAKE_MODULE_PATH ${ICUB_MODULE_PATH})

In the main build this is done automatically for you, so Find* scripts are available. However, if you are writing external modules (i.e. modules in contrib), you have to do this explicitly in your CMakeLists file.

CMake and contrib

The root CMakeLists file in iCub main performs a series of useful operations such as setting global options and checking dependencies. Implementing the same behavior can be time consuming, tedious and error-prone. The main build provides a set of CMake files that perform these task and can be used to write modules in contrib, together with files provided by YARP.

These files are installed with the binaries so they are available with the iCub software:

  • iCubOptions.cmake: defines global options (disable warning in msvc, append postfix 'd' to debug libraries, set optimized flag by default, enable options for adding rpath)
  • iCubFindDependencies.cmake: checks all the iCub dependencies, creates ICUB_USE_* variables (see above)

The YARP software provides the following file:

  • YarpInstallationHelpers.cmake: provides macros to handle the installation of application-related files (configuration files, module descriptions, application descriptions)

You can use these files in your build like this:

 # find the YARP package
 find_package(YARP REQUIRED)
 list(APPEND CMAKE_MODULE_PATH ${YARP_MODULE_PATH})
 
 # load support files 
 include(YarpInstallationHelpers)
 # find the iCub package
 find_package(ICUB REQUIRED)
 list(APPEND CMAKE_MODULE_PATH ${ICUB_MODULE_PATH})
 # load support files 
 include(iCubOptions)
 include(iCubFindDependencies) #optional

Furthermore, the ICUBcontrib package has been created inside contrib. This must be configured with CMake and installed prior to configuring all other contrib packages. The configuration step simply consists in deciding the installation prefix, which will then be shared by all contrib packages. These lines should then be added to contrib modules' CMakeLists:

 # find the ICUBcontrib package
 find_package(ICUBcontrib REQUIRED)
 list(APPEND CMAKE_MODULE_PATH ${ICUBCONTRIB_MODULE_PATH})
 # load support files 
 include(ICUBcontribHelpers)
 # change default CMAKE_INSTALL_PREFIX to ICUBcontrib's one; print warning if the user changes it
 icubcontrib_set_default_prefix()

Template Files

Simple template for modules in main

Simple template for libraries in main

Simple template for modules in contrib

Simple template for libraries in contrib

Personal tools
Namespaces

Variants
Actions
Navigation
Print/export
Toolbox