44,39 €
CMake is cross-platform, open-source software for managing the build process in a portable fashion. This book features a collection of recipes and building blocks with tips and techniques for working with CMake, CTest, CPack, and CDash.
CMake Cookbook includes real-world examples in the form of recipes that cover different ways to structure, configure, build, and test small- to large-scale code projects. You will learn to use CMake's command-line tools and master modern CMake practices for configuring, building, and testing binaries and libraries. With this book, you will be able to work with external libraries and structure your own projects in a modular and reusable way. You will be well-equipped to generate native build scripts for Linux, MacOS, and Windows, simplify and refactor projects using CMake, and port projects to CMake.
Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:
Seitenzahl: 580
Veröffentlichungsjahr: 2018
Copyright © 2018 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the authors, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
Commissioning Editor: Smeet ThakkarAcquisition Editor: Noyonika DasContent Development Editor: Francis CarneiroTechnical Editor: Sachin SunilkumarCopy Editor: Safis EditingProject Coordinator: Devanshi DoshiProofreader: Safis EditingIndexer: Pratik ShirodkarGraphics: Jason MonteiroProduction Coordinator: Aparna Bhagat
First published: September 2018
Production reference: 1240918
Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.
ISBN 978-1-78847-071-1
www.packtpub.com
Mapt is an online digital library that gives you full access to over 5,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website mapt.io.
Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals
Improve your learning with Skill Plans built especially for you
Get a free eBook or video every month
Mapt is fully searchable
Copy and paste, print, and bookmark content
Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.PacktPub.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.
At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks.
Radovan Bast works at the High Performance Computing Group at UiT - The Arctic University of Norway in Tromsø and leads the CodeRefinery project. He has a PhD in theoretical chemistry and contributes to a number of quantum chemistry programs as a code developer. He enjoys learning new programming languages and techniques, and teaching programming to students and researchers. He got in touch with CMake in 2008 and has ported a number of research codes and migrated a number of communities to CMake since then.
Roberto Di Remigio is a postdoctoral fellow in theoretical chemistry at UiT - The Arctic University of Norway in Tromsø, Norway and Virginia Tech, USA. He is currently working on stochastic methods and solvation models. He is a developer of the PCMSolver library and the Psi4 open source quantum chemistry program. He contributes or has contributed to the development of popular quantum chemistry codes and libraries: DIRAC, MRCPP, DALTON, LSDALTON, XCFun, and ReSpect. He usually programs in C++ and Fortran.
Holding an engineering degree from ENSEEIHT and a PhD in computer science from UVSQ in France, Eric Noulard has been writing and compiling source code in a variety of languages for 20 years. A user of CMake since 2006, he has also been an active contributor to the project for several years. During his career, Eric has worked for private companies and government agencies. He is now employed by Antidot, a software vendor developing and marketing high-end information retrieval technology and solutions.
Shlomi Fish is an Israeli software developer and writer. He has been contributing to various open source and open culture projects since at least 2000. Among other endeavors, he has initiated some solvers for games, which led to him maintaining the PySol FC suite of solitaire games, adopting fortune-mod, solving over 290 Project Euler problems, and writing several stories, essays, aphorisms, and other documents.
If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.
Title Page
Copyright and Credits
CMake Cookbook
Packt Upsell
Why subscribe?
PacktPub.com
Contributors
About the authors
About the reviewers
Packt is searching for authors like you
Preface
Who this book is for
What this book covers
To get the most out of this book
Download the example code files
Download the color images
Conventions used
Additional reading resources
Get in touch
Reviews
Setting up Your System
Obtaining the code
Docker image
Installing prerequisite software
Getting CMake
Compilers
Build-automation tools
Python
Additional software
BLAS and LAPACK
Message passing interface (MPI)
The Eigen linear algebra template library
The Boost libraries
Cross-compilers
ZeroMQ, pkg-config, UUID, and Doxygen
Conda build and deployment tools
Testing the recipes
Reporting problems and suggesting improvements
From a Simple Executable to Libraries
Introduction
Compiling a single source file into an executable
Getting ready
How to do it
How it works
There is more
Switching generators
Getting ready
How to do it
How it works
See also
Building and linking static and shared libraries
Getting ready
How to do it
How it works
There is more
Controlling compilation with conditionals
How to do it
How it works
Presenting options to the user
How to do it
How it works
There is more
Specifying the compiler
How to do it
How it works
There is more
Switching the build type
How to do it
How it works
There is more
Controlling compiler flags
Getting ready
How to do it
How it works
There is more
Setting the standard for the language
Getting ready
How to do it
How it works
There is more
Using control flow constructs
Getting ready
How to do it
How it works
There is more
Detecting the Environment
Introduction
Discovering the operating system
How to do it
How it works
Dealing with platform-dependent source code
Getting ready
How to do it
How it works
Dealing with compiler-dependent source code
Getting ready
How to do it
How it works
Discovering the host processor architecture
Getting ready
How to do it
How it works
There is more
Discovering the host processor instruction set
Getting ready
How to do it
How it works
There is more
Enabling vectorization for the Eigen library
Getting ready
How to do it
How it works
There is more
Detecting External Libraries and Programs
Introduction
Detecting the Python interpreter
How to do it
How it works
There is more
Detecting the Python library
Getting ready
How to do it
How it works
There is more
See also
Detecting Python modules and packages
Getting ready
How to do it
How it works
Detecting the BLAS and LAPACK math libraries
Getting ready
How to do it
How it works
There is more
Detecting the OpenMP parallel environment
Getting ready
How to do it
How it works
Detecting the MPI parallel environment
Getting ready
How to do it
How it works
Detecting the Eigen library
Getting ready
How to do it
How it works
There is more
Detecting the Boost libraries
Getting ready
How to do it
How it works
Detecting external libraries: I. Using pkg-config
Getting ready
How to do it
How it works
Detecting external libraries: II. Writing a find-module
How to do it
How it works
There is more
Creating and Running Tests
Introduction
Creating a simple unit test
Getting ready
How to do it
How it works
There is more
Defining a unit test using the Catch2 library
Getting ready
How to do it
How it works
There is more
See also
Defining a unit test and linking against Google Test
Getting ready
How to do it
How it works
There is more
See also
Defining a unit test and linking against Boost test
Getting ready
How to do it
How it works
There is more
Using dynamic analysis to detect memory defects
Getting ready
How to do it
How it works
There is more
See also
Testing expected failures
Getting ready
How to do it
How it works
There is more
Using timeouts for long tests
Getting ready
How to do it
How it works
Running tests in parallel
Getting ready
How to do it
How it works
There is more
Running a subset of tests
Getting ready
How to do it
How it works
There is more
Using test fixtures
Getting ready
How to do it
How it works
There is more
Configure-time and Build-time Operations
Introduction
Using platform-independent file operations
Getting ready
How to do it
How it works
There is more
Running a custom command at configure time
How to do it
How it works
There is more
Running a custom command at build time: I. Using add_custom_command
Getting ready
How to do it
How it works
Running a custom command at build time: II. Using add_custom_target
Getting ready
How to do it
How it works
Running custom commands for specific targets at build time
Getting ready
How to do it
How it works
Probing compilation and linking
Getting ready
How to do it
How it works
There is more
Probing compiler flags
Getting ready
How to do it
How it works
See also
Probing execution
Getting ready
How to do it
How it works
Fine-tuning configuration and compilation with generator expressions
Getting ready
How to do it
How it works
There is more
See also
Generating Source Code
Introduction
Generating sources at configure time
Getting ready
How to do it
How it works
There is more
Generating source code at configure time using Python
Getting ready
How to do it
How it works
There is more
Generating source code at build time using Python
Getting ready
How to do it
How it works
There is more
Recording the project version information for reproducibility
Getting ready
How to do it
How it works
There is more
Recording the project version from a file
Getting ready
How to do it
How it works
Recording the Git hash at configure time
Getting ready
How to do it
How it works
Recording the Git hash at build time
Getting ready
How to do it
How it works
There is more
Structuring Projects
Introduction
Code reuse with functions and macros
Getting ready
How to do it
How it works
There is more
Splitting CMake sources into modules
Getting ready
How to do it
How it works
There is more
Writing a function to test and set compiler flags
Getting ready
How to do it
How it works
There is more
Defining a function or macro with named arguments
Getting ready
How to do it
How it works
There is more
Redefining functions and macros
Getting ready
How to do it
How it works
Deprecating functions, macros, and variables
Getting ready
How to do it
How it works
Limiting scope with add_subdirectory
Getting ready
How to do it
How it works
There is more
See also
Avoiding global variables using target_sources
Getting ready
How to do it
How it works
There is more
Organizing Fortran projects
Getting ready
How to do it
How it works
There is more
The Superbuild Pattern
Introduction
Using the superbuild pattern
Getting ready
How to do it
How it works
There is more
Managing dependencies with a superbuild: I. The Boost libraries
How to do it
How it works
Managing dependencies with a superbuild: II. The FFTW library
Getting ready
How to do it
How it works
Managing dependencies with a superbuild: III. The Google Test framework
Getting ready
How to do it
How it works
See also
Managing your project as a superbuild
Getting ready
How to do it
How it works
Mixed-language Projects
Introduction
Building Fortran projects that use C/C++ libraries
Getting ready
How to do it
How it works
There is more
Building C/C++ projects that use Fortran libraries
Getting ready
How to do it
How it works
Building C++ and Python projects using Cython
Getting ready
How to do it
How it works
There is more
Building C++ and Python projects using Boost.Python
Getting ready
How to do it
How it works
There is more
Building C++ and Python projects using pybind11
Getting ready
How to do it
How it works
There is more
See also
Mixing C, C++, Fortran, and Python using Python CFFI
Getting ready
How to do it
How it works
There is more
See also
Writing an Installer
Introduction
Installing your project
Getting ready
How to do it
How it works
Installing to standard locations
Target properties and RPATH handling
Installation directives
There is more
Generating export headers
Getting ready
How to do it
How it works
There is more
Exporting your targets
Getting ready
How to do it
How it works
There is more
Installing a superbuild
Getting ready
How to do it
How it works
Packaging Projects
Introduction
Generating source and binary packages
Getting ready
How to do it
How it works
Source archives
Binary archives
Platform-native binary installers
There is more
Distributing a C++/Python project built with CMake/pybind11 via PyPI
Getting ready
How to do it
How it works
There is more
Distributing a C/Fortran/Python project build with CMake/CFFI via PyPI
Getting ready
How to do it
How it works
There is more
Distributing a simple project as Conda package
Getting ready
How to do it
How it works
There is more
Distributing a project with dependencies as Conda package
Getting ready
How to do it
How it works
There is more
Building Documentation
Introduction
Building documentation using Doxygen
Getting ready
How to do it
How it works
Building documentation using Sphinx
Getting ready
How to do it
How it works
Combining Doxygen and Sphinx
Getting ready
How to do it
How it works
Alternative Generators and Cross-compilation
Introduction
Building a CMake project using Visual Studio 2017
Getting ready
How to do it
How it works
See also
Cross-compiling a hello world example
Getting ready
How to do it
How it works
See also
Cross-compiling a Windows binary with OpenMP parallelization
Getting ready
How to do it
How it works
There is more
Testing Dashboards
Introduction
Setting up a CDash dashboard
Deploying tests to the CDash dashboard
Getting ready
How to do it
How it works
There is more
See also
Reporting test coverage to the CDash dashboard
Getting ready
How to do it
How it works
See also
Using the AddressSanitizer and reporting memory defects to CDash
Getting ready
How to do it
How it works
There is more
See also
Using the ThreadSanitizer and reporting data races to CDash
Getting ready
How to do it
How it works
There is more
See also
Porting a Project to CMake
Where to start
Reproducing the porting example
Creating a top-level CMakeLists.txt
How to allow both conventional configuration and configuration with CMake at the same time
Capturing a record of what the traditional build does
Debugging the migration
Implementing options
Start with the executable and very few targets, later localize scope
Generating files and writing platform checks
How to structure files
Configuring preprocessor definitions based on the system environment
Configuring files with paths and compiler flags
Executing shell scripts at configure time
Detecting required dependencies and linking
Reproducing compiler flags
Defining compiler flags
Scope of compiler flags
Porting tests
Getting started
Implementing a multi-step test
Recommendation for tests
Porting install targets
Further steps
Summary and common pitfalls when converting projects to CMake
Summary of code changes
Common pitfalls
Other Books You May Enjoy
Leave a review - let other readers know what you think
Computer software is present in almost every aspect of our daily lives: it triggers our alarm clocks, fuels our communication, banking, weather forecasts, bus schedules, calendars, meetings, travel, photo albums, television, music streaming, social media, dinner and movie reservations, from dawn till dusk.
The software that surrounds us contains many layers: apps are built on top of frameworks, frameworks on top of libraries, libraries use smaller libraries and executables, all the way down to smaller and smaller software components. Libraries and executables in turn need to be built from source code. Often we only see the outermost layer, but all these layers need to be carefully organized and built. This book is about how to build libraries and executables from sources using CMake.
CMake and its siblings, CTest, CPack, and CDash, have emerged as the leading toolset for building software from sources, surpassing in usage and popularity many other similar tools, such as the venerated GNU Autotools and the more recent, Python-based, SCons build system.
The history of the CMake project started in 1999, when Kitware, the company behind its development, was commissioned to design a new set of tools to simplify the day-to-day software work of researchers. The goal was clear: provide a set of tools that would make it easier to configure, build, test, and deploy the same project across different platforms. A fascinating account of the ensuing design choices in the CMake project can be found at https://www.aosabook.org/en/cmake.html.
CMake is abuild-system generator, offering a powerful domain-specific language (DSL) to describe what the build system should achieve. In our opinion, this is one of the main strengths of CMake, because it allows the generation ofplatform-native build systemswith the same set of CMake scripts. The CMake software toolset gives developers full control over the whole life cycle of a given project:
CMake
lets you describe how your project, whether building an executable, libraries, or both, has to be configured, built, and installed on all major hardware and operating systems.
CTest
allows you to define tests, test suites, and set how they should be executed.
CPack
offers a DSL for all your packaging needs, whether your project should be bundled and distributed in source code or platform-native binary form.
CDash
will be useful for reporting the results of tests for your project to an online dashboard.
An old adage goes that the deeper you dig, the more stones you will find. For the preparation of this book we have carefully been digging deeper through many software layers, with CMake being our quarry. The number of stones and artifacts that we have hit when building various software components and libraries on various platforms, each with their own quirks, has felt disheartening at times. But we believe we have cleared the ground of many stones and we are happy to share our findings and recipes with you, our readers. There will always be stones left but each stone will bring new insight and we encourage you to share this insight with the community.
Writing software that can run natively, reliably, and efficiently on many different platforms is of paramount importance for all sectors of industry and society. Software build systems take center stage in this task. They are a crucial part in the management of the software development life cycle: from incubation and prototype development to testing and all the way till packaging, deployment, and distribution. CMake is designed to help you manage these operations: if you are a software developer who wishes to manage the build system using CMake or who would like to be able to understand and modify CMake code written by others, this book is for you.
We have written this book as a progressive sequence of tasks and recipes. At each point, we introduce enough information about CMake to show how to achieve our goals, without overwhelming you with details. By the end of the book, you will be able to tackle increasingly complex operations and leverage the contents of the recipes in your own real-world projects with confidence.
We will cover these topics:
Configure, build, test, and install code projects using CMake
Detect operating systems, processors, libraries, files, and programs for conditional compilation
Increase the portability of your code
Refactor a large code base into modules with the help of CMake
Build multi-language projects
Know where and how to tweak CMake configuration files written by somebody else
Package projects for distribution
Port projects to CMake
The workflow of a project managed by CMake happens in a number of stages, which we refer to as times. These can be summarized neatly in the following figure:
CMake time
or
configure time
. This is when CMake is running. In this phase CMake will process the
CMakeLists.txt
files in your project and configure it.
Generation time
. Upon successful configuration, CMake will generate the scripts needed by the native build tools to perform subsequent steps in the project.
Build time
. This is when the native build tools are invoked on the platform- and tool-native build scripts previously generated by CMake. At this point, the compiler will be invoked and the targets (executables and libraries) will be built in a specific build directory. Note the recursive CMake-time arrow: this can seem baffling, but it is a mechanism we will use many times throughout the book to achieve a truly platform-independent build.
CTest time
or
test time
. This is when we run the test suite of the project to check whether the targets perform as intended.
CDash time
or
report time
. This is when the results of testing the project are uploaded to a dashboard to be shared with other developers.
Install time
. This is when the project's targets, source files, executables, and libraries are installed from the build directory to an install location.
CPack time
or
packaging time
. This is when we package our project for distribution, either as source code or binary.
Package install time
. This is when the newly minted package is installed system-wide.
This book is organized as follows:
Chapter 1, From a Simple Executable to Libraries, shows how to get started configuring and building simple executables and libraries with CMake.
Chapter 2, Detecting the Environment, explains how to interact with the operating system and processor architecture using simple CMake commands.
Chapter 3, Detecting External Libraries and Programs, shows how CMake can simplify the detection of dependencies for your project.
Chapter 4, Creating and Running Tests, explains how to harness the power of CMake and CTest to define and run tests.
Chapter 5, Configure-time and Build-time Operations, shows how to perform custom operations at different stages of the build process with cross-platform CMake commands.
Chapter 6, Generating Source Code, discusses CMake commands to automatically generate source code.
Chapter 7, Structuring Projects, shows powerful CMake syntax for organizing your projects to make them more maintainable.
Chapter 8, The Superbuild Pattern, explains the powerful CMake superbuild pattern for managing critical project dependencies with control over side effects.
Chapter 9, Mixed-language Projects, shows how to build projects mixing different programming languages with the help of CMake.
Chapter 10, Writing an Installer, takes care of the installation of projects with the cross-platform power of CMake.
Chapter 11, Packaging Projects, shows how to use CPack to produce source and platform-native source archives and how to build Python and Conda packages for distribution.
Chapter 12, Building Documentation, shows how to use CMake to also build the documentation for your code.
Chapter 13, Alternative Generators and Cross-compilation, shows how to use CMake to cross-compile projects between platforms.
Chapter 14, Testing Dashboards, shows how to report the results of tests to an online dashboard.
Chapter 15, Porting a Project to CMake, shows best practices, tips, and tricks that will help you port a project to a CMake-based build system.
This is a book written by programmers, for programmers. We have assumed basic knowledge and familiarity with the following:
The command line for your favorite operating system
Native tools for building software on your favorite operating system
The compiled languages C++, C, or Fortran, and the corresponding compilers on your favorite operating system
The Python programming language
You can download the example code examples for this book from https://github.com/dev-cafe/cmake-cookbook. For more details please see the Setting up Your Systemsection.
We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here:http://www.packtpub.com/sites/default/files/downloads/CMakeCookbook_ColorImages.pdf.
There are a number of text conventions used throughout this book.
CodeInText: Indicates code commands in text, folder names, filenames, module names, and target names.
A block of code is set as follows:
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)project(recipe-01 LANGUAGES CXX)add_executable(hello-world hello-world.cpp)
Any command-line input is written in bold and contains a $ prompt in front of the command to type:
$ mkdir -p build
$ cd build$ cmake ..
To distinguish command-line input and output, we keep output non-bold:
$ ./hello-world
Hello World!
The documentation for CMake available online is comprehensive and we will refer to it throughout the book: https://cmake.org/documentation/
In preparing this book we have been inspired also by other resources:
The presentation by Daniel Pfeifer, available on GitHub:
https://github.com/boostcon/cppnow_presentations_2017/blob/master/05-19-2017_friday/effective_cmake__daniel_pfeifer__cppnow_05-19-2017.pdf
The CMake tutorial by Eric Noulard, available on GitHub:
https://github.com/TheErk/CMake-tutorial
The CMake-related blog posts by Craig Scott:
https://crascit.com/tag/cmake/
We can also recommend to browse the curated list of CMake resources, scripts, modules, and examples collected by Viktor Kirilov: https://github.com/onqtam/awesome-cmake.
It is also worth noting that our book is not the only one out there covering CMake:
Mastering CMake
by Ken Martin and Bill Hoffman, 2015, Kitware Inc.
Professional CMake
by Craig Scott:
https://crascit.com/professional-cmake/
Feedback from our readers is always welcome.
Source code improvements and issues: Please direct pull requests towards https://github.com/dev-cafe/cmake-cookbook and report specific issues with recipes viahttps://github.com/dev-cafe/cmake-cookbook/issues.
General feedback: [email protected] mention the book title in the subject of your message. If you have questions about any aspect of this book, please email us [email protected].
Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/submit-errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.
Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.
If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.
Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!
For more information about Packt, please visit packtpub.com.
Before diving into CMake and the recipes in this book, you will need to set your system up to successfully run all of the examples. In this section, we will discuss the following topics:
How to obtain the code for the recipes
How to install all of the tools required to run the code samples on GNU/Linux, macOS, and Windows
How the automated testing for the repository works
How to report problems with the recipes and suggest improvements
We have strived to make our discussions of the topics in this book as accessible to novices as possible. However, this book does not start from absolute scratch. We assume that you have basic knowledge of the native tools for building software that are available on your platform of choice. It is also helpful (but not required) to have basic experience with version control using Git, to interact with the repository holding the recipe sources.
The source code for the recipes in this book is available on GitHub, at https://github.com/dev-cafe/cmake-cookbook. The code is licensed under the standard open source MIT license: this is a permissive software license, and you can reuse and remix the code in whatever way you see fit, as long as the original copyright and license notice are included in any copies of the software/source. The full text of the license is available at https://opensource.org/licenses/MIT.
In order to test the recipes by yourself, you will need a working installation of Git, obtained as follows:
All major GNU/Linux distributions offer Git prepackaged,
via
their package managers. If that is not your case, a binary distribution can be downloaded from the Git project website at
https://git-scm.com
.
On macOS, it is possible to use Homebrew or MacPorts to install Git.
On Windows, you can download the Git executable from the Git project website at
https://git-scm.com
.
Alternatively, you can access the examples with the GitHub desktop client at https://desktop.github.com.
Yet another alternative is to download and extract the ZIP file from https://github.com/dev-cafe/cmake-cookbook.
Once you have Git installed, you can clone the repository to your local machine, as follows:
$ git clone https://github.com/dev-cafe/cmake-cookbook.git
This will create a folder named cmake-cookbook. The book and the repository are organized in chapters and recipes. The numbering of chapters and the order of recipes in the repository reflect the order in the text. Each recipe is further organized into example folders. Some of the recipes have more than one example, usually when similar CMake concepts are illustrated in different programming languages.
The recipes are tested on GNU/Linux, macOS, and Windows, using state-of-the-art continuous integration services. We will discuss the testing set up shortly.
We have tagged the precise versions that correspond to the examples printed in this book with the tag v1.0. For maximum overlap with the book text, you can fetch this particular version as follows:
$ git clone --single-branch -b v1.0 https://github.com/dev-cafe/cmake-cookbook.git
We expect to receive bug fixes and the GitHub repository to evolve. To get the latest updates, you may prefer to follow the master branch of the repository, instead.
You will likely find that the easiest approach to testing the book's recipes in a software environment (which contains all of the dependencies preinstalled) is to use a Docker image that we have set up, based on Ubuntu 18.04. You can install Docker on your favorite operating system, following the official documentation athttps://docs.docker.com.
Once Docker is installed, you can run our image and test the recipes with the full software environment in place, as follows:
$ docker run -it devcafe/cmake-cookbook_ubuntu-18.04
$
git clone https://github.com/dev-cafe/cmake-cookbook.git
$ cd cmake-cookbook
$ pipenv install --three
$ pipenv run python testing/collect_tests.py 'chapter-*/recipe-*'
An alternative to running the book recipes in a container is to install the dependencies directly on the host operating system. For this, we have assembled a minimal toolstack that can be used as a basic starting point for all of our recipes. You will have to install the following:
CMake
Language-specific tools, that is, the compilers
Build automation tools
Python
We will also detail how to install the additional dependencies required by some of the recipes.
CMake 3.5 is the minimum required version of CMake for this book. Only a few, specific recipes and examples that demonstrate useful features that were introduced after version 3.5 will require a more recent version of CMake. The introduction to every recipe features an info box, pointing out where the code is available, which examples are given, and the minimum version of CMake required. The info boxes will look like the following box:
Some, if not most, of the recipes will still be valid with older versions of CMake. However, we have made no attempts to test this assumption, since we consider CMake 3.5 to be the default on most systems and distributions. We also consider upgrading to later versions of CMake to be a straightforward step.
CMake can be installed in a number of different ways. Downloading and extracting the binary distribution maintained by Kitware will work across all platforms. The download page is at https://cmake.org/download/.
Most GNU/Linux distributions have CMake available in their package managers. However, on some distributions, the packaged version can be rather old, so downloading the binary maintained by Kitware is still the preferred option. The following commands will download and install CMake 3.5.2 under $HOME/Deps/cmake (adjust this path to your preference), from the version packaged by CMake:
$ cmake_version="3.5.2"
$ target_path=$HOME/Deps/cmake/${cmake_version}
$ cmake_url="https://cmake.org/files/v${cmake_version%.*}/cmake-${cmake_version}-Linux-x86_64.tar.gz"
$ mkdir -p "${target_path}"
$ curl -Ls "${cmake_url}" | tar -xz -C "${target_path}" --strip-components=1
$ export PATH=$HOME/Deps/cmake/${cmake_version}/bin${PATH:+:$PATH}$ cmake --version
Homebrew for macOS reliably ships the latest version of CMake:
$ brew upgrade cmake
On Windows, you can use Visual Studio 2017, which provides CMake support. The installation of Visual Studio 2017 is documented in Chapter 13, Alternative Generators and Cross-compilation, Recipe 1, Building a CMake project using Visual Studio 2017.
Alternatively, you can download the MSYS2 installer from https://www.msys2.org , follow the instructions given therein to update the list of packages, and then install CMake using the package manager, pacman. The following code assumes that we are building the 64-bit version:
$ pacman -S mingw64/mingw-w64-x86_64-cmake
For the 32-bit version, use the following (though we will only refer to 64-bit versions in future, for the sake of brevity):
$ pacman -S mingw64/mingw-w64-i686-cmake
Another nice feature of MSYS2 is that it provides a terminal on Windows that feels and behaves like a terminal on a Unix-like operating system, providing a useful development environment.
We will need compilers for C++, C, and Fortran. These should be fairly recent, as we require support for recent language standards in most of the recipes. CMake offers very good support for many compilers, from both commercial and non-commercial vendors. To keep the recipes consistently cross-platform and as operating system independent as possible, we have worked with open source compilers:
On GNU/Linux, the GNU Compiler Collection (GCC) is the obvious choice. It is free and available for all distributions. For example, on Ubuntu, you can install the compilers as follows:
$ sudo apt-get install g++ gcc gfortran
Clang, in the LLVM family, is also a good choice for C++ and C:
$ sudo apt-get install clang clang++ gfortran
On macOS, the LLVM compilers shipped with XCode will work for C++ and C. We have used the Fortran compiler from GCC in our macOS testing. This has to be installed separately, using the package manager. For example, the command for Homebrew is as follows:
$ brew install gcc
On Windows, you can use Visual Studio for the C++ and C recipes. Alternatively, you can use the MSYS2 installer and install the entire toolchain, including a C++, C, and Fortran compiler, with the following single command in an MSYS2 environment (for the 64-bit version):
$ pacman -S mingw64/mingw-w64-x86_64-toolchain
These build-automation tools will provide the infrastructure for building and linking the projects presented in the recipes. What you will end up installing and using strongly depends on your operating system and your taste:
On GNU/Linux, GNU Make will most likely be installed automatically, when installing the compilers.
On macOS, XCode will provide GNU Make.
On Windows, Visual Studio will provide you with the complete infrastructure. In the MSYS2 environment, GNU Make is installed as a part of the
mingw64/mingw-w64-x86_64-toolchain
package, which we installed previously.
For maximum portability, we have made the recipes as agnostic about these system-dependent details as possible. A clear advantage of this approach is that configuring, building, and linking are native to each platform and each set of compilers.
The Ninja program is a different build-automation tool that works on GNU/Linux, macOS, and Windows. Ninja is a new build tool, with a focus on speed, especially for incremental rebuilds. Prepackaged binaries for GNU/Linux, macOS, and Windows can be found on the project's GitHub repository at https://github.com/ninja-build/ninja/releases.
Using CMake and Ninja with Fortran projects requires some care. CMake 3.7.2 or later is required, along with the version of Ninja maintained by Kitware, available at https://github.com/Kitware/ninja/releases.
On GNU/Linux, you can install Ninja with the following series of commands:
$ mkdir -p ninja
$ ninja_url="https://github.com/Kitware/ninja/releases/download/v1.8.2.g3bbbe.kitware.dyndep-1.jobserver-1/ninja-1.8.2.g3bbbe.kitware.dyndep-1.jobserver-1_x86_64-linux-gnu.tar.gz"
$ curl -Ls ${ninja_url} | tar -xz -C ninja --strip-components=1
$ export PATH=$HOME/Deps/ninja${PATH:+:$PATH}
On Windows, using the MSYS2 environment (assuming the 64-bit version), executing the command:
$ pacman -S mingw64/mingw-w64-x86_64-ninja
Some recipes will require additional software, which will be covered in the following sections.
Most Linux distributions provide packages for BLAS and LAPACK. For example, on Ubuntu 14.04 LTS, you can run the following:
$ sudo apt-get install libatlas-dev liblapack-dev liblapacke-dev
On macOS, the Accelerate libraries, shipped with XCode, are enough for our purposes.
On Windows, using the MSYS2 environment, these libraries can be installed as follows (assuming the 64-bit version):
$ pacman -S mingw64/mingw-w64-x86_64-openblas
Alternatively, you can download the reference implementation of BLAS and LAPACK from GitHub (https://github.com/Reference-LAPACK/lapack) and compile the libraries from sources. Commercial vendors might offer packages for their own implementations of the BLAS and LAPACK APIs, available as installers for your platform.
There are many commercial and non-commercial implementations of MPI. For our introductory purposes, it is enough to install any of the freely available non-commercial implementations. On Ubuntu 14.04 LTS, we recommend OpenMPI. It can be installed with the following command:
$ sudo apt-get install openmpi-bin libopenmpi-dev
For macOS, Homebrew distributes MPICH:
$ brew install mpich
It is also possible to compile OpenMPI from the sources publicly available at https://www.open-mpi.org/software/.
For Windows, the Microsoft MPI implementation can be installed via https://msdn.microsoft.com/en-us/library/bb524831(v=vs.85).aspx.
Some recipes will need the Eigen linear algebra template library, version 3.3 or later. If your package manager does not provide Eigen, you can install it from the online source archive (http://eigen.tuxfamily.org). For example, on GNU/Linux and macOS, you can install Eigen to the directory $HOME/Deps/eigen, as follows:
$ eigen_version="3.3.4"
$ mkdir -p eigen
$ curl -Ls http://bitbucket.org/eigen/eigen/get/${eigen_version}.tar.gz | tar -xz -C eigen --strip-components=1
$ cd eigen
$ cmake -H. -Bbuild_eigen -DCMAKE_INSTALL_PREFIX="$HOME/Deps/eigen" &> /dev/null
$ cmake --build build_eigen -- install &> /dev/null
Boost packages are available for every operating system; most Linux distributions have packages available through their package managers. On Ubuntu 14.04 LTS, for instance, the Boost Filesystem, Boost Python, and Boost Test libraries can be installed with the following command:
$ sudo apt-get install libboost-filesystem-dev libboost-python-dev libboost-test-dev
For macOS, both MacPorts and Homebrew provide packages for recent versions of Boost. Our testing setup on macOS installs Boost as follows:
$ brew cask uninstall --force oclint
$ brew uninstall --force --ignore-dependencies boost$ brew install boost $ brew install boost-python3
Prebuilt binary distributions for Windows are also available for download from the Boost website at http://www.boost.org. Alternatively, you can download the sources from https://www.boost.org and compile the libraries yourself.
On Debian/Ubuntu-like systems, cross-compilers can be installed with the following command:
$ sudo apt-get install gcc-mingw-w64 g++-mingw-w64 gfortran-mingw-w64
On macOS, using Brew, the cross-compilers can be installed as follows:
$ brew install mingw-w64
Other package managers offer corresponding packages.
An alternative to using packaged cross-compilers is to build them from sources, using the M cross environment (https://mxe.cc).
On Ubuntu 14.04 LTS, these packages can be installed as follows:
$ sudo apt-get install pkg-config libzmq3-dev doxygen graphviz-dev uuid-dev
On macOS, we recommend installing with Brew:
$ brew install ossp-uuid
pkg-config
zeromq doxygen
The pkg-config program and UUID library are only available on Unix-like systems.
On Windows, using the MSYS2 environment, these dependencies can be installed as follows (assuming the 64-bit version):
$ pacman -S mingw64/mingw-w64-x86_64-zeromq
$ pacman -S mingw64/mingw-w64-x86_64-pkg-config
$ pacman -S mingw64/mingw-w64-x86_64-doxygen
$ pacman -S mingw64/mingw-w64-x86_64-graphviz
The recipes are tested on state-of-the-art continuous integration (CI) services: Travis (https://travis-ci.org) for GNU/Linux and macOS, Appveyor (https://www.appveyor.com) for Windows, and CircleCI (https://circleci.com) for additional GNU/Linux testing with commercial compilers. The configuration files for the CI services can be found in the repository (https://github.com/dev-cafe/cmake-cookbook/): .travis.yml for Travis, .appveyor.yml for Appveyor, and .circleci/config.yml for CircleCI. Additional installation scripts for Travis and Appveyor can be found in the folder testing/dependencies.
The testing machinery is a set of Python scripts that are also contained in the testing folder. The script collect_tests.py will run tests and report their statuses. Recipes can be tested in isolation, or in batches; collect_tests.py accepts a regular expression as command-line input, for example:
$ pipenv run python testing/collect_tests.py 'chapter-0[1,7]/recipe-0[1,2,5]'
This command will run tests for Recipes 1, 2, and 5, in Chapters 1 and 7. A sample of the output looks as follows:
To get more verbose output, set VERBOSE_OUTPUT=ON:
$ env VERBOSE_OUTPUT=ON pipenv run python testing/collect_tests.py 'chapter-*/recipe-*'
Please report issues at https://github.com/dev-cafe/cmake-cookbook/issues.
To contribute changes,we recommend forking the repository https://github.com/dev-cafe/cmake-cookbook and submitting changes using pull requests, following https://help.github.com/articles/creating-a-pull-request-from-a-fork/.
For non-trivial changes, we recommend to first describe and discuss the proposed change by opening an issue on https://github.com/dev-cafe/cmake-cookbook/issues before sending a pull request.
In this chapter, we will cover the following recipes:
Compiling a single source file into an executable
Switching generators
Building and linking static and shared libraries
Controlling compilation with conditionals
Presenting options to the user
Specifying the compiler
Switching the build type
Controlling compiler flags
Setting the standard for the language
Using control flow constructs
The recipes in this chapter will walk you through fairly basic tasks needed to build your code: compiling an executable, compiling a library, performing build actions based on user input, and so forth. CMake is a build system generator particularly suited to being platform- and compiler-independent. We have striven to show this aspect in this chapter. Unless stated otherwise, all recipes are independent of the operating system; they can be run without modifications on GNU/Linux, macOS, and Windows.
The recipes in this book are mainly designed for C++ projects and demonstrated using C++ examples, but CMake can be used for projects in other languages, including C and Fortran. For any given recipe and whenever it makes sense, we have tried to include examples in C++, C, and Fortran. In this way, you will be able to choose the recipe in your favorite flavor. Some recipes are tailor-made to highlight challenges to overcome when a specific language is chosen.
In this recipe, we will demonstrate how to run CMake to configure and build a simple project. The project consists of a single source file for a single executable. We will discuss the project in C++, but examples for C and Fortran are available in the GitHub repository.
We wish to compile the following source code into a single executable:
#include <cstdlib>#include <iostream>#include <string>std::string say_hello() { return std::string("Hello, CMake world!"); }int main() { std::cout << say_hello() << std::endl; return EXIT_SUCCESS;}
Alongside the source file, we need to provide CMake with a description of the operations to perform to configure the project for the build tools. The description is done in the CMake language, whose comprehensive documentation can be found online at https://cmake.org/cmake/help/latest/. We will place the CMake instructions into a file called CMakeLists.txt.
In detail, these are the steps to follow:
Open a text file with your favorite editor. The name of this file will be
CMakeLists.txt
.
The first line sets a minimum required version for CMake. A fatal error will be issued if a version of CMake lower than that is used:
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
The second line declares the name of the project (recipe-01) and the supported language (
CXX
stands for C++):
project(recipe-01 LANGUAGES CXX)
We instruct CMake to create a new
target
: the executable
hello-world
. This executable is generated by compiling and linking the source file
hello-world.cpp
. CMake will use default settings for the compiler and build automation tools selected:
add_executable(hello-world hello-world.cpp)
Save the file in the same directory as the source file
hello-world.cpp
. Remember that it can only be named
CMakeLists.txt
.
$ mkdir -p build
$ cd build
$ cmake ..
-- The CXX compiler identification is GNU 8.1.0-- Check for working CXX compiler: /usr/bin/c++-- Check for working CXX compiler: /usr/bin/c++ -- works-- Detecting CXX compiler ABI info-- Detecting CXX compiler ABI info - done-- Detecting CXX compile features-- Detecting CXX compile features - done-- Configuring done-- Generating done-- Build files have been written to: /home/user/cmake-cookbook/chapter-01/recipe-01/cxx-example/build
If everything went well, the configuration for the project has been generated in the build directory. We can now compile the executable:
$ cmake --build .
Scanning dependencies of target hello-world[ 50%] Building CXX object CMakeFiles/hello-world.dir/hello-world.cpp.o[100%] Linking CXX executable hello-world[100%] Built target hello-world
In this recipe, we have used a simple CMakeLists.txt to build a "Hello world" executable:
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)project(recipe-01 LANGUAGES CXX)add_executable(hello-world hello-world.cpp)
To configure the project and generate its build system, we have to run CMake through its command-line interface (CLI). The CMake CLI offers a number of switches, cmake --help will output to screen the full help menu listing all of the available switches. We will learn more about them throughout the book. As you will notice from the output of cmake --help, most of them will let you access the CMake manual. The typical series of commands issued for generating the build system is the following:
$ mkdir -p build
$ cd build
$ cmake ..
Here, we created a directory, build, where the build system will be generated, we entered the build directory, and invoked CMake by pointing it to the location of CMakeLists.txt (in this case located in the parent directory). It is possible to use the following invocation to achieve the same effect:
This invocation is cross-platform and introduces the -H and -B CLI switches. With -H. we are instructing CMake to search for the root CMakeLists.txt file in the current directory. -Bbuild tells CMake to generate all of its files in a directory called build.
Running the cmake command outputs a series of status messages to inform you of the configuration:
$ cmake ..
-- The CXX compiler identification is GNU 8.1.0-- Check for working CXX compiler: /usr/bin/c++-- Check for working CXX compiler: /usr/bin/c++ -- works-- Detecting CXX compiler ABI info-- Detecting CXX compiler ABI info - done-- Detecting CXX compile features-- Detecting CXX compile features - done-- Configuring done-- Generating done-- Build files have been written to: /home/user/cmake-cookbook/chapter-01/recipe-01/cxx-example/build
CMake is a build system generator. You describe what type of operations the build system, such as Unix Makefiles, Ninja, Visual Studio, and so on, will have to run to get your code compiled. In turn, CMake generates the corresponding instructions for the chosen build system. By default, on GNU/Linux and macOS systems, CMake employs the Unix Makefiles generator. On Windows, Visual Studio is the default generator. We will take a closer look at generators in the next recipe and also revisit generators in Chapter 13, Alternative Generators and Cross-compilation. On GNU/Linux, CMake will by default generate Unix Makefiles to build the project:
Makefile
: The set of instructions that
make
will run to build the project.
CMakeFiles
: Directory which contains temporary files, used by CMake for detecting the operating system, compiler, and so on. In addition, depending on the chosen
generator,
it also contains project-specific files.
cmake_install.cmake
: A CMake script handling install rules, which is used at install time.
CMakeCache.txt
:
The CMake cache, as the filename suggests. This file is used by CMake when re-running the configuration.
To build the example project, we ran this command:
$ cmake --build .
This command is a generic, cross-platform wrapper to the native build command for the chosen generator, make in this case. We should not forget to test our example executable:
$ ./hello-world
Hello, CMake world!
Finally, we should point out that CMake does not enforce a specific name or a specific location for the build directory. We could have placed it completely outside the project path. This would have worked equally well:
$ mkdir -p /tmp/someplace
$ cd /tmp/someplace
$ cmake /path/to/source
$ cmake --build .
