CMake

All ArmarX packages use CMake as a build system generator. Each ArmarX package is a CMake project that has a top-level CMakeLists.txt file. Within nested CMakeLists.txt files, targets (e.g. libraries, executables) are defined with their dependencies.

General information about CMake can be found here: https://cmake.org/cmake/help/v3.21/.

The following is intended to provide developers with a reference for creating and extending

  • libraries
  • components
  • statecharts
  • GUI plugins
  • tests

However, none of those CMakeLists.txt files has to be created manually. armarx-package is a command line tool that can be used for exactly this purpose. Developers only have to add source and header files as well as dependencies of their targets.

Lately, the ArmarX CMake functions have undergone a complete redesign. This redesign addresses several limitations of its predecessor and features the following

  • Clean and functional API: a single CMake statement is sufficient to create targets.
  • Built on the best-practices of modern CMake (CMake version >= 3)
  • Less error-prone e.g. missing optional dependencies will result in targets being properly disabled and not causing linker errors.

The legacy version is described here. All ArmarX packages should be migrated to the new version.

In the following, the new API will be described together with the armarx-package tool that creates the CMakeLists.txt files.

The top-level CMakeLists.txt

The top-level CMakeLists.txt describes the ArmarX package and all of its dependencies.

Generate

$ armarx-package init example_package

Example

cmake_minimum_required(VERSION 3.18)

find_package(ArmarXCore REQUIRED)
include(${ArmarXCore_USE_FILE})

set(ARMARX_ENABLE_DEPENDENCY_VERSION_CHECK_DEFAULT FALSE)

# Project definition.
armarx_enable_modern_cmake_project()  # Temporary until migration of ArmarXCore.
armarx_project(example_package)

add_subdirectory(etc)

# Required ArmarX dependencies.
# armarx_find_package(PUBLIC RobotAPI REQUIRED)

# Optional ArmarX dependencies.
armarx_find_package(PUBLIC ArmarXGui QUIET)

# Required system dependencies.
# armarx_find_package(PUBLIC Boost REQUIRED)

# Optional system dependencies.
# armarx_find_package(PUBLIC PCL QUIET)

add_subdirectory(source/example_package)

armarx_install_project()

API reference

The above example contained the following statements:

cmake_minimum_required(VERSION 3.18)

All new ArmarX CMake projects require at least CMake version 3.18. It is recommended to use CMake 3.21.

find_package(ArmarXCore REQUIRED)
include(${ArmarXCore_USE_FILE})

set(ARMARX_ENABLE_DEPENDENCY_VERSION_CHECK_DEFAULT FALSE)

This is needed to use all custom ArmarX CMake functions. These are always prefixed with "armarx_".

To define dependencies, add the following

# Required ArmarX dependencies.
# armarx_find_package(PUBLIC ... REQUIRED)

# Optional ArmarX dependencies.
armarx_find_package(PUBLIC ... QUIET)

# Required system dependencies.
# armarx_find_package(PUBLIC ... REQUIRED)

# Optional system dependencies.
# armarx_find_package(PUBLIC ... QUIET)

Please never use "find_package" without the "armarx_" prefix because armarx_find_package will automatically forwards the dependencies of this package to downstream packages. Also, don't use armarx_find_package in any nested CMakeLists.txt files. Dependencies should always be defined in the top-level CMakeLists.txt file!

Slice interface libraries

In many cases, each ArmarX package has a central slice interface library located in "source/{package_name}/interfaces". Slice interface libraries can also be coupled with components if those slice files describe (private) interfaces. A central slice interface library can be defined as such:

Example

armarx_add_ice_library(interfaces
    SLICE_FILES
        MyInterface.ice
    DEPENDENCIES
        other_package::interfaces
        # or any other dependency
)

API reference

  • SLICE_FILES defines a list of slice interface files that C++ (and python) code will be generated for.
  • DEPENDENCIES is a list of all dependencies needed for code generation.

Libraries

Libraries are located in "source/{PACKAGE_NAME}/"

In general, algorithms should be part of libraries, not components.

Generate

$ armarx-package add library example_library

Example

armarx_add_library(example_library
    SOURCES
        MyClass.cpp
    HEADERS
        MyClass.h
    DEPENDENCIES
        other_package::library_foo
        example_package::library_bar
    DEPENDENCIES_LEGACY
        a_legacy_dependency
)

API reference

  • SOURCES is a list of source files that will be compiled to create this library
  • HEADERS are the associated header files
  • DEPENDENCIES is a list of public dependencies. It is also possible to define its scope. To do so, use DEPENDENCIES_PRIVATE, DEPENDENCIES_PUBLIC and DEPENDENCIES_INTERFACE instead.
  • DEPENDENCIES_LEGACY is a list of dependencies that do not expose CMake targets. If only CMake variables are available, add the dependencies to this list. Examples are PCL, OpenCV, SOEM. It is also possible to define PUBLIC, PRIVATE and INTERFACE
  • STATIC | SHARED | INTERFACE | OBJECT. A flag that defines whether a static or shared library, an interface library (header-only) or object library (just the object files) wil be created. By default, a shared library will be built.

