Repository navigation
doc: cmake compilation guide - #7744
Conversation
|
I've been using 'ninja' locally with {
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 22,
"patch": 0
},
"configurePresets": [
{
"name": "ninja-release-local",
"displayName": "Ninja Release (local install)",
"description": "Matches mycmake.sh options but installs into ./_install to avoid sudo.",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/_install",
"PKG_CONFIG_EXECUTABLE": "/usr/bin/pkg-config",
"CBLAS_PKGCONFIG": "openblas",
"LAPACKE_PKGCONFIG": "lapacke",
"WITH_NETCDF": "ON",
"WITH_READLINE": "ON",
"WITH_FREETYPE": "ON"
}
},
{
"name": "unix-makefiles-release-local",
"displayName": "Unix Makefiles Release (local install)",
"description": "Fallback preset when Ninja is not available.",
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_INSTALL_PREFIX": "${sourceDir}/_install",
"PKG_CONFIG_EXECUTABLE": "/usr/bin/pkg-config",
"CBLAS_PKGCONFIG": "openblas",
"LAPACKE_PKGCONFIG": "lapacke",
"WITH_NETCDF": "ON",
"WITH_READLINE": "ON",
"WITH_FREETYPE": "ON"
}
},
{
"name": "ninja-release-system",
"displayName": "Ninja Release (/usr/local)",
"description": "Closest to mycmake.sh (may require sudo for install).",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_INSTALL_PREFIX": "/usr/local",
"PKG_CONFIG_EXECUTABLE": "/usr/bin/pkg-config",
"CBLAS_PKGCONFIG": "openblas",
"LAPACKE_PKGCONFIG": "lapacke",
"WITH_NETCDF": "ON",
"WITH_READLINE": "ON",
"WITH_FREETYPE": "ON"
}
}
],
"buildPresets": [
{
"name": "build",
"displayName": "Build",
"configurePreset": "ninja-release-local",
"jobs": 0
},
{
"name": "install",
"displayName": "Install",
"configurePreset": "ninja-release-local",
"targets": [
"install"
],
"jobs": 0
}
]
}
I also have 'mycmake.sh` #!/bin/bash
# Build, install, and optionally test GRASS on Pop!_OS 22.04.
#
# Usage:
# ./mycmake.sh Build and install (default).
# ./mycmake.sh -t Build, install, then run the whole test suite.
# ./mycmake.sh -t PATH Build, install, then test only PATH (a tool or
# lib dir, e.g. lib/octree or raster/r.slope.aspect).
# ./mycmake.sh -T [PATH] Test only, against the existing build/install
# (skip configure, build, and install).
#
# Test layers, run in order; a failure in one is reported but the rest still run:
# 1. C unit-test modules (test.*.lib) via grass --tmp-project --exec ... -u
# 2. ctest registered suite (the gunittest testsuite/ tests)
# 3. pytest (the tests/ pytest-style tests)
# The NC SPM "thorough" gunittest run is intentionally not invoked here: it is
# assumed any sample data a test needs is already downloaded.
# This script uses bash-only features (pipefail, arrays). If invoked as `sh`,
# re-exec with bash.
if [ -z "${BASH_VERSION:-}" ]; then
exec bash "$0" "$@"
fi
set -euo pipefail
build_dir="build"
install_prefix="/usr/local"
# Preserve the caller's PATH (and thus any active venv) for the test phase; the
# build phase deliberately runs with a minimal PATH.
orig_path="${PATH}"
run_tests=0
test_only=0
test_path=""
while [ $# -gt 0 ]; do
case "$1" in
-t | --test) run_tests=1 ;;
-T | --test-only)
run_tests=1
test_only=1
;;
-h | --help)
sed -n '2,21p' "$0"
exit 0
;;
-*)
echo "Unknown option: $1" >&2
exit 2
;;
*) test_path="$1" ;;
esac
shift
done
build_and_install() {
local cmake_generator_args=()
if command -v ninja >/dev/null 2>&1; then
cmake_generator_args=(-G Ninja)
fi
rm -rf "${build_dir}"
# Configure and build with a minimal PATH, scoped to this subshell so the
# caller's PATH (and venv) survive for the test phase.
(
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cmake -S . -B "${build_dir}" "${cmake_generator_args[@]}" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${install_prefix}" \
-DPKG_CONFIG_EXECUTABLE=/usr/bin/pkg-config \
-DCBLAS_PKGCONFIG=openblas \
-DLAPACKE_PKGCONFIG=lapacke \
-DWITH_NETCDF=ON \
-DWITH_READLINE=ON \
-DWITH_FREETYPE=ON
cmake --build "${build_dir}" --parallel
)
# Use sudo only for the install step.
sudo cmake --install "${build_dir}"
}
# Populate the environment pytest and the grass session runner need, using the
# freshly installed grass.
setup_test_env() {
export PATH="${install_prefix}/bin:${orig_path}"
if ! command -v grass >/dev/null 2>&1; then
echo "grass not found on PATH; build and install first (drop -T)." >&2
exit 1
fi
gisbase_path="$(grass --config path)"
export PYTHONPATH="$(grass --config python_path):${PYTHONPATH:-}"
export LD_LIBRARY_PATH="${gisbase_path}/lib:${LD_LIBRARY_PATH:-}"
# Used by .coveragerc path mapping; harmless otherwise.
export INITIAL_GISBASE="${gisbase_path}"
export INITIAL_PWD="${PWD}"
}
# Run installed C unit-test modules (test.*.lib). With a filter, run only the
# module for that lib (lib/octree -> test.octree.lib).
run_c_unit_tests() {
local filter="$1" found=0
local bin name
for bin in "${gisbase_path}"/bin/test.*.lib; do
[ -e "${bin}" ] || continue
name="$(basename "${bin}")"
if [ -n "${filter}" ] && [ "${name}" != "test.${filter}.lib" ]; then
continue
fi
found=1
echo ">>> C unit test: ${name}"
if ! grass --tmp-project XY --exec "${name}" -u; then
tests_failed=1
fi
done
if [ "${found}" -eq 0 ] && [ -n "${filter}" ]; then
echo ">>> no C unit-test module test.${filter}.lib (skipping)"
fi
}
# Run the ctest-registered suite (gunittest testsuite/ tests). With a filter,
# restrict to tests whose name matches it.
run_ctest_suite() {
local filter="$1"
echo ">>> ctest suite${filter:+ (filter: ${filter})}"
if [ -n "${filter}" ]; then
ctest --test-dir "${build_dir}" --output-on-failure -R "${filter}" \
|| tests_failed=1
else
ctest --test-dir "${build_dir}" --output-on-failure || tests_failed=1
fi
}
# Run pytest. With a path, scope to it; otherwise collect from the repo root.
# pytest exit code 5 means "no tests collected", which we treat as a skip.
run_pytest() {
local path="${1:-.}" rc=0
echo ">>> pytest: ${path}"
pytest "${path}" || rc=$?
if [ "${rc}" -eq 5 ]; then
echo ">>> no pytest tests under ${path} (skipping)"
elif [ "${rc}" -ne 0 ]; then
tests_failed=1
fi
}
run_test_suite() {
setup_test_env
tests_failed=0
local filter=""
if [ -n "${test_path}" ]; then
filter="$(basename "${test_path}")"
fi
run_c_unit_tests "${filter}"
run_ctest_suite "${filter}"
run_pytest "${test_path}"
if [ "${tests_failed}" -ne 0 ]; then
echo "!!! one or more test layers failed" >&2
exit 1
fi
echo "=== all test layers passed ==="
}
if [ "${test_only}" -eq 0 ]; then
build_and_install
fi
if [ "${run_tests}" -eq 1 ]; then
run_test_suite
fi
|
nilason
left a comment
There was a problem hiding this comment.
Looks good to me. The install instruction cmake --install build is only mentioned under Compiling Addons, should probably deserve to be added before.
(I'm not too happy with ccache being ON by default, but that is the current behaviour so this description is correct).
I added a missing install section. |
I added a mention of ninja in the documentation. But I am not sure what to do with this, do you want to suggest an advanced section? Or is this something that should be in AGENTS.md? |
nilason
left a comment
There was a problem hiding this comment.
Thanks!
(There is a wiki page https://grasswiki.osgeo.org/wiki/Compile_and_Install_With_CMake, which could be added as a link. It needs however an update.)
This is an attempt to help myself and other transition to cmake compilation. I wanted to have an overview of cmake commands and workflows. I also tried to link it in relevant parts of the documentation and CONTRIBUTING, I ended up removing a lot of stuff from there. There is probably more cleanup needed there and in INSTALL.md and testing README, but that was somewhat out of scope. In the guide, I specifically am not sure about parts: