CMake coding standards ====================== In order to make your ROS packages most maintainable and avoid several frequent errors, follow the standards below. Do care about the style your cmake code is written in. For inspiration, look at the guideline examples here: `CMake Style recommendations `_ `KDE cmake coding guidelines `_ **Call catkin_package before any targets** The following lines must always appear the CMakeLists.txt in this order:: cmake_minimum_required(VERSION 3.0.2) project(myproject) find_package(catkin REQUIRED ) catkin_package(<...>) While there might be additional function calls before catkin_package() (e.g. for finding other libraries or generating messages) it must be invoked before any targets are added. **Use ${PROJECT_NAME} wherever possible** Use ``${PROJECT_NAME}`` for global variables, targets and labels instead of repeating the project name manually or using fixed names. You can use ${PROJECT_NAME} as prefix for global variable names as well, as shown in examples below. Variables become global if they are set using the CACHE argument or created using the option() macro. After you defined your project name like this:: project(myproject) dont do this:: catkin_add_gtest(test ...) add_executable(myproject ...) set(use_feature 42 CACHE STRING "description") option(use_feature "on or off" OFF) macro(xyz) ... do this instead:: catkin_add_gtest(${PROJECT_NAME}_test ...) add_executable(${PROJECT_NAME} ...) set(${PROJECT_NAME}_use_feature 42 CACHE STRING "description") option(${PROJECT_NAME}_use_feature "on or off" OFF) macro(${PROJECT_NAME}_xyz) ... This will avoid conflicts between packages and errors due to copy&paste. **find_package(... REQUIRED)** Use ``REQUIRED`` on all calls to ``find_package`` if they aren't actually optional (i.e. you're not going to check ``thing_FOUND`` and enable/disable features). **Avoid Pkg_config** CMake allows to use pkg-config:: find_package(PkgConfig) pkg_check_modules(XXX xxx) However you should (in CMakeLists.txt files) if possible always prefer the method:: find_package(xxx) This is usually the most flexible and portable way. **Keep lists sorted** Whenever using a list of items (i.e. in find_package(COMPONENTS ...) or files which should be build or installed) keep them alphabetically sorted. This improves readability when looking for specific items. (There are exceptions which require a specific custom order like the list of projects inside a stack). **Lowercase keywords** Keywords like ``if``, ``for`` etc. are all lowercase. **Upper arguments** Use UPPER_CASE for arguments to CMake functions and macros (e.g. REQUIRED, NO_MODULE, QUIET) **Closing keyword should have empty parenthesis** The closing keywords like ``endif()`` and ``endforeach()`` should not repeat the condition of the opening keyword. **Indentation** Indent all code correctly, i.e. the body of * if/else/endif * foreach/endforeach * while/endwhile * macro/endmacro * function/endfunction Use spaces for indenting, 2 spaces preferably **Variable names** For custom variables, avoid the following list of suffixes in any CMakeLists.txt, those should only be set by FindXXX.cmake or XXXConfig.cmake files: * XXX_DEFINITIONS * XXX_EXECUTABLE * XXX_INCLUDE_DIRS * XXX_INCLUDE_DIR * XXX_YY_INCLUDE_DIR * XXX_LIBRARIES * XXX_LIBRARY_DIRS * XXX_LIBRARY * XXX_YY_LIBRARY * XXX_ROOT_DIR * XXX_FOUND * XXX_YY_FOUND * XXX_RUNTIME_LIBRARY_DIRS * XXX_VERSION_STRING * XXX_VERSION_MAJOR * XXX_VERSION_MINOR * XXX_VERSION_PATCH * XXX_VERSION_YY * XXX_WRAP_YY You may use such variables of course by reading their value after calling find_package(), but do not manually change them. **Forbidden variables** Do not set * CMAKE_CXX_FLAGS * CMAKE_FIND_ROOT_PATH **Conditions and Variables** Always quote variable that represent a string:: set(myvar "foo") if("${myvar}" STREQUAL "bar") # ... endif() Do not quote variable that are booleans:: set(mybvar ON) set(mybvar OFF) if(${myvar}) # ... endif() When storing paths in variables, do NOT have the cmake variables end up with a slash:: # YES: set(_my_path "path/to/foo") set(_my_other_path "${_my_path}/${_my_var}") # NO: set(my_path "path/to/foo/") set(_my_other_path "${_my_path}${_my_var}") # wrong: this is ugly Use if(DEFINED varname) to check if a variable is set:: if(DEFINED myvar) # ... endif() Use if(varname) to check it a variable has a non-empty value:: if(myvar) # ... endif()