OpenDDS can be used with CMake-Based projects by using the OpenDDS CMake Config
package (located here). This package bridges the gap between the
MPC build system used by OpenDDS and CMake-Based projects by exposing OpenDDS CMake
targets (see comments here), and by providing the
OPENDDS_TARGET_SOURCES
macro
which simplifies IDL compilation.
Table of Contents:
- Requirements
- CMake Messenger Examples
- Configure-Generated Variables
- Using the OpenDDS CMake Package
- Adding IDL Sources with
OPENDDS_TARGET_SOURCES
- Advanced Usage
In order to use the Config module, CMake version 3.3.2 or greater is required. However, to run the Messenger examples, version 3.8.2 or greater is required (due to some dependencies which simplify the custom file-copy targets).
For a simple quick-start example of an CMakeLists.txt
using OpenDDS see the
Developer's Guide Messenger example.
Included with OpenDDS are two example CMake-Based Messenger applications which exist to showcase two different strategies for adding IDL files:
The first scenario adds the IDL file directly into each executable so the generated headers/sources are added to the publisher/subscriber targets independently.
The second scenario creates a library which has the IDL-generated headers/sources) added to it. The publisher and subscriber executables then link against the library to use the IDL-generated code.
The configure script ($DDS_ROOT/configure
) is responsible for generating the
config.cmake
file (placed in $DDS_ROOT/cmake
),
which has various configuration options. These options provide the OpenDDS
CMake package with the required context it needs to integrate with the
OpenDDS code generators and libraries.
Note: With an installed version of OpenDDS (generated using make install
),
config.cmake
will be placed in $INSTALL_PREFIX/lib/cmake/OpenDDS
.
Not all of the variables in this file are used. They are procedurally generated from variables in the configure script. However, to get a better sense of how they are being used take a look at the options.cmake file. This is where various CMake C/C++ compile-time defines and IDL-Compiler flags are set/unset depending upon which variables are enabled/disabled.
If you are using OpenDDS libraries that were built without the help of the
configure
script, the config.cmake
file needs to be created manually.
See Manually Creating config.cmake
in the advanced section below.
To use the OpenDDS CMake Package, first it has to be loaded using
find_package
.
For example to mandatorily load OpenDDS:
find_package(OpenDDS REQUIRED)
For this to work, CMake has to be able to find the package. If the OpenDDS
build environment variables (from source setenv.sh
or call setenv.cmd
) are
set then CMake should be able to find it using the PATH
environment variable.
If those environment variables aren't set, OpenDDS was installed to a path
CMake doesn't search automatically, or CMake can't otherwise find the OpenDDS
package, then CMake has to be told about it explicitly somehow. This can be
done a number of ways, which includes adding the OpenDDS source tree or install
prefix path to CMAKE_PREFIX_PATH
or setting OPENDDS_ROOT
to that path (if
using CMake 3.12 or later).
Consult the find_package
documentation for your CMake version for all the
details on how CMake could find OpenDDS.
Here
is the documentation for the latest version of CMake
The following cache variables/options can be used to control the behavior of the OpenDDS CMake package.
Cache Variable | Description | Default |
---|---|---|
OPENDDS_CMAKE_VERBOSE |
Print detailed status information at CMake-Generation time | OFF |
OPENDDS_DEFAULT_NESTED |
Topic types must be declared explicitly. | ON |
OPENDDS_FILENAME_ONLY_INCLUDES |
No directory info in generated #includes. | OFF |
OPENDDS_DEFAULT_SCOPE |
Default scope of unscoped files in OPENDDS_TARGET_SOURCES |
PRIVATE |
OPENDDS_ALWAYS_GENERATE_LIB_EXPORT_HEADER |
Default for ALWAYS_GENERATE_LIB_EXPORT_HEADER in OPENDDS_TARGET_SOURCES |
OFF |
The following libraries are some of what can be defined by the package:
OpenDDS::Dcps
- Core OpenDDS Library
OpenDDS::Rtps
- RTPS Discovery
OpenDDS::InfoRepoDiscovery
- InfoRepo Discovery
OpenDDS::Rtps_Udp
- RTPS Transport
OpenDDS::Multicast
- Multicast Transport
OpenDDS::Shmem
- Shared Memory Transport
OpenDDS::Tcp
- TCP Transport
OpenDDS::Udp
- UDP Transport
OpenDDS::Security
- DDS Security Implementation, if Available
OpenDDS::OpenDDS
- A Combination of all the Previous Libraries
See OpenDDSConfig.cmake for all the libraries and more details on them.
The following instructions show how to configure and build the Developer's
Guide Messenger example. Make sure the environment is setup by using call setenv.cmd
on Windows or source setenv.sh
elsewhere.
cd DevGuideExamples/DCPS/Messenger
mkdir build
cd build
cmake ..
cmake --build .
perl run_test.pl
The following assumes Visual Studio 2017 using 64-bit architecture (adjust the
CMake -G
parameter if using something different).
cd DevGuideExamples\DCPS\Messenger
mkdir build
cd build
cmake -G "Visual Studio 15 2017 Win64" ..
cmake --build .
perl run_test.pl
The --prefix
switch can be passed to configure to enable the install
target,
which will install OpenDDS (including the OpenDDS CMake Config module) into the
specified directory. See the INSTALL.md document for details.
Note: Be sure to pass an absolute path to --prefix
.
OPENDDS_PREFIX="$PWD/opendds-install"
DDS_ROOT="$PWD/OpenDDS"
ACE_ROOT="$DDS_ROOT/ACE_wrappers"
cd OpenDDS
./configure --prefix="$OPENDDS_PREFIX"
make -j $(getconf _NPROCESSORS_ONLN)
make install
cd DevGuideExamples/DCPS/Messenger
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH="$OPENDDS_PREFIX" ..
cmake --build .
PERL5LIB="$DDS_ROOT/bin:$ACE_ROOT/bin" LD_LIBRARY_PATH="$OPENDDS_PREFIX/lib:$LD_LIBRARY_PATH" perl run_test.pl
Aside from importing the various OpenDDS targets, the OpenDDS Config Package
provides an easy way to add IDL sources to CMake targets. This is achieved by
the OPENDDS_TARGET_SOURCES
macro, which was inspired by the built-in
target_sources
:
OPENDDS_TARGET_SOURCES(<target>
[<idl file>...]
[INTERFACE|PUBLIC|PRIVATE <idl file>...]...
[TAO_IDL_OPTIONS <option>...]
[OPENDDS_IDL_OPTIONS <option>...]
[SUPPRESS_ANYS ON|OFF]
[ALWAYS_GENERATE_LIB_EXPORT_HEADER ON|OFF]
)
<idl file>...
are IDL files that can be absolute or relative to
CMAKE_CURRENT_SOURCE_DIR
. Each one will generate code using tao_idl
and
opendds_idl
that is added to the target. C/C++ items can also be accepted and
added to the target, but passing non-IDL sources was deprecated in OpenDDS
3.15. The scope-qualifier (PUBLIC
, PRIVATE
, INTERFACE
) sets the scope of
the generated files. When it is omitted, OPENDDS_DEFAULT_SCOPE
is used, which
is PRIVATE
by default.
Command-line options can be supplied to the TAO/OpenDDS IDL compilers using
TAO_IDL_OPTIONS
and/or OPENDDS_IDL_OPTIONS
(if the default behavior is not
suitable). Add OPENDDS_IDL_OPTIONS -Lc++11
to use the C++11 IDL Mapping.
An option is available to force creation of typecodes by using SUPPRESS_ANYS OFF
. This value will overrule the one received from config.cmake
OPENDDS_SUPPRESS_ANYS
.
If the passed-in target is a shared library, a custom command will also be
added to generate the required IDL export header file (TARGET_export.h
),
which is necessary to properly export symbols for the IDL-Generated sources.
The ALWAYS_GENERATE_LIB_EXPORT_HEADER
argument forces this to be true for all
libraries types. This is only really useful if the target is a library that
uses the export header itself and also needs to be built as a static library as
well. This way the export library can be included unconditionally. The default
for this is set by the OPENDDS_ALWAYS_GENERATE_LIB_EXPORT_HEADER
, which
defaults to OFF
.
See "OPENDDS_TARGET_SOURCES
Target Properties"
for the target properties that are set on the target.
Based on the Messenger with direct IDL inclusion, here is a snippet showing
how IDL files can be added directly to executable targets using the OPENDDS_TARGET_SOURCES
macro:
add_executable(publisher
"publisher.cpp"
)
OPENDDS_TARGET_SOURCES(publisher "Messenger.idl")
target_link_libraries(publisher OpenDDS::OpenDDS)
This is fine for small IDL files and builds with a few targets that need the code generated from IDL, but it can be improved upon.
Another snippet, based upon Messenger with auxiliary IDL library, showcases how an auxiliary IDL library can be created for inclusion by other executables:
add_library(messenger)
OPENDDS_TARGET_SOURCES(messenger PUBLIC "Messenger.idl")
target_link_libraries(messenger OpenDDS::Dcps)
add_executable(publisher
"publisher.cpp"
)
target_link_libraries(publisher OpenDDS::OpenDDS)
Here the generated code from the IDL is only compiled once. This is especially advantageous if you have a significant amount of IDL and/or have many targets that require the IDL. Additionally if the library is a shared library, executables can share that file and this reduces the size of the executable files.
Note: CMake version 3.10 and below will issue a harmless warning if
add_library
is called without any sources.
In order to work with config.cmake it is important to understand the purpose of its contained variables. Below is a list of the important variables with some information on their purpose.
When OpenDDS is built using the MPC build-system
and optional features are specified, various C/C++; tao_idl
; and opendds_idl
compiler macros are set on dependent libraries or executables. These defines
are set by MPC using the dcps_optional_features.mpb
file.
CMake-Based targets must also inherit the defined macros, so the functionality
in dcps_optional_features.mpb
is simulated using options.cmake
and its associated config.cmake
. As such, if features are enabled/disabled when
compiling OpenDDS then it is paramount that the MPC features are also matched
within config.cmake
prior to compilation.
This table outlines the feature-related variables and their expected default
values (generated when the configure script is run without arguments).
If any feature is explicitly enabled/disabled in the OpenDDS build then the corresponding
CMake variable below should be set to either ON
or OFF
depending on the desired state.
CMake Variable | Notes (from configure script output) | Default |
---|---|---|
OPENDDS_BUILT_IN_TOPICS |
Built-in Topics | ON |
OPENDDS_CONTENT_FILTERED_TOPIC |
ContentFilteredTopic (CS Profile) | ON |
OPENDDS_CONTENT_SUBSCRIPTION |
Content-Subscription Profile. When this is set to OFF then CONTENT_FILTERED_TOPIC , MULTI_TOPIC , and QUERY_CONDITION will also be set to OFF . See the OpenDDS Dev Guide ch. 5 for info. |
ON |
OPENDDS_MULTI_TOPIC |
MultiTopic (CS Profile) | ON |
OPENDDS_OBJECT_MODEL_PROFILE |
Object Model Profile | ON |
OPENDDS_OWNERSHIP_KIND_EXCLUSIVE |
Exclusive Ownership (Ownership Profile) | ON |
OPENDDS_OWNERSHIP_PROFILE |
Ownership Profile. When this is set to OFF then OWNERSHIP_KIND_EXCLUSIVE will also be set to OFF . |
ON |
OPENDDS_PERSISTENCE_PROFILE |
Persistence Profile | ON |
OPENDDS_QUERY_CONDITION |
QueryCondition (CS Profile) | ON |
OPENDDS_SECURITY |
DDS Security plugin | OFF |
OPENDDS_SAFETY_PROFILE |
Safety Profile | OFF |
The following values impact the build in one way or another.
CMake Variable | Notes | Default |
---|---|---|
OPENDDS_ACE |
Location of ACE root dir. | N/A |
OPENDDS_TAO |
Location of TAO root dir. | N/A |
OPENDDS_INLINE |
ACE's inline build flag | See below |
OPENDDS_STATIC |
Use static libraries | OFF |
OPENDDS_XERCES3 |
Adds dependencies to targets; required when OPENDDS_SECURITY is ON |
OFF |
OPENDDS_CXX11 |
ACE/TAO and OpenDDS were built with C++11 or later. | N/A |
OPENDDS_WCHAR |
ACE/TAO and OpenDDS were built to prefer wide characters. | OFF |
OPENDDS_VERSIONED_NAMEPSACE |
ACE/TAO and OpenDDS have versioned namespaces. | OFF |
OPENDDS_FEATURES |
List of additional features which impact the build. | N/A |
OPENDDS_INLINE
should be explicitly set to ON
or OFF
(based on the ACE platform_macros.GNU
variable inline
) in config.cmake
unless you will only be using a CMake Microsoft Visual Studio Generator.
When parsing IDL, OpenDDS will determine if it should be prepared for a
struct
or union
to be used in a topic if its "nested" value is false. See
the OpenDDS Developer's Guide for more information.
OPENDDS_DEFAULT_NESTED
sets the global default for what the "nested" property
is. If this value is ON
, then topic types for a given project must be
declared using annotations. If it's OFF
, then every valid type is assumed to
be needed for use as a topic type by default, which might add code to the IDL
type support libraries that will never be used.
This default can also be controlled on a finer level when calling
OPENDDS_TARGET_SOURCES
by passing --default-nested
or --no-default-nested
to OPENDDS_IDL_OPTIONS
. For example:
add_library(messenger)
OPENDDS_TARGET_SOURCES(messenger
PUBLIC
Messenger.idl
OPENDDS_IDL_OPTIONS --no-default-nested
)
This setting tells OpenDDS's IDL compiler to strip path information from #include
lines in generated files. Turning the option on can make it easier to specify build
rules for IDL files that include other IDL files.
After OPENDDS_TARGET_SOURCES
is run on a target, it will have these
properties set.
This signifies the IDL language mappings available in the generated IDL
bindings built into the target based on what is passed to
OPENDDS_IDL_OPTIONS
. It will be a list that can contain one or more of the
following:
"C++03"
- Default IDL-to-C++ mapping generated by default.
"C++11"
- IDL-to-C++11 mapping available when passing
-Lc++11
.
- IDL-to-C++11 mapping available when passing
"FACE"
- Will appear if
-Lface
is passed.
- Will appear if
"Java"
- Currently unsupported in the CMake module, but this will appear in the
list if
-Wb,java
is passed.
- Currently unsupported in the CMake module, but this will appear in the
list if
If the CMake version is at least 3.12, then this property will exported with the target.
The is the directory where generated files have been placed. This is an
absolute path and is not exported with the target. This can be used along with
the OPENDDS_*_INTERFACE_FILES
properties to help
install the generated interface files.
These properties are lists of the PUBLIC
and INTERFACE
scoped IDL files
that were passed in the files generated based on those.
Some things to note about these:
- All paths are absolute.
- All the generated files will be somewhere within the path from the
OPENDDS_GENERATED_DIRECTORY
target property of the target. - All the properties have the
INTERFACE
in their name, but this includesPUBLIC
scoped files asPUBLIC
impliesINTERFACE
in CMake.PRIVATE
scoped files are excluded from these lists as they shouldn't have a use outside the target. - These properties are not exported with the target because those paths may not be valid any more if the build directory has been removed or the export is being used on another machine.
- As passing non-IDL files to
OPENDDS_TARGET_SOURCES
is deprecated, those files don't show up in these lists even if they are givenPUBLIC
orINTERFACE
scope.
The properties are:
OPENDDS_PASSED_IDL_INTERFACE_FILES
- The
PUBLIC
andINTERFACE
scoped IDL files passed toOPENDDS_TARGET_SOURCES
.
- The
OPENDDS_GENERATED_IDL_INTERFACE_FILES
- The IDL generated from the IDL files in
OPENDDS_PASSED_IDL_INTERFACE_FILES
.
- The IDL generated from the IDL files in
OPENDDS_ALL_IDL_INTERFACE_FILES
- Combination of
OPENDDS_PASSED_IDL_INTERFACE_FILES
andOPENDDS_GENERATED_IDL_INTERFACE_FILES
.
- Combination of
OPENDDS_GENERATED_HEADER_FILES
- The
.h
and.inl
files generated byOPENDDS_ALL_IDL_INTERFACE_FILES
.
- The
OPENDDS_ALL_GENERATED_INTERFACE_FILES
- Combination of
OPENDDS_GENERATED_IDL_INTERFACE_FILES
andOPENDDS_GENERATED_HEADER_FILES
.
- Combination of
OPENDDS_ALL_INTERFACE_FILES
- All the
INTERFACE
andPUBLIC
scoped files that were passed in or generated.
- All the
The main indented purpose of these properties is to help install the generated interface files.
If using CMake 3.21 or later, it's possible to install shared libraries from
OpenDDS, ACE, and TAO in CMake along side the application using
install(IMPORTED_RUNTIME_ARTIFACTS)
.
This will just install shared libraries, not any development dependencies like
opendds_idl
or static libraries.
If OpenDDS and ACE/TAO is built with clang
, the shared libraries might be
missing an SONAME
entry. Is is
an issue with ACE/TAO.
If trying to use install(IMPORTED_RUNTIME_ARTIFACTS)
in this case, it causes
the dynamic linker to ignore the libraries and report that they could not be
found. One workaround is to add SOFLAGS+=-Wl,-h,$(SONAME)
to
$ACE_ROOT/include/makeinclude/platform_macros.GNU
before building. This can
be done manually after running the configure script or by passing
--macros=SOFLAGS+=-Wl,-h,\$\(SONAME\)
to the configure script.
A function called opendds_get_library_dependencies
is provided to help find
out what libraries need to be installed:
opendds_get_library_dependencies(<output_list_var_name> <lib>...)
<lib>...
are the libraries as individual arguments or lists.
The result will contain all targets passed and their OpenDDS, ACE, and TAO
dependencies. The result is put in a list named by output_list_var_name
.
See the install Test for an example of using this.
It is possible to install files from
OPENDDS_*_INTERFACE_FILES
target properties
for downstream projects to use. See the install Test for an example of
installing these files. It uses
install(FILES)
,
but there isn't any restriction on what installation method can be used. For
example, the
PUBLIC_HEADER
target property could be set on target to the desired files from the interface
lists. Then they installed using
install(TARGETS ... PUBLIC_HEADER ...)
.