CMake package guidelines - ArchWiki

Jump to content

# CMake package guidelines

From ArchWiki

This document covers standards and guidelines on writing PKGBUILDs for software that uses cmake.

From the CMake web page:

  CMake is an open-source, cross-platform family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice.

## Typical usage

The typical usage consists of running the `cmake` command and after that execute the building command. The `cmake` command usually sets some parameters, checks for the needed dependencies and creates the build files, letting the software ready to be built by other tools like `make` and `ninja`.

## CMake undesired behaviors

Due to its own internal characteristics for generating the build files, sometimes CMake can behave in undesired ways. Being such, some steps should be noted when writing PKGBUILDs for CMake-based software.

### CMake can automatically override the default compiler optimization flag

It is very common to see people running CMake with the `-DCMAKE_BUILD_TYPE=Release` option. Some upstream projects even inadvertently include this option in their building instructions, but this produces an undesired behavior.

Each build type causes CMake to automatically append a set of flags to `CFLAGS` and `CXXFLAGS`. When using the common `Release` build type, it automatically appends the `-O3`[1] compiler optimization flag, and this overrides the default Arch Linux flag which currently is `-O2` (defined in the makepkg configuration file). This is undesired, as it deviates from the Arch Linux targeted optimization level.

#### Notes about -O3

Using `-O3` does not guarantee that the software will perform better, and sometimes it can even slow down the program. It can also break software in some situations. There is a good reason why the Arch Linux developers choose `-O2` as the target optimization level and we should stick with it. Unless you know exactly what you are doing, or if upstream explicitly tells or implies that `-O3` is needed, we should avoid using it in our packages.

#### Fixing the automatic optimization flag override

Fixing this in a 100% guaranteed way is not a simple question due to CMake flexibility. Please note that there is no standard solution that can be applied to all cases. This section will discuss possible solutions and some points that should be observed.

The default CMake build type is `None` and it does not append any flags to `CFLAGS` and `CXXFLAGS` by default, so simply omitting the usage of the `CMAKE_BUILD_TYPE` option can work as it will default to `None`. But note that omitting this option is not guaranteed to fix the problem, as many software projects automatically set the build type to `Release` (or other type) in the CMake files if `CMAKE_BUILD_TYPE` is not set at command line. Also be aware of possible references to source files in the resulting package and the corresponding makepkg's `WARNING: Package contains reference to $srcdir` caused by a missing `NDEBUG` definition in the `None` build type.

Since the default `None` build type does not append any flags to `CFLAGS` and `CXXFLAGS` by default, using the `-DCMAKE_BUILD_TYPE=None` option can also work. Generally speaking, using the `-DCMAKE_BUILD_TYPE=None` option is better than omitting the usage of `CMAKE_BUILD_TYPE`. It will cover the case when upstream automatically sets the build type to `Release` when `CMAKE_BUILD_TYPE` is omitted, it will not append any flags by default and it is uncommon to see software setting undesired flags for the `None` build type.

But unfortunately, things are not that simple like only using `-DCMAKE_BUILD_TYPE=None` to fix this. When using the `None` build type to fix the `-O3` issue, one may fall into another problem. It is a common practice for many software projects to define some required compiler flags for the `Release` build type in the CMake files (for example, like setting the `CMAKE_C_FLAGS_RELEASE` and `CMAKE_CXX_FLAGS_RELEASE` CMake variables). Such software may break or misbehave when compiled without these upstream defined flags if you use the `None` build type. In order to determine if you are missing some flags you will need to look at the CMake files, or you can compare the output of `make VERBOSE=1` for the `None` and `Release` build types. What to do if the `None` build type causes some upstream defined flags to be missed? In this case you may be at the middle of two problematic situations, because if you use the `Release` build type you may be using the undesired `-O3` flag, and if you use the `None` build type you will miss some required upstream defined flags. There is no standard way of solving this situation and it should be analyzed on a case-by-case basis. If upstream defines `-O2` for the `Release` build type, you can use `-DCMAKE_BUILD_TYPE=Release` (see below). Otherwise, patching the CMake files may be a solution.

Some few software projects hardcode `-O2` for the `Release` build type in their CMake files, and thus `-DCMAKE_BUILD_TYPE=Release` can be safely set in this case if you are sure that `-O2` is the optimization level being used.

Warning

- Some software may break when using the `None` build type. Test the software when using the `None` build type to check if it will break or lose functionality.
- Some software may work only with the `Release` build type. You will need to experiment and test the software.

### Verifying the fixes

You can verify if the fixes are being correctly used by CMake by enabling the verbose mode of the build tool. For example, when using `make` (which is the CMake default), this can be done by adding `VERBOSE=1` to it (like `make VERBOSE=1`). This will enable `make` to output the compiler commands that are being executed. You can then run makepkg and examine the output to see if the compiler is using the `-D_FORTIFY_SOURCE=2` and `-O2` flags. If multiple optimization flags are being displayed in each command line, the last flag in the line will be the one used by the compiler (it means that `-O2` needs to be the last optimization flag in order to be effective).

## Prefix and library install directories

