Previously we modified the example application published on Qt Company’s blog to build on Ubuntu 16.04 LTS. We then migrated the examples from RTI’s Traditional C++ API to the newer Modern C++ API. I’ll scratch one more itch, making use of Modern CMake to build the DDS blog examples.
CMake is an open-source, cross-platform family of tools designed to build, test and package software (https://cmake.org). Qt has excellent support for CMake, although a few holes in the documentation can make adoption a bit challenging (Presenting an opportunity for future blog posts on the topic!). Modern CMake is set of best practices that as best I can tell are documented everywhere and nowhere online. There is no single authoritative source on the subject at this time, but it’s easy to find talks and blogs that seem to come to a general consensus on the do’s and don’ts.
RTI provides official CMake modules, which are made available to our project via the following lines in the top-level CMakeLists.txt. This offers an immediate improvement over our hardcoded support, for a single platform, in the prior qmake project file. Complete documentation on the capabilities of these CMake modules can be found within the headers of the two cmake files at $NDDSHOME/resource/cmake.
find_package(RTIConnextDDS)
find_package(RTICodeGenerator)
The headers, libraries, and compile defitions are then explicitly added as dependencies to the “common” project. I’ve created this project to encompass the responsibilities of the old dds.pri
, the generation of sources from our sensor.idl file. “common” is both project name and the name of the CMake target, a static library which will be used by all three example applications.
target_link_libraries(common
PUBLIC ${CONNEXTDDS_CPP2_API_LIBRARIES_RELEASE_SHARED}
)
target_include_directories(common
PUBLIC ${PROJECT_BINARY_DIR}
PUBLIC ${PROJECT_SOURCE_DIR}
PUBLIC ${CONNEXTDDS_INCLUDE_DIRS}
)
target_compile_definitions(common
PUBLIC RTI_UNIX
PUBLIC RTI_LINUX
PUBLIC RTI_64BIT
)
All of this work gets brought into the individual examples via the following. Let’s say that “example” is the project name of a Qt application. Using target_link_libraries
we can instruct CMake to figure out what dependencies to bring along for the ride. In this case, anything marked PUBLIC
from “common”.
target_link_libraries(example
common
)
The greatest advantage to our use of CMake is the reduction from the whole of dds.pri to the following 15 lines. This can be leveraged to great effect when generating sources from multiple IDL files. Simply loop over the custom command in a macro with a list of IDL to generate the needed source files.
set(GENERATED_SOURCES
${PROJECT_BINARY_DIR}/sensor.cxx
${PROJECT_BINARY_DIR}/sensorImpl.cxx
${PROJECT_BINARY_DIR}/sensorImplPlugin.cxx
)
add_custom_command(OUTPUT ${GENERATED_SOURCES}
COMMAND ${RTICODEGEN} -language C++11 -d ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/sensor.idl
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${PROJECT_SOURCE_DIR}/sensor.idl
)
set_source_files_properties(${GENERATED_SOURCES} PROPERTIES GENERATED TRUE)
add_library(common
${GENERATED_SOURCES}
)
To build the project, we’ll assume that Qt is in your PATH. The follwing commands effectively replace qmake
and make
.
cmake -DCONNEXTDDS_ARCH=x64Linux3gcc5.4.0 -DCMAKE_MODULE_PATH=$NDDSHOME/resource/cmake -DRTICODEGEN_DIR=$NDDSHOME/bin ..
cmake --build .
Qt Creator is capable of opening CMake projects as well. In my opinion, this results in a cleaner project view and better management of shared dependencies. Some additional work is needed in the Projects configuration to set the CMake variables as in the command above.
There is an even cleaner, and more “Modern CMake”-ish way to integrate RTI Connext DDS into this project, but I leave that as an exercise to the reader.