CMake package guidelines
32-bit – CLR – CMake – Cross – DKMS – Eclipse – Electron – Font – Free Pascal – GNOME – Go – Haskell – Java – KDE – Kernel modules – Lisp – Meson – MinGW – Node.js – Nonfree – OCaml – Perl – PHP – Python – R – Ruby – Rust - Security – Shell – VCS – Web – Wine
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, and can cause regressions, see Makepkg#Higher optimization level.
Fixing the automatic optimization flag override
Please note that there is no standard solution that can be applied to all cases due to CMake's flexibility. This section will discuss possible solutions and some points that should be observed.
The default CMake build type is None, which does not append any flags to CFLAGS and CXXFLAGS by default. However, omitting CMAKE_BUILD_TYPE is not guaranteed to fix the problem, as many projects automatically set the build type to Release (or another type) in their CMake files when it is left unset. In this case, one can explicitly set CMAKE_BUILD_TYPE=None, because most projects do not define any flags for None, let alone undesired ones. Also be aware of possible references to source files in the resulting package and the corresponding makepkg warning WARNING: Package contains reference to $srcdir, caused by the missing NDEBUG definition in the None build type.
However, using CMAKE_BUILD_TYPE=None is not always sufficient, since it is a common practice for many projects to define some required compiler flags for the Release build type in the CMake files (for example, modifying the CMAKE_C_FLAGS_RELEASE and CMAKE_CXX_FLAGS_RELEASE 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 some flags are being missed, one will need to look at the CMake files, or compare the output of make VERBOSE=1 for the None and Release build types (see #Verifying the fixes for more details). If the None build type causes some upstream flags to be missed, one may be caught between two problematic situations, because if the Release build type is used, they may be using the undesired -O3 flag, and if the None build type is used they will miss some required upstream-defined flags.
Another workaround is that if upstream doesn't explicitly initialize or modify CMAKE_C_FLAGS_RELEASE and CMAKE_CXX_FLAGS_RELEASE, then one can just override -O3 by setting CMAKE_C_FLAGS='-O2 -DNDEBUG' and the same for the latter. This preserves any other build-type-specific flags appended later by upstream.
A few 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 have verified that -O2 is the optimization level being used.
- Some software may break when using the
Nonebuild type. Test it to check if it will break or lose functionality. - Some software may work only with the
Releasebuild type. You may need to test and experiment with it.
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 (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, the last flag will take precedence.
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 CMake defaults to installing files into the /usr/local prefix (and it isn't overridden by many projects).
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.
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.