CMake Cookbook - Radovan Bast - E-Book

CMake Cookbook E-Book

Radovan Bast

0,0
44,39 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

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:

EPUB
MOBI

Seitenzahl: 580

Veröffentlichungsjahr: 2018

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



CMake Cookbook
Building, testing, and packaging modular software with modern CMake
Radovan Bast Roberto Di Remigio
BIRMINGHAM - MUMBAI

CMake Cookbook

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.

Why subscribe?

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

PacktPub.com

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.

Contributors

About the authors

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.

We would like to acknowledge the very valuable comments and suggestions from the reviewers, Eric Noulard and Shlomi Fish. In particular, Eric's feedback and input has improved the quality of the chapters significantly. We also thank Lori A. Burns for her comments and suggestions for chapters 8 through 11. Special thanks to the Tromsø Public Library for providing a wonderful space for writing and thinking. We are very grateful for the testing infrastructure and support provided by Travis CI, GmbH, Appveyor Systems Inc., and Circle Internet Services, Inc. – it is thanks to these infrastructure services that we can be confident that the examples work across the major operating systems.

About the reviewers

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.

Packt is searching for authors like you

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.

Table of Contents

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

Preface

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.

Search interest over time for three popular build systems: CMake, Automake, and SCons. The interest is measured using the number of searches for the relevant terms over time. The figure was obtained using data provided by Google Trends.

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.

Who this book is for

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.

What this book covers

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.

To get the most out of this book

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

Download the example code files

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.

Download the color images

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.

Conventions used

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!

Important notes appear like this.
Tips and tricks appear like this.

Additional reading resources

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/

Get in touch

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.

Reviews

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.

Setting up Your System

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.

Obtaining the code

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.

Docker image

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-*'

Installing prerequisite software

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.

Getting CMake

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:

The code for this recipe is available at https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-03/recipe-10, and includes a C example. The recipe is valid with CMake version 3.5 (and higher) and has been tested on GNU/Linux, macOS, and Windows.

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.

Compilers

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

Build-automation tools

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

We recommend reading the essay at http://www.aosabook.org/en/posa/ninja.html for an enlightening discussion of Ninja's history and design choices.

Additional software

Some recipes will require additional software, which will be covered in the following sections.

BLAS and LAPACK

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.

Message passing interface (MPI)

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.

The Eigen linear algebra template library

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

The Boost libraries

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.

Cross-compilers

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).

ZeroMQ, pkg-config, UUID, and Doxygen

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

Testing the recipes

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.

We test the recipes with CMake 3.5.2 and CMake 3.12.1 on the Travis GNU/Linux infrastructure. CMake 3.12.1 is used on the Travis macOS infrastructure. On Appveyor, testing uses CMake 3.11.3. On Circle, CMake 3.12.1 is used.

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-*'

Reporting problems and suggesting improvements

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.

From a Simple Executable to Libraries

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

Introduction

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.

Compiling a single source file into an executable

The code for this recipe is available at https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-01/recipe-01 and has C++, C, and Fortran examples. The recipe is valid with CMake version 3.5 (and higher) and has been tested on GNU/Linux, macOS, and Windows.

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.

Getting ready

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;}

How to do it

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.

The name of the file is case sensitive; it has to be called CMakeLists.txt for CMake to be able to parse it.

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

.

We are now ready to configure the project by creating and stepping into a build directory:

$ 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

How it works

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)

The CMake language is caseinsensitive, but the arguments are casesensitive.
In CMake, C++ is the default programming language. However, we suggest to always explicitly state the project’s language in theproject command using the LANGUAGES option.

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:

$ cmake -H. -Bbuild

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.

Note that the cmake -H. -Bbuild invocation of CMake is still undergoing standardization: https://cmake.org/pipermail/cmake-developers/2018-January/030520.html. This is the reason why we will instead use the traditional approach in this book (create a build directory, step into it, and configure the project by pointing CMake to the location of CMakeLists.txt).

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

Running cmake . in the same directory as CMakeLists.txt would in principle be enough to configure a project. However, CMake would then write all generated files into the root of the project. This would be an in-source build and is generally undesirable, as it mixes the source and the build tree of the project. The out-of-source build we have demonstrated is the preferred practice.

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 .