cmake
is controlled by input files that by convention are
called CMakeLists.txt
, listing both configuration commands
as well as dependencies between source files and targets.
This page presents some CMakeLists.txt
examples for
potential use in your projects. (A detailed description of the
deal.II project configuration is given in the
deal.II CMake ReadMe.)
In this section, we start out with a minimal CMakeLists.txt
based on the DEAL_II_SETUP_TARGET
macro. This method gives
full control of what's happening and is easily extensible to more complex
projects, as exemplified in the subsections here and later in the section
on advanced topics. Here is a full example
(plain text
version):
cmake_minimum_required(VERSION 3.13.4) find_package(deal.II 9.5.0 REQUIRED HINTS ${DEAL_II_DIR} ../ ../../ $ENV{DEAL_II_DIR} ) deal_ii_initialize_cached_variables() project(myproject) add_executable(mycode mycode.cc) deal_ii_setup_target(mycode)
The first line of this code makes sure that a sufficiently high version of CMake is installed. 2.8.8 is the minimal version required to set up deal.II, therefore it is safe to use this number here.
Next, we find our deal.II installation with the help of the
FIND_PACKAGE
command. In this case requiring at least
version 9.0.0. The HINTS
are a list of directories where the
install directory of deal.II is likely to be found. First, the location
possibly defined in the CMake variable DEAL_II_DIR
is
considered. After that, we check whether we are in a subdirectory (first
and second level) of the deal.II installation and otherwise use the
environment variable DEAL_II_DIR
. If all of these hints fail
the default system locations /usr/
and
/usr/local/
are considered. The list after
HINTS
can be changed according to your preferences.
After finding the deal.II project, we fetch a set of cached variables
with the
deal_ii_initialize_cached_variables
macro. You
can inspect these for instance with ccmake
.
Every CMakeLists.txt
must contain a project definition,
which we do next.
Finally, the last two lines define the executable that is to be produced
and its source code. The
deal_ii_setup_target
macro will set up necessary include
directories, compile flags, compile definitions, link flags and the link
interface.
In order to specify multiple executable targets, simply repeat
the last two lines of the simple CMakeLists.txt
:
add_executable(mycode2 mycode2.cc) deal_ii_setup_target(mycode2) add_executable(mycode3 mycode3.cc) deal_ii_setup_target(mycode3)If the list gets longer, consider using a loop, possibly with GLOB.
Adding a library is as simple as adding an executable target. We specify the library name and then have to tell cmake that the executables depend on it. The code in the simple file below the project definition accordingly changes for instance to:
add_library(mylib libsrc1.cc libsrc2.cc libsrc3.cc) deal_ii_setup_target(mylib) add_executable(mycode mycode.cc) deal_ii_setup_target(mycode) target_link_libraries(mycode mylib)
When you have multiple targets, repeat the last line of code for each of them. Accordingly, a loop becomes even more attractive.
If you only have a single file or few files with common source code, an alternative to creating a library might be the option:
add_executable(mycode mycode.cc common.cc) deal_ii_setup_target(mycode) add_executable(mycode2 mycode2.cc common.cc) deal_ii_setup_target(mycode2)
You should be aware though that in this case common.cc
will
be compiled for each target, not only once. If you want to avoid this and
still don't want to use a shared library or static archive, another option
is to create an OBJECT
"library":
add_library(common OBJECT common.cc) deal_ii_setup_target(common) add_executable(mycode mycode.cc $<TARGET_OBJECTS:common>) deal_ii_setup_target(mycode) add_executable(mycode2 mycode2.cc $<TARGET_OBJECTS:common>) deal_ii_setup_target(mycode2)This will compile
common.cc
once for the object target
common
and link the resulting object file into the two
executables.
The build type is controlled via the variable
CMAKE_BUILD_TYPE
. If it is set to Debug
executables and libraries specified in your CMakeLists.txt
file will be compiled in debug mode and linked against the debug version of
the deal.II library. Contrary, Release
will build in optimized
mode and link against the optimized release version of deal.II. You can set
CMAKE_BUILD_TYPE
with the help of ccmake
or via
cmake
on the command line:
$ cmake -DCMAKE_BUILD_TYPE="Debug" . $ cmake -DCMAKE_BUILD_TYPE="Release" .Alternatively, you can specify custom targets to switch the build type and compile automatically:
add_custom_target(debug COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${CMAKE_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all COMMENT "Switch CMAKE_BUILD_TYPE to Debug" ) add_custom_target(release COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Release ${CMAKE_SOURCE_DIR} COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all COMMENT "Switch CMAKE_BUILD_TYPE to Release" )With that, switching the build type and compiling the project can be done very conveniently via:
$ make debug $ make release
If you wish to have a "run" target for make, like in the deal.II tutorial, specify one this way (obviously, a single "run" target can only run a single executable):
add_custom_target(run COMMAND mycode COMMENT "Run with ${CMAKE_BUILD_TYPE} configuration" )
CMakeLists.txt
This section covers some advanced topics for a user
CMakeLists.txt
file.
For complex projects it is desirable to organize source code and header files in subdirectories. Assume the following project structure with a library "mylib" and an executable "mycode":
mylib/source/*.cc mylib/include/*.h mycode/source/*.cc mycode/include/*.hIn this case the top level
CMakeLists.txt
file may be:
# top level CMakeLists.txt cmake_minimum_required(VERSION 3.13.4) find_package(deal.II 9.5.0 REQUIRED) deal_ii_initialize_cached_variables() project(myproject) add_subdirectory(mylib) add_subdirectory(mycode)The
add_subdirectory
statement will include the CMakeLists.txt
file in the specified subdirectory. In our case:
# mylib/CMakeLists.txt include_directories(include) add_library(mylib source/mylib1.cc source/mylib2.cc ) deal_ii_setup_target(mylib)We have to add the directory
include
for the header files to
the current include directories with the INCLUDE_DIRECTORIES
statement (see this section for
details). The corresponding configuration file for the executable looks
like:
# mycode/CMakeLists.txt include_directories( include ${CMAKE_SOURCE_DIR}/mylib/include ) add_executable(mycode source/mycode.cc) deal_ii_setup_target(mycode) target_link_libraries(mycode mylib)
Please note that CMakeLists.txt
files have directory scope.
Any manipulation of properties and variables have only effect in the
current directory (and subdirectories, included with
add_subdirectory
. The level above will not be affected.
Therefore, we have to specify the include directories for "mylib" again in
the subdirectory mycode
- this time with full path
${CMAKE_SOURCE_DIR}/mylib/include
.
CMake defines the following variables for access to important directories:
CMAKE_SOURCE_DIR - the source directory (i.e. the directory of the top level CMakeLists.txt file) CMAKE_BINARY_DIR - the (top level) build directory CMAKE_CURRENT_SOURCE_DIR - the current source directory, i.e. location of the currently processed CMakeLists.txt file (top level or included via ADD_SUBDIRECTORY) CMAKE_CURRENT_BINARY_DIR - the build (sub)directory corresponding to CMAKE_CURRENT_SOURCE_DIR
Control statements in CMake take the following form:
if(<expression>) ... endif()or in long form:
if(<expression1>) ... elseif(<expression2>) ... else() ... endif()Please note the (somehow uncommon) empty, opening and closing brackets behind
else()
and endif()
.
<expression>
can take a multitude of different forms,
have a look at the
CMake
documentation for a complete list. Important examples are:
if(${variable}) - the body will be evaluated if the variable "variable" is defined and synonymous to true, e.g. 1, TRUE, ON, YES (modulo case insensitivity) if(variable MATCHES <regular expression>) - the body will be evaluated if the variable "variable" is defined and matches the specified regular expression if("${variable}" STREQUAL "foobar") - the body will be evaluated if both strings are equal. Note that "${variable}" will be replaced by the content of the (string) variable "variable"
An expression can be negated by prefixing NOT
:
IF(NOT <expression>) ... ENDIF()
Loops are implemented with the help of WHILE
and
FOR
statements. The former takes the same
<expression>
as the IF
statement:
while(<expression>) ... endwhile()Given a variable
list
containing a list, the individual
elements element
can be accessed with a FOREACH
statement:
foreach(element ${list}) ... endforeach()Note: It is also possible to specify the list directly:
foreach(element foo bar baz) # The variable element will iterate through foo, bar and baz. endforeach
A very common task is to pick up a list of source files from a
directory. You can either manage a list of source files in
CMakeLists.txt
by hand, e.g. by manually updating all source
files for a given target, or you can use a glob to automate this process.
The following example will pick up every source file under
SOURCE_DIR/sources/
and add it to an executable:
file(GLOB sources ${CMAKE_SOURCE_DIR}/source/*.cc) add_executable(mycode ${sources})
Please be aware of one caveat of this approach: Due to the fact that
CMake is a build system generator the resulting build configuration
(for make) has no way to detect whether a new source file was added (or
removed) and that it has to call back to cmake. So, after adding a new
source file you have to touch a CMakeLists.txt
file or to run
cmake .
again by hand.
deal_ii_setup_target
revisited
The deal_ii_setup_target
macro is responsible for setting up
a target to compile and link against deal.II. It will append the
include_directories
property with the location of the
deal.II include directories, and append the properties
COMPILE_FLAGS
, COMPILE_DEFINITIONS
and
LINK_FLAGS
by their respective values from the deal.II
configuration (depending on build type and available debug and/or
optimized flavor of the library).
Except in the case of an object library, the specified target will also be set up to link against deal.II (and its transitive link interface) as well.
Optionally, the deal_ii_setup_target
macro takes an
additional argument DEBUG
, or RELEASE
, after
the target name to explicitly state the library flavor the target should
be set up for. If the parameter is omitted, the correct choice is deduced
from the current build type. (This is supported for the build types
Debug
, and Release
. In case of
DebugRelease
the additional argument is always required.)
Note: The flags that are added with DEAL_II_SETUP_TARGET
to
the target come last in the final link compiler invocation, or linker
invocation. This means they take precedence over all flags defined via
globally via CMAKE_CXX_FLAGS
, etc., or as a directory
property. If you wish to modify flags or preprocessor definitions set up
with DEAL_II_SETUP_TARGET
modify one of the following
variables (see the section about deal.IIConfig.cmake
for
details):
DEAL_II_CXX_FLAGS DEAL_II_CXX_FLAGS_DEBUG DEAL_II_CXX_FLAGS_RELEASE DEAL_II_LINKER_FLAGS DEAL_II_LINKER_FLAGS_DEBUG DEAL_II_LINKER_FLAGS_RELEASE DEAL_II_DEFINITIONS DEAL_II_DEFINITIONS_DEBUG DEAL_II_DEFINITIONS_RELEASE
DEAL_II_INITIALIZE_CACHED_VARIABLES
revisited
The DEAL_II_INITIALIZE_CACHED_VARIABLES
macro is responsible
for setting up cached variables and has to invoked before the
PROJECT
call:
find_package(deal.II 9.5.0 REQUIRED) deal_ii_initialize_cached_variables() project(myproject)The macro will set an uninitialized
CMAKE_BUILD_TYPE
variable
to the build type of deal.II, i.e. DEAL_II_BUILD_TYPE
. If
CMAKE_BUILD_TYPE
is specified it will automatically be reset
if the given value is unsupported by the deal.II installation (i.e., if it
is not equal to Debug
, Release
, or
DebugRelease
).
Furthermore, this macro sets the C++ compiler to the one used for compiling
the deal.II library. The variables CMAKE_CXX_FLAGS
,
CMAKE_CXX_FLAGS_DEBUG
, and
CMAKE_CXX_FLAGS_RELEASE
will be initialized with the empty
string.
Note: If you wish to override the flags and definitions set by the
deal_ii_setup_target
macro you have to override the
corresponding DEAL_II_*
variable instead. See the
documentation of deal_ii_setup_target
for further details.
You can specify custom include directories and compile definitions prior to a target definition on a per directory basis (have a look at the CMake documentation for further details):
include_directories(include1 include2) add_definitions(-DFOO -DBAR="BAZ") add_executable(...) # or add_library(...)
For external libraries that provide a CMake project configuration or where a CMake find module is available, including this external library in your project is more or less straightforward. E.g. to require an external project "foo" at least of version 8.0 write:
find_package(foo 8.0 REQUIRED)Alternatively, the version number and
REQUIRED
keyword can be
omitted. Depending on the external library, the project configuration or
find macro will usually define variables like FOO_INCLUDE_DIRS
and FOO_LIBRARIES
that can be directly used in your
CMakeLists.txt
file:
include_directories(${FOO_INCLUDE_DIRS}) add_executable(mycode mycode.cc) deal_ii_setup_target(mycode) target_link_libraries(mycode ${FOO_LIBRARIES})The first statement will set up the include directories for the following targets as explained above. The last statement with
target_link_libraries
will add the libraries in the
FOO_LIBRARIES
variable to the link interface of the target
mycode
.
The simple run statement as explained above will run the generated executable in the
build directory. Sometimes it is more desirable to run the executable in a
dedicated run
directory within in the build directory which is
a copy of a skeleton run
folder from the source directory:
# Copy folder run from the source to the build directory: file(COPY ${CMAKE_SOURCE_DIR}/run DESTINATION ${CMAKE_BINARY_DIR}) add_executable(mycode mycode.cc) set_property(TARGET mycode PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/run ) add_custom_target(run COMMAND mycode WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/run )Here, we modify the
RUNTIME_OUTPUT_DIRECTORY
property of our
target so that the executable is linked inside our designated
run
folder, so that it is conveniently available as an
executable inside the run folder. Furthermore, we specify a
WORKING_DIRECTORY
for the run
target, so that
make run
invokes the executable inside the intended run
directory.
If you want the make install
to install your project to
CMAKE_INSTALL_PREFIX
(that may be set on command line or in
the cache during the configuration stage), add appropriate
INSTALL
statements. To install e.g. a project consisting of a
library and an executable as well as a run folder:
# [...] all the target definitions INSTALL(TARGETS mylib DESTINATION lib) INSTALL(TARGETS mycode DESTINATION bin) INSTALL(DIRECTORY run DESTINATION share/mycode/run)
If you want a make interface similar to the deal.II library and
its tutorial, namely maker targets for debug and release
versions, running the code and cleaning, the easiest way to
write a CMakeLists.txt
file may be to use
an "autopilot" style macro. Here is a minimalistic example for the
step-1 tutorial program (plain text version) that can be used for simple
projects:
find_package(deal.II 9.5.0 REQUIRED HINTS ${DEAL_II_DIR} ../ ../../ $ENV{DEAL_II_DIR} # You can specify additional hints for search paths here, e.g. # $ENV{HOME}/workspace/deal.II ) # Set the name of the project and target: set(TARGET "step-1") # Declare all source files the target consists of: set(TARGET_SRC step-1.cc # You can specify additional files here! ) cmake_minimum_required(VERSION 3.13.4) deal_ii_initialize_cached_variables() project(${TARGET} CXX) deal_ii_invoke_autopilot()This
CMakeLists.txt
is intended for use with a small
project and in-source build (i.e., one does not create a separate
build directory as we recommend for the deal.II build
in the readme
file). Using this input file, you can run cmake
in the
source directory as follows:
$ cd step-1 $ cmake . [...] ### # # Project step-1 set up with deal.II-8.3 found at # /usr # # CMAKE_BUILD_TYPE: Debug # # You can now run # $ make - to compile and link the program # $ make run - to (compile, link and) run the program # # $ make debug - to switch the build type to 'Debug' # $ make release - to switch the build type to 'Release' # # $ make edit_cache - to change (cached) configuration variables # and rerun the configure and generate phases of CMake # # $ make strip_comments - to strip the source files in this # directory off the documentation comments # $ make clean - to remove the generated executable as well as # all intermediate compilation files # $ make runclean - to remove all output generated by the program # $ make distclean - to clean the directory from _all_ generated # files (includes clean, runclean and the removal # of the generated build system) # $ make info - to view this message again # # Have a nice day! # ###There are two additional configuration options (in addition to
TARGET
and TARGET_SRC
) that can be set via
variables before DEAL_II_INVOKE_AUTOPILOT()
is called
(plain text version):
# (Optional) # Specify a list of files (file globs) that will be removed # with the "make runclean" and "make distclean" targets. # (If empty, sensible default values will be used.) set(CLEAN_UP_FILES # a custom list of globs, e.g. *.log *.vtk ) # (Optional) # A custom command line that should be invoked by "make run". # (If empty, ./${TARGET} will be invoked.) set(TARGET_RUN # a custom command line, e.g. mpirun -np 2 ${TARGET} )
deal.IIConfig.cmake
Importing the deal.IIConfig.cmake file via FIND_PACKAGE
will set the following variables and macros; all of the form
DEAL_II_*
:
# # General package information: # DEAL_II_PACKAGE_NAME DEAL_II_PACKAGE_VERSION - the full package version string, e.g. "8.1.pre" DEAL_II_PACKAGE_VENDOR DEAL_II_PACKAGE_DESCRIPTION DEAL_II_VERSION - numerical version number (with "pre" and "rc?" replaced by "0"), e.g. "8.2.0" DEAL_II_VERSION_MAJOR - the major number, e.g. "8" DEAL_II_VERSION_MINOR - the minor version number, e.g. "2" DEAL_II_VERSION_SUBMINOR - the minor version number, e.g. "0" DEAL_II_GIT_BRANCH - name of the local git branch of the source directory DEAL_II_GIT_REVISION - full sha1 revision of the current git HEAD DEAL_II_GIT_SHORTREV - short sha1 revision of the current git HEAD DEAL_II_BUILD_TYPE - the configured build type, e.g. "DebugRelease" DEAL_II_BUILD_TYPES - an all caps list of available configurations, e.g. "DEBUG;RELEASE" # # Information about component locations: # DEAL_II_PATH DEAL_II_SHARE_RELDIR DEAL_II_DOCREADME_RELDIR DEAL_II_DOCHTML_RELDIR DEAL_II_EXAMPLES_RELDIR DEAL_II_EXECUTABLE_RELDIR DEAL_II_INCLUDE_RELDIR DEAL_II_LIBRARY_RELDIR DEAL_II_PROJECT_CONFIG_RELDIR DEAL_II_BUILD_DIR - true if deal.II was picked up from a build dir, false if the configuration is from an installation # # Compiler and linker configuration # DEAL_II_CXX_COMPILER - the compiler used to compile deal.II DEAL_II_CXX_FLAGS - compile flags for all configurations DEAL_II_CXX_FLAGS_DEBUG - _additional_ compile flags for the debug configuration DEAL_II_CXX_FLAGS_RELEASE - _additional_ compile flags for the release configuration DEAL_II_LINKER_FLAGS - link flags for all configurations DEAL_II_LINKER_FLAGS_DEBUG - _additional_ link flags for debug configuration DEAL_II_LINKER_FLAGS_RELEASE - _additional_ link flags for release configuration DEAL_II_DEFINITIONS - compile definitions for all configurations DEAL_II_DEFINITIONS_DEBUG - _additional_ compile definitions for debug configuration DEAL_II_DEFINITIONS_RELEASE - _additional_ compile definitions for release configuration # # Information about MPI runtime for the mpi implementation used in the # deal.II library # DEAL_II_MPIEXEC DEAL_II_MPIEXEC_NUMPROC_FLAG DEAL_II_MPIEXEC_PREFLAGS DEAL_II_MPIEXEC_POSTFLAGS # # Information about include directories and libraries # DEAL_II_INCLUDE_DIRS DEAL_II_LIBRARIES_DEBUG - a list of the full link interface for the debug configuration DEAL_II_LIBRARIES_RELEASE - a list of the full link interface for the release configuration DEAL_II_LIBRARIES - full list of libraries with "debug" and "optimized" keywords # # Information about library targets # DEAL_II_TARGET_CONFIG - the target config file DEAL_II_TARGET_DEBUG - the name of the debug target that is available after inclusion of the target config file DEAL_II_TARGET_RELEASE - the name of the release target DEAL_II_TARGET - full list of targets with "debug" and "optimized" keywords # # Feature configuration: The following booleans are set to "ON" or "OFF" depending # on the current feature configuration (note that other variables are defined # only if one of the following variable is "ON", for example DEAL_II_MPI_VERSION # is only defined if DEAL_II_WITH_MPI is "ON"): # DEAL_II_WITH_64BIT_INDICES DEAL_II_WITH_ADOLC DEAL_II_WITH_ARPACK DEAL_II_WITH_ASSIMP DEAL_II_WITH_COMPLEX_VALUES DEAL_II_WITH_CUDA DEAL_II_WITH_GINKGO DEAL_II_WITH_GMSH DEAL_II_WITH_GSL DEAL_II_WITH_HDF5 DEAL_II_WITH_LAPACK LAPACK_WITH_64BIT_BLAS_INDICES DEAL_II_LAPACK_WITH_MKL DEAL_II_WITH_METIS DEAL_II_WITH_MPI DEAL_II_WITH_MUPARSER DEAL_II_WITH_OPENCASCADE DEAL_II_WITH_P4EST DEAL_II_WITH_PETSC DEAL_II_WITH_SCALAPACK DEAL_II_WITH_SLEPC DEAL_II_WITH_SUNDIALS DEAL_II_WITH_SYMENGINE DEAL_II_WITH_TASKFLOW DEAL_II_WITH_TBB DEAL_II_WITH_TRILINOS DEAL_II_WITH_UMFPACK DEAL_II_WITH_ZLIB