How to Use CMake in ArmarX including Conventions and Tricks
See also
Todo:
Update link to conventions

Types of Dependencies in <tt>CMakeLists.txt</tt>

Libraries

Code that implements your business logic, i.e. what your program shall actually do, shall be written in libraries. Libraries help to keep your components as small as possible, and to re-use code.

To create a library, the CMakeLists.txt needs to have the following content:

armarx_add_library(some_target_name
SOURCES
./SomeClassName.cpp
./AnotherClassName.cpp
HEADERS
## optional, but useful:
# ./forward_declarations.h
./SomeClassName.h
./AnotherClassName.h
./AClassTemplate.h
./AClassTemplate.tpp
DEPENDENCIES_PUBLIC
## e.g.:
ArmarXCore
RobotAPICore
your_project_name::another_target_name
## potential further sections, but often not needed:
# DEPENDENCIES_PRIVATE
# ...
# DEPENDENCIES_INTERFACE
# ...
# DEPENDENCIES_LEGACY_PUBLIC
# ...
# DEPENDENCIES_LEGACY_PRIVATE
# ...
# DEPENDENCIES_LEGACY_INTERFACE
# ...
)

A Note on Target Names

Dependencies may contain only a single :: separator, which separates the project name from the target name. Hence, the target name itself may not contain :: separators, so it cannot directly reflect the namespace structure. By convention, an underscore _ is used instead.

For example, if your project is named project_name, and the target covers classes defined in the namespace project_name::tools::geometry, the corresponding target name would be core_geometry, resulting in the dependency name project_name::tools_geometry.

Note that this convention can lead to ambiguities: An underscore might either separate namespaces, which should usually coincide with the folder structure, or correspond to a snake_case namespace.

For example, the dependency name project_name::tools_point_cloud could either refer to the namespace project_name::tools::point_cloud or (in this case less likely) project_name::tools::point::cloud.

Components

Use armarx_add_component with the additional section ICE_DEPENDENCIES.

armarx_add_component(NameOfYourComponentTarget
ICE_FILES # (TODO: needed?)
ComponentInterface.ice
ICE_DEPENDENCIES
NameOfYourComponentTarget_ice
ArmarXCoreInterfaces # (TODO: needed?)
RobotAPIInterfaces # (TODO: needed?)
SOURCES
Component.cpp
HEADERS
Component.h
DEPENDENCIES
## e.g.:
ArmarXCore
ArmarXCoreComponentPlugins # For DebugObserver plugin.
ArmarXGuiComponentPlugins # For RemoteGui plugin.
armem
armarx_manipulation::core
your_project_name::some_target_name
NameOfYourComponentTarget_ice # (TODO: needed?)
## potential further section, but often not needed:
# DEPENDENCIES_LEGACY
## Add libraries that do not provide any targets but ${FOO_*} variables.
# ...
## If you need a separate shared component library you can enable it with the following flag.
# SHARED_COMPONENT_LIBRARY
)

Aron Data Types

Defining ARON targets and dependencies inside armarx_add_library is and old and deprecated approach. Instead, use armarx_add_aron_library. By convention, that target should have the same name as the library, extended by the suffix _aron.

armarx_add_aron_library(some_target_name_aron
ARON_FILES
aron/SomeDataType.xml
aron/AnotherDataType.xml
DEPENDENCIES
## dependencies to other aron types used in includes within the xml files, e.g.:
aroncommon
armem_human
armarx_manipulation::core_aron
)
armarx_add_library(some_target_name
SOURCES
aron_conversions.cpp
SomeDataType.cpp
AnotherDataType.cpp
HEADERS
forward_declarations.h
aron_conversions.h
SomeDataType.h
AnotherDataType.h
DEPENDENCIES_PUBLIC
## e.g.:
ArmarXCore
RobotAPICore
your_project_name::another_target_name
your_project_name::some_target_name_aron
## (TODO: is aroncommon indeed not needed as a dependency, as it is alway listed in the _aron target?)
# ...
)

Trick: Silently Failing Targets

When dependencies of a target cannot be fulfilled, the target is silently disabled. Making cmake fail in such a case is currently not an option due to existing ArmarX packages.

A consequence might be that you cannot execute a component of your project, since it has not been built successfully. Even worse, it might happen that you run an old version of your component and wonder why your code changes do not change its behavior as expected. Thus, you might be interested in knowing whether targets failed.

Default Solution

The most direct option is to check the cmake output with cmake's default settings. This means to check whether after a line like

== Configuring library 'target::name' ...

there is a following line like

-- Target 'target::name' disabled because the following dependencies were not found:

More Comfortable Solution

A slightly more comfortable solution is to let cmake create a summary of the targets. To do so, e.g. ensure your workspace is activated, go to the build folder of your project and open cmake-gui:

cd build
cmake-gui

Set the path of the source code and of the build directory. Then, enter e.g. summary in the search field, to find the property ARMARX_CMAKE_ADDON_FEATURE_SUMMARY. Enable it using the checkbox, and click configure.

Now, every time you execute cmake (including when doing so inside qtcreator), you will get a summary of targets that have or have not been built:

== Feature summary:
-- The following features have been enabled:
* some_target_within_your_project
* another_target_within_your_project
* ...
-- The following OPTIONAL packages have been found:
* Coin
* ArmarXGui
* Qt5
* ...
-- The following REQUIRED packages have been found:
* ArmarXCore
* Ice (required version >= 3.7)
* VisionX
* Boost (required version >= 1.40.0)
* Eigen3
* ...
-- The following features have been disabled:
* this_target_has_failed_which_might_be_unintended
* ...

In the last category, you see a list of targets that have been disabled, which might not be what you had intended. Hence, you can more easily see whether problems have occurred.