Building projects with CMake
Victor Eijkhout
Fall 2023
1 Eijkhout – CMake tutorial – Fall 2023
Justification
CMake is a portable build system that is becoming a de facto standard for
C++ package management.
Also usable with C and Fortran.
2 Eijkhout – CMake tutorial – Fall 2023
Table of contents
1 Help! This software uses CMake!
Using a cmake-based library
Using CMake packages through pkgconfig
2 Help! I want to write CMake myself!
Make your own CMake configuration
3 Help! I want people to use my CMake package!
Making your package discoverable through pkgconfig
4 Example libraries
Parallelism
5 TBB
Data packages
More libraries
3 Eijkhout – CMake tutorial – Fall 2023
Help! This software uses
CMake!
4 Eijkhout – CMake tutorial – Fall 2023
Using a cmake-based library
5 Eijkhout – CMake tutorial – Fall 2023
What are we talking here?
You have downloaded a library
It contains a file CMakeLists.txt
⇒ you need to install it with CMake.
. . . and then figure out how to use it in your code.
6 Eijkhout – CMake tutorial – Fall 2023
Building with CMake
Use CMake for the configure stage, then make:
cmake -D CMAKE_INSTALL_PREFIX=/home/yourname/packages \
/home/your/software/package ## source location
make
make install
or
do everything with CMake:
cmake ## arguments
cmake --build ## stuff
cmake --install ## stuff
We focus on the first option; the second one is portable to non-Unix
environments.
7 Eijkhout – CMake tutorial – Fall 2023
What does this buy you?
1. The source directory is untouched
2. The build directory contains all temporaries
3. Your install directory (as specified to CMake) now contains executables,
libraries, headers etc.
You can add these to $PATH, compiler options, $LD_LIBRARY_PATH.
But see later . . .
8 Eijkhout – CMake tutorial – Fall 2023
The build/make cycle
CMake creates makefiles;
makefiles ensure minimal required compilation
cmake ## make the makefiles
make ## compile your project
emacs onefile.c ## edit
make ## minimal recompile
Only if you add (include) files do you rerun CMake.
9 Eijkhout – CMake tutorial – Fall 2023
Directory structure: two options
dir dir
src src
build build
install install
In-source build: pretty common
Out-of-source build: cleaner because never touches the source tree
Some people skip the install step, and use everything from the build
directory.
10 Eijkhout – CMake tutorial – Fall 2023
Out-of-source build: preferred
Work from a build directory
Specify prefix and location of CMakeLists.txt
1 ls some_package_1.0.0 # we are outside the source
2 ls some_package_1.0.0/CMakeLists.txt # source contains cmake file
3 mkdir builddir # location for temporaries
4 cd builddir # goto build location
5 cmake -D CMAKE_INSTALL_PREFIX=../installdir \
6 ../some_package_1.0.0 # cmake invocation
7 make # make all tmp data in build loc
8 make install # move only final products
11 Eijkhout – CMake tutorial – Fall 2023
Example: eigen
Download from
https://eigen.tuxfamily.org/index.php
and install.
What compiler is it finding? If you are at TACC, is it the module you have
loaded?
12 Eijkhout – CMake tutorial – Fall 2023
Basic customizations
Compiler settings:
cmake -D CMAKE_CXX_COMPILER=icpx
Alternatively:
export CXX=icpx
cmake .......
Many settings can be done on the commandline:
-D BUILD_SHARED_LIBS=ON
Also check out the ccmake utility.
13 Eijkhout – CMake tutorial – Fall 2023
Tracing and logging
CMake prints some sort of progress messages.
To see commandlines:
cmake -D CMAKE_VERBOSE_MAKEFILE=ON ...
make V=1
CMake leaves behind a log and error file, but these are insufficent:
⇒ use the above verbose mode and capture all output.
14 Eijkhout – CMake tutorial – Fall 2023
Using CMake packages through pkgconfig
15 Eijkhout – CMake tutorial – Fall 2023
What are we talking here?
You have just installed a CMake-based library.
Now you need it in your own code, or in another library.
How easy can we make that?
16 Eijkhout – CMake tutorial – Fall 2023
Problem
You want to install an application/package
. . . which needs 2 or 3 other packages.
gcc -o myprogram myprogram.c \
-I/users/my/package1/include \
-L/users/my/package1/lib \
-I/users/my/package2/include/package \
-L/users/my/package2/lib64
or:
cmake \
-D PACKAGE1_INC=/users/my/package1/include \
-D PACKAGE1_LIB=/users/my/package1/lib \
-D PACKAGE2_INC=/users/my/package2/include/package \
-D PACKAGE2_LIB=/users/my/package2/lib64 \
../newpackage
Can this be made simpler?
17 Eijkhout – CMake tutorial – Fall 2023
Finding packages with ‘pkg config’
Many packages come with a package.pc file
Add that location to PKG_CONFIG_PATH
The package can now be found by other CMake-based packages.
18 Eijkhout – CMake tutorial – Fall 2023
Package config settings
Let’s say you’ve installed a library with CMake.
Somewhere in the installation is a .pc file:
find $TACC_SMTHNG_DIR -name \*.pc
${TACC_SMTHNG_DIR}/share/pkgconfig/smthng3.pc
That location needs to be on the PKG_CONFIG_PATH:
export PKG_CONFIG_PATH=${TACC_SMTHNG_DIR}/share/pkgconfig:${
PKG_CONFIG_PATH}
19 Eijkhout – CMake tutorial – Fall 2023
Example: eigen
Can you find the .pc file in the Eigen installation?
20 Eijkhout – CMake tutorial – Fall 2023
Scenario 1: finding without cmake
Packages with a .pc file can be found through the pkg-config command:
gcc -o myprogram myprogram.c \
$( pkg-config --cflags package1 ) \
$( pkg-config --libs package1 )
In a makefile:
CFLAGS = -g -O2 $$( pkg-config --cflags package1 )
21 Eijkhout – CMake tutorial – Fall 2023
Example: eigen
#include "Eigen/Core"
int main(int argc,char **argv) {
return 0;
}
Can you compile this on the commandline, using pkg-config? Small
problem: ‘eigen’ wants to be called ‘eigen3’.
22 Eijkhout – CMake tutorial – Fall 2023
Scenario 2: finding from CMake
You are installing a CMake-based library
and it needs Eigen, which is also CMake-based
1. you install Eigen with CMake, as above
2. you add the location of eigen.pc to PKG_CONFIG_PATH
3. you run the installation of the higher library:
this works because it can now find Eigen.
23 Eijkhout – CMake tutorial – Fall 2023
Lifting the veil
So how does a CMake install find libraries such as Eigen?
1 cmake_minimum_required( VERSION 3.12 )
2 project( eigentest )
3
4 find_package( PkgConfig REQUIRED )
5 pkg_check_modules( EIGEN REQUIRED eigen3 )
6
7 add_executable( eigentest eigentest.cxx )
8 target_include_directories(
9 eigentest PUBLIC
10 ${EIGEN_INCLUDE_DIRS})
Note 1: header-only so no library, otherwise PACKAGE_LIBRARY_DIRS and
PACKAGE_LIBRARIES defined.
Note 2: you will learn how to write these configurations in the second part.
24 Eijkhout – CMake tutorial – Fall 2023
Summary for now
You can use CMake to install libraries;
You can use these libraries from commandline / makefile;
You can let other CMake-based libraries find them.
25 Eijkhout – CMake tutorial – Fall 2023
Other discovery mechanisms
Some packages come with FindWhatever.cmake or similar files.
Add package root to CMAKE_MODULE_PATH
Pity that there is not just one standard.
These define some macros, but you need to read the docs to see which.
Pity that there is not just one standard.
Some examples follow.
26 Eijkhout – CMake tutorial – Fall 2023
Help! I want to write CMake
myself!
27 Eijkhout – CMake tutorial – Fall 2023
Make your own CMake configuration
28 Eijkhout – CMake tutorial – Fall 2023
What are we talking here?
You have a code that you want to distribute in source form for easy
installation.
You decide to use CMake for portability.
You think that using CMake might make life easier.
⇒ To do: write the CMakeLists.txt file.
29 Eijkhout – CMake tutorial – Fall 2023
The CMakeLists file
cmake_minimum_required( VERSION 3.12 )
project( myproject VERSION 1.0 )
Which cmake version is needed for this file?
(CMake has undergone quite some evolution!)
Give a name to your project.
Maybe pick a language.
C and C++ available by default, or:
enable_language(Fortran)
(list: C, CXX, CSharp, CUDA, OBJC, OBJCXX, Fortran, HIP, ISPC, Swift,
and a couple of variants of ASM)
30 Eijkhout – CMake tutorial – Fall 2023
Target philosophy
Declare a target: something that needs to be built, and specify what is
needed for it
add_executable( myprogram )
target_sources( myprogram PRIVATE program.cxx )
Use of macros:
add_executable( ${PROJECT_NAME} )
Do things with the target, for instance state where it is to be installed:
install( TARGETS myprogram DESTINATION . )
relative to the prefix location.
31 Eijkhout – CMake tutorial – Fall 2023
Example: single source
Build an executable from a single source file:
cmake_minimum_required( VERSION 3.12 )
project( singleprogram VERSION 1.0 )
add_executable( program )
target_sources( program PRIVATE program.cxx )
install( TARGETS program DESTINATION . )
32 Eijkhout – CMake tutorial – Fall 2023
Deprecated usage
add_executable( myprogram myprogram.c myprogram.h )
Prefer ‘target’ design.
33 Eijkhout – CMake tutorial – Fall 2023
Exercise
Write a ‘hello world’ program;
Make a CMake setup to compile and install it;
Test it all.
34 Eijkhout – CMake tutorial – Fall 2023
Exercise: using the Eigen library
This is a short program using Eigen:
#include <iostream>
#include <Eigen/Dense>
int main() {
// Define a 3x3 matrix
Eigen::Matrix3d matrix;
matrix << 1, 2, 3,
4, 5, 6,
7, 8, 9;
std::cout << "Original matrix:\n" << matrix << std::endl;
return 0;
}
Make a CMake setup to compile and install it;
Test it.
35 Eijkhout – CMake tutorial – Fall 2023
Make your own library
First a library that goes into the executable:
add_library( auxlib )
target_sources( auxlib PRIVATE aux.cxx aux.h )
target_link_libraries( program PRIVATE auxlib )
36 Eijkhout – CMake tutorial – Fall 2023
Library during build, setup
Full configuration for an executable that uses a library:
1 cmake_minimum_required( VERSION 3.12 )
2 project( cmakeprogram VERSION 1.0 )
3
4 add_executable( program )
5 target_sources( program PRIVATE program.cxx )
6
7 add_library( auxlib )
8 target_sources( auxlib PRIVATE aux.cxx aux.h )
9
10 target_link_libraries( program PRIVATE auxlib )
11
12 install( TARGETS program DESTINATION . )
Library shared by default; see later.
37 Eijkhout – CMake tutorial – Fall 2023
Shared and static libraries
In the configuration file:
add_library( auxlib STATIC )
# or
add_library( auxlib SHARED )
(default shared if left out), or by adding a runtime flag
cmake -D BUILD_SHARED_LIBS=TRUE
Build both by having two lines, one for shared, one for static.
Related: the -fPIC compile option is set by
CMAKE_POSITION_INDEPENDENT_CODE:
cmake -D CMAKE_POSITION_INDEPENDENT_CODE=ON
38 Eijkhout – CMake tutorial – Fall 2023
Release a library
To have the library released too, use PUBLIC.
Add the library target to the install command.
39 Eijkhout – CMake tutorial – Fall 2023
Example: released library
1 cmake_minimum_required( VERSION 3.12 )
2 project( cmakeprogram VERSION 1.0 )
3
4 add_executable( program )
5 target_sources( program PRIVATE program.cxx )
6
7 add_library( auxlib STATIC )
8 target_sources( auxlib PRIVATE lib/aux.cxx lib/aux.h )
9
10 target_link_libraries( program PUBLIC auxlib )
11 target_include_directories( program PRIVATE lib )
12
13 install( TARGETS program DESTINATION bin )
14 install( TARGETS auxlib DESTINATION lib )
15 install( FILES lib/aux.h DESTINATION include )
Note the separate destination directories.
40 Eijkhout – CMake tutorial – Fall 2023
We are getting realistic
The previous setup was messy
Better handle the library through a recursive cmake
and make the usual lib include bin setup
41 Eijkhout – CMake tutorial – Fall 2023
Recursive setup, main directory
Declare that there is a directory to do recursive make:
1 cmake_minimum_required( VERSION 3.13 )
2 # needs >3.12 to let the executable target find the .h file
3 project( cmakeprogram VERSION 1.0 )
4
5 add_executable( program )
6 target_sources( program PRIVATE program.cxx )
7 add_subdirectory( lib )
8 target_include_directories(
9 program PUBLIC lib )
10 target_link_libraries( program PUBLIC auxlib )
11 install( TARGETS program DESTINATION bin )
(Note that the name of the library comes from the subdirectory)
42 Eijkhout – CMake tutorial – Fall 2023
Recursive setup, subdirectory
Installs into lib and include
1 cmake_minimum_required( VERSION 3.13 )
2 # needs >3.12 to let the executable target find the .h file
3
4 add_library( auxlib STATIC )
5 target_sources( auxlib
6 PRIVATE aux.cxx
7 PUBLIC aux.h )
8 install( TARGETS auxlib DESTINATION lib )
9 install( FILES aux.h DESTINATION include )
43 Eijkhout – CMake tutorial – Fall 2023
External libraries
Use LD_LIBRARY_PATH, or
use rpath.
(Apple note: forced to use second option)
set_target_properties(
${PROGRAM_NAME} PROPERTIES
BUILD_RPATH "${CATCH2_LIBRARY_DIRS};${FMTLIB_LIBRARY_DIRS}"
INSTALL_RPATH "${CATCH2_LIBRARY_DIRS};${FMTLIB_LIBRARY_DIRS}"
)
44 Eijkhout – CMake tutorial – Fall 2023
Install other project
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
45 Eijkhout – CMake tutorial – Fall 2023
Help! I want people to use
my CMake package!
46 Eijkhout – CMake tutorial – Fall 2023
Making your package discoverable through pkgconfig
47 Eijkhout – CMake tutorial – Fall 2023
How does pkgconfig work?
Use the PKG_CONFIG_PATH variable:
$ module show cxxopts 2>&1 | grep -i pkg
prepend_path("PKG_CONFIG_PATH","/opt/cxxopts/intel23/lib64/pkgconfig")
48 Eijkhout – CMake tutorial – Fall 2023
Write your own .pc file
configure_file line in CMakeLists.txt:
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
@ONLY)
49 Eijkhout – CMake tutorial – Fall 2023
Write your own .pc file’
The .pc.in file:
prefix="@CMAKE_INSTALL_PREFIX@"
exec_prefix="${prefix}"
libdir="${prefix}/lib"
includedir="${prefix}/include"
Name: @PROJECT_NAME@
Description: @CMAKE_PROJECT_DESCRIPTION@
Version: @PROJECT_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -l@libtarget@
Note the initial cap!
Combination of built-in variables and your own:
set( libtarget auxlib )
50 Eijkhout – CMake tutorial – Fall 2023
Installing the pc file
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc
DESTINATION share/pkgconfig
)
51 Eijkhout – CMake tutorial – Fall 2023
Example libraries
52 Eijkhout – CMake tutorial – Fall 2023
Parallelism
53 Eijkhout – CMake tutorial – Fall 2023
MPI from C
MPI has a module:
find_package( MPI )
target_include_directories(
${PROJECT_NAME} PUBLIC
${MPI_C_INCLUDE_DIRS} )
target_link_libraries(
${PROJECT_NAME} PUBLIC
${MPI_C_LIBRARIES} )
54 Eijkhout – CMake tutorial – Fall 2023
MPI from C++
find_package( MPI )
target_include_directories(
${PROJECT_NAME} PUBLIC
${MPI_CXX_INCLUDE_DIRS} )
target_link_libraries(
${PROJECT_NAME} PUBLIC
${MPI_CXX_LIBRARIES} )
55 Eijkhout – CMake tutorial – Fall 2023
MPI from Fortran90
find_package( MPI )
target_include_directories(
${PROJECT_NAME} PUBLIC
${MPI_INCLUDE_DIRS} )
target_link_directories(
${PROJECT_NAME} PUBLIC
${MPI_LIBRARY_DIRS} )
target_link_libraries(
${PROJECT_NAME} PUBLIC
${MPI_Fortran_LIBRARIES} )
56 Eijkhout – CMake tutorial – Fall 2023
MPI from Fortran2008
if( MPI_Fortran_HAVE_F08_MODULE )
else()
message( FATAL_ERROR "No f08 module for this MPI" )
endif()
57 Eijkhout – CMake tutorial – Fall 2023
MPL
find_package( mpl REQUIRED )
target_include_directories(
${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
mpl::mpl )
target_link_libraries(
${PROJECT_NAME} PUBLIC
mpl::mpl )
58 Eijkhout – CMake tutorial – Fall 2023
OpenMP from C
find_package(OpenMP)
target_link_libraries(
${PROJECT_NAME}
PUBLIC OpenMP::OpenMP_C )
59 Eijkhout – CMake tutorial – Fall 2023
OpenMP from C++
find_package(OpenMP)
if(OpenMP_CXX_FOUND)
else()
message( FATAL_ERROR "Could not find OpenMP" )
endif()
target_link_libraries(
${PROJECT_NAME}
PUBLIC OpenMP::OpenMP_CXX )
60 Eijkhout – CMake tutorial – Fall 2023
OpenMP from Fortran
enable_language(Fortran)
find_package(OpenMP)
target_link_libraries(
${PROJECT_NAME}
PUBLIC OpenMP::OpenMP_Fortran )
61 Eijkhout – CMake tutorial – Fall 2023
TBB
62 Eijkhout – CMake tutorial – Fall 2023
TBB
find_package(TBB REQUIRED)
target_link_libraries( ${PROJECT_NAME} PUBLIC TBB::tbb)
63 Eijkhout – CMake tutorial – Fall 2023
Kokkos
find_package(Kokkos REQUIRED)
target_link_libraries(myTarget Kokkos::kokkos)
Either set CMAKE_PREFIX_PATH or add
-DKokkos_ROOT=<Kokkos Install Directory>/lib64/cmake/Kokkos
Maybe:
-DCMAKE_CXX_COMPILER=<Kokkos Install Directory>/bin/
nvcc_wrapper
See https://kokkos.org/kokkos-core-wiki/ProgrammingGuide/
Compiling.html
64 Eijkhout – CMake tutorial – Fall 2023
Data packages
65 Eijkhout – CMake tutorial – Fall 2023
Hdf5
C:
find_package( PkgConfig REQUIRED )
pkg_check_modules( HDF REQUIRED hdf5 )
message( STATUS "Hdf5 includes: ${HDF_INCLUDE_DIRS}" )
target_include_directories(
${PROJECTNAME} PUBLIC
${HDF_INCLUDE_DIRS}
)
66 Eijkhout – CMake tutorial – Fall 2023
Netcdf
C:
find_package( PkgConfig REQUIRED )
pkg_check_modules( NETCDF REQUIRED netcdf )
target_include_directories(
${PROJECTNAME} PUBLIC
${NETCDF_INCLUDE_DIRS} )
target_link_libraries(
${PROJECTNAME} PUBLIC
${NETCDF_LIBRARIES} )
target_link_directories(
${PROJECTNAME} PUBLIC
${NETCDF_LIBRARY_DIRS} )
target_link_libraries(
${PROJECTNAME} PUBLIC netcdf )
67 Eijkhout – CMake tutorial – Fall 2023
Hdf5, Fortran
find_package( PkgConfig REQUIRED )
pkg_check_modules( NETCDFF REQUIRED netcdf-fortran )
pkg_check_modules( NETCDF REQUIRED netcdf )
target_include_directories(
${PROJECTNAME} PUBLIC
${NETCDFF_INCLUDE_DIRS}
)
target_link_libraries(
${PROJECTNAME} PUBLIC
${NETCDFF_LIBRARIES} ${NETCDF_LIBRARIES}
)
target_link_directories(
${PROJECTNAME} PUBLIC
${NETCDFF_LIBRARY_DIRS} ${NETCDF_LIBRARY_DIRS}
)
target_link_libraries(
${PROJECTNAME} PUBLIC netcdf )
68 Eijkhout – CMake tutorial – Fall 2023
HighFive
Third party C++ interface to hdf5
find_package( HighFive REQUIRED )
target_link_libraries( ${PROJECTNAME} HighFive)
69 Eijkhout – CMake tutorial – Fall 2023
More libraries
70 Eijkhout – CMake tutorial – Fall 2023
Package finding
Package dependent:
Sometimes through pkg-config:
find the .pc file
Sometimes through a Find.... module
see CMake documentation
71 Eijkhout – CMake tutorial – Fall 2023
Catch2
find_package( PkgConfig REQUIRED )
pkg_check_modules( CATCH2 REQUIRED catch2-with-main )
target_include_directories(
${PROGRAM_NAME} PUBLIC
${CATCH2_INCLUDE_DIRS}
)
target_link_directories(
${PROGRAM_NAME} PUBLIC
${CATCH2_LIBRARY_DIRS}
)
target_link_libraries(
${PROGRAM_NAME} PUBLIC
${CATCH2_LIBRARIES}
)
72 Eijkhout – CMake tutorial – Fall 2023
Cxxopts
Header-only:
find_package( PkgConfig REQUIRED )
pkg_check_modules( OPTS REQUIRED cxxopts )
target_include_directories(
${PROGRAM_NAME} PUBLIC
${OPTS_INCLUDE_DIRS}
)
73 Eijkhout – CMake tutorial – Fall 2023
Eigen
Header-only:
cmake_minimum_required( VERSION 3.12 )
project( eigentest )
find_package( PkgConfig REQUIRED )
pkg_check_modules( EIGEN REQUIRED eigen3 )
add_executable( eigentest )
target_sources( eigentest PRIVATE eigentest.cxx )
target_include_directories(
myprogram PUBLIC
${EIGEN_INCLUDE_DIRS})
74 Eijkhout – CMake tutorial – Fall 2023
Fmtlib
find_package( PkgConfig REQUIRED )
pkg_check_modules( FMTLIB REQUIRED fmt )
target_include_directories(
${PROGRAM_NAME} PUBLIC ${FMTLIB_INCLUDE_DIRS}
)
target_link_directories(
${PROGRAM_NAME} PUBLIC ${FMTLIB_LIBRARY_DIRS}
)
target_link_libraries(
${PROGRAM_NAME} PUBLIC ${FMTLIB_LIBRARIES}
)
75 Eijkhout – CMake tutorial – Fall 2023
Range-v3
Has its own module:
find_package( range-v3 REQUIRED )
target_link_libraries(
${PROGRAM_NAME} PUBLIC range-v3::range-v3 )
76 Eijkhout – CMake tutorial – Fall 2023