The standard Arch Linux `/usr` prefix can be specified by the `-DCMAKE_INSTALL_PREFIX=/usr` CMake option. This is usually needed because a lot of software defaults to install files into the `/usr/local` prefix.

Some upstream projects set their CMake files to install libraries into the `/usr/lib64` directory. If this is the case, you can correctly set the library installation directory to `/usr/lib` by using the `-DCMAKE_INSTALL_LIBDIR=lib` CMake option.

## Tips and tricks

### Specifying directories

Since CMake version 3.13, there is a `-B` option that automatically creates the build directory. This avoids the creation of the build directory by a separated `mkdir` (or `install`) command. The `-S` option specifies the source directory (where to search for a `CMakeLists.txt` file) and avoids the need of `cd`'ing into the source tree before executing `cmake`. Combined together, these two options are a convenient way to specify the build and the source directories.

Since building a typical CMake project requires many options, it is convenient to specify them in a local array in the build function. This avoids the need to use backslashes to split the long command to multiple lines and allows to include comments for each option separately.

    PKGBUILD

    build() {
      local cmake_options=(
        -B build
        -S $pkgname-$pkgver
        # Any other options required to build a project may follow
        [other_cmake_options]
      )
      cmake "${cmake_options[@]}"
      cmake --build build
    }

### Reducing possible unneeded output

The `-Wno-dev` CMake option will suppress the output of some warnings that are meant only for the upstream project developers who write the `CMakeLists.txt` files. Removing these warnings makes the CMake output smoother and reduces the burden on examining it. As a general rule, these warnings usually can be safely ignored by packagers.

### Removing insecure RPATH references from binaries

Sometimes the resulting binaries can contain insecure references in `RPATH`. This can be verified by running Namcap on the built package and consists in a security issue that should be fixed. There is a good chance to fix this by using the `CMAKE_SKIP_INSTALL_RPATH=YES` or `CMAKE_SKIP_RPATH=YES` CMake options. You need to experiment with both and see what will work in the software in question (using both options is not needed).

### Getting all available CMake options

For getting all "visible" CMake options that are available for a software project, execute `cmake -LAH` in the source tree (where the main `CMakeLists.txt` file is located).

If you want to save the output for later reference, you can redirect it to a file:

    $ cmake -LAH >options.txt 2>&1

### Avoiding FetchContent downloads during the build

CMake provides the FetchContent module that allows fetching additional resources or subprojects at build-time. However, ideally all sources should be fetched by makepkg prior to the build, as they are specified in the `sources` array. This can be accomplished via the option FETCHCONTENT_SOURCE_DIR_<uppercaseName>, which allows specifying the path to the files that would otherwise be fetched. Additionally, FETCHCONTENT_FULLY_DISCONNECTED=ON can be used to skip all downloads during the build, even if you missed any `FetchContent` declarations.

#### Example

Assume a project fetches the resource `foo`:

    CMakeLists.txt

    FetchContent_Declare(
        foo
        URL https://example.com/foo.tar.gz
        URL_HASH SHA256=cf051bf611a94884ba5e4c2d03932d14e83875c5b77f0fdf55c404cad0e4a6e6
    )
    FetchContent_MakeAvailable(foo)

Then, instead of downloading it during the build, this resource can be added to the `sources` array and declared when generating the build files:

    PKGBUILD

    sources=(
        ...
        "https://example.com/foo.tar.gz"
    )

    sha256sums=(
        ...
        "cf051bf611a94884ba5e4c2d03932d14e83875c5b77f0fdf55c404cad0e4a6e6"
    )

    $ cmake -B build -S "$pkgname-$pkgver" -DFETCHCONTENT_FULLY_DISCONNECTED=ON -DFETCHCONTENT_SOURCE_DIR_FOO="$srcdir/foo"

## Template

Here is a general template for the `build()` function that serves as a starting point for CMake-based packages. Supposing the package is C and C++ based and that it does not define any required compiler flags for the `Release` build type in the CMake files.

Note The check function will only work if projects use `enable_testing()` and/or `add_test()` functionality in `CMakeLists.txt`.

    PKGBUILD

    build() {
      local cmake_options=(
        -B build
        -S $pkgname-$pkgver
        -W no-dev
        -D CMAKE_BUILD_TYPE=None
        -D CMAKE_INSTALL_PREFIX=/usr
      )
      cmake "${cmake_options[@]}"
      cmake --build build
    }

    check() {
      local excluded_tests=""
      local ctest_flags=(
        --test-dir build
        # show the stdout and stderr when the test fails
        --output-on-failure
        # execute tests in parallel
        --parallel $(nproc)
        # exclude problematic tests
        --exclude-regex "$excluded_tests"
      )
      ctest "${ctest_flags[@]}"
    }

    package() {
      DESTDIR="$pkgdir" cmake --install build
    }

Do not forget to place cmake in makedepends.

## Example packages

- i2pd
- libmysofa

## See also

- CMake documentation
- Documentation reference for the latest version
- Wikipedia article
- cmake(1)

Category:

- Arch package guidelines

CMake package guidelines
Add topic