How Do I Use dds
in a CMake Project?¶
If you have a CMake project and you wish to pull your dependencies via dds
,
you’re in luck: Such a process is explicitly supported. Here’s the recommended
approach:
Download PMM and place and commit the PMM script into your CMake project. 1
In your
CMakeLists.txt
,include()
pmm.cmake
.Call
pmm(DDS)
and list your dependencies.
Below, we’ll walk through this in more detail.
Note
You don’t even have to have dds
downloaded and present on your system to
use dds
in PMM! Read on…
Using PMM¶
PMM is the Package Manager Manager for CMake, and is designed to offer
greater integration between a CMake build and an external package management
tool. PMM supports Conan, vcpkg, and, of course, dds
.
See also
Refer to the README.md
file in the PMM repo for information on
how to use PMM.
Getting PMM¶
To use PMM, you need to download one only file and commit it to your project:
pmm.cmake, the entrypoint for PMM 1. It is not significant where the
pmm.cmake
script is placed, but it should be noted for inclusion.
pmm.cmake
should be committed to the project because it contains version
pinning settings for PMM and can be customized on a per-project basis to alter
its behavior for a particular project’s needs.
Including PMM¶
Suppose I have downloaded and committed pmm.cmake into the tools/
subdirectory of my CMake project. To use it in CMake, I first need to
include()
the script. The simplest way is to simply include()
the file
cmake_minimum_required(VERSION 3.12)
project(MyApplication VERSION 2.1.3)
include(tools/pmm.cmake)
The include()
command should specify the path to pmm.cmake
, including
the file extension, relative to the directory that contains the CMake script
that contains the include()
command.
Running PMM¶
Simply include()
-ing PMM won’t do much, because we need to actually invoke
it.
PMM’s main CMake command is pmm()
. It takes a variety of options and
arguments for the package managers it supports, but we’ll only focus on dds
for now.
The basic signature of the pmm(DDS)
command looks like this:
pmm(DDS [DEP_FILES [filepaths...]]
[DEPENDS [dependencies...]]
[TOOLCHAIN file-or-id])
The most straightforward usage is to use only the DEPENDS
argument. For
example, if we want to import {fmt}:
pmm(DDS DEPENDS "fmt^7.0.3")
When CMake executes the pmm(DDS ...)
line above, PMM will download the
appropriate dds
executable for your platform, generate
a dds toolchain based on the CMake environment, and
then invoke dds build-deps
to build the dependencies that were listed in the
pmm()
invocation. The results from build-deps
are then imported into
CMake as IMPORTED
targets that can be used by the containing CMake project.
See also
For more in-depth discussion on dds build-deps
, refer to
Building and Using dds in Another Build System.
Note
The _deps
directory and generated CMake imports file will be placed in
the CMake build directory, out of the way of the rest of the project.
Note
The version of dds
that PMM downloads depends on the version of PMM
that is in use.
Using the IMPORTED
Targets¶
Like with dds
, CMake wants us to explicitly declare how our build targets
use other libraries. After pmm(DDS)
executes, there will be IMPORTED
targets that can be linked against.
In dds
(and in libman), a library is identified by a combination of
namespace and name, joined together with a slash /
character. This
qualified name of a library is decided by the original package author or
maintainer, and should be documented. In the case of fmt
, the only library
is fmt/fmt
.
When pmm(DDS)
imports a library, it creates a qualified name using a
double-colon “::
” instead of a slash. As such, our fmt/fmt
is imported
in CMake as fmt::fmt
. We can link against it as we would with any other
target:
add_executable(my-application app.cpp)
target_link_libraries(my-application PRIVATE fmt::fmt)
This will allow us to use {fmt} in our CMake project as an external dependency.
In all, this is our final CMakeLists.txt
:
CMakeLists.txt
¶cmake_minimum_required(VERSION 3.12)
project(MYApplication VERSION 2.1.3)
include(tools/pmm.cmake)
pmm(DDS DEPENDS fmt^7.0.3)
add_executable(my-application app.cpp)
target_link_libraries(my-application PRIVATE fmt::fmt)
Changing Compile Options¶
dds
supports setting compilation options using
toolchains. PMM supports specifying a toolchain using
the TOOLCHAIN
argument:
pmm(DDS DEPENDS fmt^7.0.3 TOOLCHAIN my-toolchain.json5)
Of course, writing a separate toolchain file just for your dependencies can be
tedious. For this reason, PMM will write a toolchain file on-the-fly when it
executes dds
. The generated toolchain is created based on the current CMake
settings when pmm()
was executed.
To add compile options, simply add_compile_options
:
add_compile_options(-fsanitize=address)
pmm(DDS ...)
The above will cause all dds
-built dependencies to compile with
-fsanitize=address
as a command-line option.
The following CMake variables and directory properties are used to generate the
dds
toolchain:
COMPILE_OPTIONS
Adds additional compiler options. Should be provided by
add_compile_options
.COMPILE_DEFINITIONS
Add preprocessor definitions. Should be provided by
add_compile_definitions
CXX_STANDARD
Control the
cxx_version
in the toolchianCMAKE_MSVC_RUNTIME_LIBRARY
Sets the
runtime
option. This option has limited support for generator expressions.CMAKE_C_FLAGS
andCMAKE_CXX_FLAGS
, and their per-config variantsSet the basic compile flags for the respective file sypes
CXX_COMPILE_LAUNCHER
Allow providing a compiler launcher, e.g.
ccache
.
Note
Calls to add_compile_options
, add_compile_definitions
, or other CMake
settings should appear before calling pmm(DDS)
, since the toolchain file
is generated and dependencies are built at that point.
add_link_options
has no effect on the dds
toolchain, as dds
does
not generate any runtime binaries.
Footnotes