#*+-------------------------------------------------------------------------+
# | MultiVehicle simulator (libmvsim) |
# | |
# | https://github.com/ual-arm-ros-pkg/multivehicle-simulator |
# | |
# | Copyright (C) 2014-2025 Jose Luis Blanco Claraco |
# | Distributed under 3-clause BSD License |
# | See COPYING |
# +-------------------------------------------------------------------------+
cmake_minimum_required(VERSION 3.9) # for CMAKE_MATCH_1
if("$ENV{ROS_VERSION}" STREQUAL "2")
set(DETECTED_ROS2 TRUE)
set(PACKAGE_ROS_VERSION 2)
elseif("$ENV{ROS_VERSION}" STREQUAL "1")
set(DETECTED_ROS1 TRUE)
set(PACKAGE_ROS_VERSION 1)
set(CMAKE_CXX_STANDARD 14)
endif()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(default_build_type "Release")
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE)
endif()
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo" "SanitizeAddress" "SanitizeThread")
# CMake file for the library target
project(mvsim)
#----
# Extract version from package.xml
# Example line:" 0.3.2"
file(READ package.xml contentPackageXML)
string(REGEX MATCH "([0-9\.]*)" _ ${contentPackageXML})
set(MVSIM_VERSION ${CMAKE_MATCH_1})
message(STATUS "MVSIM version: ${MVSIM_VERSION} (detected in package.xml)")
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ ${MVSIM_VERSION})
set(MVSIM_MAJOR_VERSION ${CMAKE_MATCH_1})
set(MVSIM_MINOR_VERSION ${CMAKE_MATCH_2})
set(MVSIM_PATCH_VERSION ${CMAKE_MATCH_3})
#TODO: Avoid overwriting source dir.
configure_file(
cmake/mvsim_version.h.in
${CMAKE_CURRENT_SOURCE_DIR}/modules/simulator/include/mvsim/mvsim_version.h
@ONLY
)
#----
# This is required for pybind11 to generate C++17 code in all OS versions:
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ version selection")
set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported
set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensions off
find_package(Threads)
include(GNUInstallDirs)
include(cmake/mvsim_cmake_functions.cmake REQUIRED)
if (POLICY CMP0057)
cmake_policy(SET CMP0057 NEW) # IN_LIST (needed by pybind11 cmake scripts)
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
# BUILD_TYPE: SanitizeAddress
set(CMAKE_CXX_FLAGS_SANITIZEADDRESS "-fsanitize=address -fsanitize=leak -g")
set(CMAKE_EXE_LINKER_FLAGS_SANITIZEADDRESS "-fsanitize=address -fsanitize=leak")
set(CMAKE_SHARED_LINKER_FLAGS_SANITIZEADDRESS "-fsanitize=address -fsanitize=leak")
# BUILD_TYPE: SanitizeThread
set(CMAKE_CXX_FLAGS_SANITIZETHREAD "-fsanitize=thread -g")
set(CMAKE_EXE_LINKER_FLAGS_SANITIZETHREAD "-fsanitize=thread")
set(CMAKE_SHARED_LINKER_FLAGS_SANITIZETHREAD "-fsanitize=thread")
endif()
# ------------------------------------------------------------------
# IMPORTANT NOTE
#
# This package can be built as:
# 1) Standalone lib+app
# 2) ROS1 or ROS2 node(s)
# Depending on what packages are found, this script will instruct
# CMake to configure the project accordingly.
# ------------------------------------------------------------------
# ROS1:
set(BUILD_FOR_ROS FALSE)
if (DETECTED_ROS1)
set(BUILD_FOR_ROS TRUE)
endif()
# ROS2:
if (DETECTED_ROS2)
set(BUILD_FOR_ROS TRUE)
find_package(rclcpp REQUIRED)
endif()
if (BUILD_FOR_ROS)
message(STATUS " ==== multivehicle-simulator: ROS$ENV{ROS_VERSION} detected. ROS nodes will be built. ===== ")
add_definitions(-DMVSIM_HAS_ROS)
endif()
# -----------------------------------------------------------------------------
# ROS1
# -----------------------------------------------------------------------------
if (BUILD_FOR_ROS AND DETECTED_ROS1)
# Find catkin macros and libraries
# if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
# is used, also find other catkin packages
find_package(catkin
REQUIRED
COMPONENTS
roscpp tf2 dynamic_reconfigure std_msgs nav_msgs sensor_msgs visualization_msgs
tf2_geometry_msgs
)
find_package(mrpt-ros1bridge 2.5.6 REQUIRED)
#add dynamic reconfigure api
if (dynamic_reconfigure_FOUND)
generate_dynamic_reconfigure_options(
cfg/mvsimNode.cfg
)
endif()
###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if you package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
CATKIN_DEPENDS
dynamic_reconfigure nav_msgs roscpp sensor_msgs visualization_msgs
tf2 tf2_geometry_msgs
# INCLUDE_DIRS include
# LIBRARIES mrpt_localization
# DEPENDS mrpt
)
endif()
# -----------------------------------------------------------------------------
# ROS2
# -----------------------------------------------------------------------------
if (BUILD_FOR_ROS AND DETECTED_ROS2)
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(tf2 REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(visualization_msgs REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(mrpt-ros2bridge 2.5.6 REQUIRED)
endif()
# --------------------------
# Build options
# --------------------------
if (UNIX)
set(DEFAULT_SHARED_LIBS ON)
else()
set(DEFAULT_SHARED_LIBS OFF)
endif()
set(BUILD_SHARED_LIBS ${DEFAULT_SHARED_LIBS} CACHE BOOL "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)")
# Save all libs and executables in the same place
if (NOT BUILD_FOR_ROS)
set( LIBRARY_OUTPUT_PATH ${${PROJECT_NAME}_BINARY_DIR}/lib CACHE PATH "Output directory for libraries" )
set( EXECUTABLE_OUTPUT_PATH ${${PROJECT_NAME}_BINARY_DIR}/bin CACHE PATH "Output directory for applications" )
endif()
set(CMAKE_DEBUG_POSTFIX "-dbg")
# --------------------------
# Global compiler flags
# --------------------------
if(MSVC)
# Force usage of UNICODE projects, which is not the default in MSVC:
add_definitions(-DUNICODE -D_UNICODE)
endif()
# ----------------------------------------------------------
# Dependency: MRPT
# Use the first cmd to set the minimum required version
# ----------------------------------------------------------
find_package(mrpt-maps 2.5.6 REQUIRED)
find_package(mrpt-tclap 2.5.6 REQUIRED)
find_package(mrpt-gui 2.5.6 REQUIRED)
find_package(mrpt-tfest 2.5.6 REQUIRED)
find_package(mrpt-topography REQUIRED)
# --------------------------
# Dependency: Box2D
# --------------------------
set(EMBEDDED_box2d_BUILD_DIR "${${PROJECT_NAME}_BINARY_DIR}/externals/box2d/")
set(EMBEDDED_box2d_INSTALL_DIR "${${PROJECT_NAME}_BINARY_DIR}/externals/box2d/install/")
set(EMBEDDED_box2d_DIR "${EMBEDDED_box2d_INSTALL_DIR}/lib/cmake/box2d/")
# 1st) Try to locate it via CMake (installed in the system or precompiled somewhere)
# Since box2d 2.4.1, the name changed "Box2D" -> "box2d". We now only support the version >=2.4.x
# for compatibility with modern Ubuntu distros:
#
# Update: Since MVSIM 0.7.0, we now always ship a custom build of box2d to
# increase its default maximum number of polygon vertices:
##find_package(box2d QUIET) # Defines: box2d::box2d
if (NOT box2d_FOUND)
message(STATUS "--- Running CMake on external submodule 'box2d'...")
file(MAKE_DIRECTORY "${EMBEDDED_box2d_BUILD_DIR}")
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.15")
set(echo_flag COMMAND_ECHO STDOUT)
endif()
execute_process(COMMAND
${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "${${PROJECT_NAME}_SOURCE_DIR}/externals/box2d"
-DBOX2D_BUILD_DOCS=OFF
-DBOX2D_BUILD_TESTBED=OFF
-DBOX2D_BUILD_UNIT_TESTS=OFF
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DCMAKE_INSTALL_PREFIX=${EMBEDDED_box2d_INSTALL_DIR}
RESULT_VARIABLE result
WORKING_DIRECTORY "${EMBEDDED_box2d_BUILD_DIR}"
${echo_flag}
)
if(result)
message(FATAL_ERROR "CMake step for box2d failed: ${result}")
endif()
execute_process(COMMAND
${CMAKE_COMMAND} --build ${EMBEDDED_box2d_BUILD_DIR} --target install
RESULT_VARIABLE result
${echo_flag}
)
if(result)
message(FATAL_ERROR "CMake make install step for box2d failed: ${result}")
endif()
message(STATUS "--- End running CMake")
# Search again:
set(box2d_DIR "${EMBEDDED_box2d_DIR}" CACHE PATH "Path to box2d CMake config file" FORCE)
mark_as_advanced(box2d_DIR)
find_package(box2d CONFIG QUIET)
# install the embedded copy too (we need box2d-config.cmake, etc.)
execute_process(COMMAND
${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" "${${PROJECT_NAME}_SOURCE_DIR}/externals/box2d"
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
RESULT_VARIABLE result
WORKING_DIRECTORY "${EMBEDDED_box2d_BUILD_DIR}"
${echo_flag}
)
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} --build \"${EMBEDDED_box2d_BUILD_DIR}\" --target install)")
endif()
if (NOT box2d_FOUND)
message(FATAL_ERROR "box2d not found, neither as system library nor git submodule. Check error messages above for possible reasons.")
endif()
if (box2d_FOUND)
set(BOX2D_LIBRARIES box2d::box2d)
endif()
# --------------------------
# Main library modules
# --------------------------
add_subdirectory(modules)
# --------------------------
# Apps
# --------------------------
add_subdirectory(mvsim-cli)
if (BUILD_FOR_ROS)
###########
## Build ##
###########
## Declare a cpp executable
add_executable(mvsim_node
mvsim_node_src/mvsim_node.cpp
mvsim_node_src/mvsim_node_main.cpp
mvsim_node_src/include/mvsim/mvsim_node_core.h
mvsim_node_src/include/wrapper/publisher_wrapper.h
)
mvsim_set_target_build_options(mvsim_node)
target_include_directories(mvsim_node PRIVATE "mvsim_node_src/include")
if (catkin_INCLUDE_DIRS)
# Specify additional locations of header files
# Your package locations should be listed before other locations
target_include_directories(mvsim_node PUBLIC ${catkin_INCLUDE_DIRS})
endif()
if (DETECTED_ROS1)
add_dependencies(mvsim_node
mvsim_gencfg
)
target_link_libraries(mvsim_node
#PRIVATE # ros catkin already used the plain signature, we cannot use this here...
${catkin_LIBRARIES}
mrpt::ros1bridge
)
# make sure configure headers are built before any node using them
if (${PROJECT_NAME}_EXPORTED_TARGETS})
add_dependencies(mvsim_node ${${PROJECT_NAME}_EXPORTED_TARGETS})
endif()
elseif(DETECTED_ROS2)
target_link_libraries(mvsim_node #PRIVATE
rclcpp::rclcpp
tf2::tf2
mrpt::ros2bridge
${geometry_msgs_TARGETS}
${nav_msgs_TARGETS}
${sensor_msgs_TARGETS}
${visualization_msgs_TARGETS}
${tf2_geometry_msgs_TARGETS}
)
# We need this to handle this backwards-incompatible change: https://github.com/ros2/geometry2/pull/416
if("${tf2_geometry_msgs_VERSION}" VERSION_GREATER_EQUAL "0.18.0")
target_compile_definitions(mvsim_node PRIVATE MVSIM_HAS_TF2_GEOMETRY_MSGS_HPP)
endif()
endif()
target_compile_definitions(mvsim_node PRIVATE
PACKAGE_ROS_VERSION=${PACKAGE_ROS_VERSION}
)
if (TARGET mvsim::comms)
set(DEP_MVSIM_COMMS_TRG mvsim::comms)
endif()
if (TARGET mvsim::msgs)
set(DEP_MVSIM_MSGS_TRG mvsim::msgs)
endif()
# Specify libraries to link a library or executable target against
target_link_libraries(
mvsim_node
#PRIVATE # ros ament already used the plain signature, we cannot use this here...
mvsim::simulator
${DEP_MVSIM_MSGS_TRG}
${DEP_MVSIM_COMMS_TRG}
${BOX2D_LIBRARIES} # Box2D libs
${catkin_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
if (DETECTED_ROS1)
target_link_libraries(mvsim_node mrpt::ros1bridge)
else()
target_link_libraries(mvsim_node mrpt::ros2bridge)
endif()
#############
## Install ##
#############
# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
# Mark executables and/or libraries for installation
if (DETECTED_ROS2)
set(CATKIN_PACKAGE_LIB_DESTINATION lib)
set(CATKIN_PACKAGE_BIN_DESTINATION lib/${PROJECT_NAME})
set(CATKIN_PACKAGE_INCLUDE_DESTINATION include)
set(CATKIN_PACKAGE_SHARE_DESTINATION share/${PROJECT_NAME})
endif()
# Mark executables and/or libraries for installation
install(TARGETS mvsim_node
ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
# Mark other files for installation (e.g. launch and bag files, etc.)
install(DIRECTORY
definitions
mvsim_tutorial
models
launch
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
)
# Mark executables and/or libraries for installation
if (DETECTED_ROS2)
#ament_export_dependencies(mrpt-gui)
# Export old-style CMake variables
#ament_export_include_directories("include/${PROJECT_NAME}")
#ament_export_libraries(${PROJECT_NAME})
# Export modern CMake targets
#ament_export_targets(export_${PROJECT_NAME})
ament_package()
endif()
endif() # BUILD_FOR_ROS
option(MVSIM_UNIT_TESTS "Build and run MVSIM unit tests (requires Python and ZMQ)" ON)
# Disable tests in armhf, since they fail due to (probably?) very slow rendering capabilities.
# See: https://github.com/ros-infrastructure/ros_buildfarm_config/pull/220
if (MVSIM_UNIT_TESTS AND
(
("armhf" STREQUAL "${CMAKE_SYSTEM_PROCESSOR}") OR
("arm64" STREQUAL "${CMAKE_SYSTEM_PROCESSOR}") OR
("aarch64" STREQUAL "${CMAKE_SYSTEM_PROCESSOR}")
)
)
set(MVSIM_UNIT_TESTS OFF CACHE BOOL "" FORCE)
message(STATUS "*Warning*: Disabling unit tests in this architecture (${CMAKE_SYSTEM_PROCESSOR})")
endif()
if (MVSIM_UNIT_TESTS AND MVSIM_WITH_PYTHON AND MVSIM_WITH_ZMQ)
enable_testing() # This must be in the root cmake file
add_subdirectory(tests)
endif()