Components

Components are located in "source/{PACKAGE_NAME}/components".

Generate

$ armarx-package add component example_component

Example

armarx_add_component(example_component
    ICE_FILES
        ComponentInterface.ice
    ICE_DEPENDENCIES
        ArmarXCoreInterfaces
        # RobotAPIInterfaces
    # ARON_FILES
        # aron/my_type.xml
    SOURCES
        Component.cpp
    HEADERS
        Component.h
    DEPENDENCIES
        # ArmarXCore
        ArmarXCore
        ## ArmarXCoreComponentPlugins  # For DebugObserver plugin.
        # ArmarXGui
        ## ArmarXGuiComponentPlugins  # For RemoteGui plugin.
        # RobotAPI
        ## RobotAPICore
        ## RobotAPIInterfaces
        ## RobotAPIComponentPlugins  # For ArViz and other plugins.
    # DEPENDENCIES_LEGACY
        ## Add libraries that do not provide any targets but a ${FOO_LIBRARIES} variable
        # FOO 
    # If you need a separate shared component library you can enable it with the following flag.
    # SHARED_COMPONENT_LIBRARY
    # If you want to define your own main method, use this command
    # EXECUTABLE_FILES
    #   ...
)

API reference

General:

  • SOURCES is a list of source files that will be compiled to create this library
  • HEADERS are the associated header files
  • DEPENDENCIES is a list of public dependencies.
  • DEPENDENCIES_LEGACY is a list of dependencies that do not expose CMake targets. If only CMake variables are available, add the dependencies to this list. Examples are PCL, OpenCV, SOEM.
  • SHARED_COMPONENT_LIBRARY. A flag that defines whether a static or shared library, an interface library (header-only) or object library (just the object files) wil be created. By default, a shared library will be built.

Executable:

  • EXECUTABLE_FILES. If this list is empty (not set), a main function will be autogenerated which also reduces the compile time significantly. In most cases, this is sufficient. However, the user can also provide a main function here.

If the code-generation is used (default), the user must make sure that the macro 'ARMARX_REGISTER_COMPONENT_EXECUTABLE' is present in the component's source file.

Slice interface:

  • ICE_FILES is a list of slice interface files.
  • ICE_DEPENDENCIES is a list of the dependencies required to generate C++ code for the interface files.

Aron interface:

  • ARON_FILES list of Aron xml files that C++ code will be generated for

ARON library

Aron XML files should be placed in a subdirectory of the library they belong to called "aron". The following should added to the beginning of the CMakeLists.txt file of the library. In general, the aron target should be named *_aron

Example

armarx_add_aron_library(example_library_aron
    ARON_FILES
        aron/MyAronFile.xml
)

Usage example:

# use the Aron library
armarx_add_library(example_library
    ...
    DEPENDENCIES
        example_package::example_library_aron
)

API reference

  • ARON_FILES a list of Aron XML files

Statechart group

A statechart group is defined by a scgxml file, e.g. ExampleGroup.scgxml. In this file, required proxies and states are listed. This file will be parsed during the CMake configuration step such that all of the information in the scgxml file does not have to be added to the user-maintained CMakeLists.txt.

Example

armarx_add_statechart_group(ExampleGroup
    GROUP_FILE
        ExampleGroup.scgxml
    SOURCES
        ExampleGroupRemoteStateOfferer.cpp
    HEADERS
        ExampleGroupRemoteStateOfferer.h
    DEPENDENCIES
)

API reference

  • GROUP_FILE the statechart group scgxml file. This is optional. If it is not explicitly set, it will be derived from the statechart group name.
  • SOURCES is a list of source files that will be compiled to create this library
  • HEADERS are the associated header files
  • DEPENDENCIES is a list of dependencies.

Tests

Statecharts are located in "source/{PACKAGE_NAME}/statecharts".

Usually, the statechart group and states are created via the Statechart GUI. States must not be registered in the CMakeLists.txt. They will be discovered automatically by parsing the corresponding statechart group XML file (e.g. ExampleStatechartGroup.scgxml).

Generate

Tests are currently generated automatically for each library.

Example

armarx_add_test(example_test
    TEST_FILES
        my_test_file.cpp
    DEPENDENCIES
        ...
    DEPENDENCIES_LEGACY
        ...
)

API reference

  • TEST_FILES source and header files associated with this test
  • DEPENDENCIES is a list of dependencies.
  • DEPENDENCIES_LEGACY is a list of dependencies that do not expose CMake targets. If only CMake variables are available, add the dependencies to this list. Examples are PCL, OpenCV, SOEM.

Usually, only the library / component that is tested should be added to the DEPENDENCIES list.

Defining dependencies

To define dependencies between targets, add them to the DEPENDENCIES list e.g. "example_package::example_library". The namespace "example_package::" will be available for targets defined with the "armarx_" CMake functions.

Although it would be possible to also use the "example_library" target here, this is a bad practice. For consistency, always use the namespace, as only the target "example_package::example_library" will be available in downstream packages.