Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/actions/cmake/build/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ runs:
-DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_PREFIX:-/usr} \
-DCMAKE_INSTALL_RUNSTATEDIR:PATH=${INSTALL_RUNSTATEDIR:-/run} \
-DCMAKE_INSTALL_SYSCONFDIR:PATH=${INSTALL_SYSCONFDIR:-/etc} \
-DENABLE_TESTS:BOOL=ON
-DENABLE_TESTS:BOOL=ON \
${EXTRA_CMAKE_OPTIONS:-}
shell: bash --noprofile --norc -euxo pipefail {0}

- name: Build `mod_tile`
Expand Down
113 changes: 113 additions & 0 deletions .github/workflows/sanitizers-and-warnings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
---
name: Sanitizers & Warnings

on:
pull_request:
push:

jobs:
Warnings:
name: >-
${{ matrix.image }}
(-Werror)
(${{ matrix.compiler }})
runs-on: ubuntu-latest
strategy:
matrix:
build_system:
- CMake
compiler:
- GNU
- LLVM
image:
- "ubuntu:24.04"
fail-fast: false
container:
env:
CC: ${{ matrix.compiler == 'LLVM' && 'clang' || 'gcc' }}
CXX: ${{ matrix.compiler == 'LLVM' && 'clang++' || 'g++' }}
EXTRA_CMAKE_OPTIONS: -DENABLE_WARNINGS:BOOL=ON -DENABLE_WERROR:BOOL=ON
image: ${{ matrix.image }}
steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Install dependencies
uses: ./.github/actions/dependencies/install

- name: Build `mod_tile`
uses: ./.github/actions/cmake/build

- name: Test `mod_tile`
uses: ./.github/actions/cmake/test

Sanitizers:
name: >-
${{ matrix.name }}
(${{ matrix.compiler }})
# Run on the host rather than in a container so that `vm.mmap_rnd_bits` can be
# lowered, which ThreadSanitizer requires on recent kernels
runs-on: ubuntu-24.04
strategy:
matrix:
build_system:
- CMake
compiler:
- GNU
sanitizers:
- address,undefined
- thread
include:
- sanitizers: address,undefined
name: ASan + UBSan
# Includes the Apache/renderd integration tests (the ASan runtime is preloaded into httpd)
test_options: --exclude-regex 'clear_dirs_.+|remove_tile_.+' --output-on-failure
- sanitizers: thread
name: TSan
# TSan cannot be preloaded into the system httpd, so only the unit test executables are run
test_options: --tests-regex '_test$|^unit_tests$' --output-on-failure
fail-fast: false
env:
ASAN_OPTIONS: detect_leaks=0
BUILD_TYPE: Debug
CC: gcc
CXX: g++
EXTRA_CMAKE_OPTIONS: -DENABLE_SANITIZERS:STRING=${{ matrix.sanitizers }}
TSAN_OPTIONS: halt_on_error=1:second_deadlock_stack=1:suppressions=${{ github.workspace }}/tests/tsan.supp
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Lower `vm.mmap_rnd_bits` for ThreadSanitizer
run: sudo sysctl vm.mmap_rnd_bits=28
if: matrix.sanitizers == 'thread'

- name: Install dependencies
uses: ./.github/actions/dependencies/install

# Unlike in containers, installing the packages on the host starts their system services,
# whose default ports (e.g. memcached on 11211) collide with the test services
- name: Stop system `apache2` & `memcached` services
run: sudo systemctl stop apache2 memcached || true

- name: Build `mod_tile`
uses: ./.github/actions/cmake/build

- name: Test `mod_tile`
uses: ./.github/actions/cmake/test
with:
options: ${{ matrix.test_options }}

# Services started by the integration tests (httpd, renderd) report sanitizer errors to
# their own log files and may still exit "successfully", so check those logs too
- name: Check service logs for sanitizer reports
run: |
if [ ! -d build/tests/logs ]; then
exit 0
fi
if grep -rlaE 'ERROR: AddressSanitizer|runtime error:|WARNING: ThreadSanitizer' build/tests/logs; then
grep -raEA30 'ERROR: AddressSanitizer|runtime error:|WARNING: ThreadSanitizer' build/tests/logs
exit 1
fi
shell: bash --noprofile --norc -euo pipefail {0}
81 changes: 69 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
#-----------------------------------------------------------------------------

cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

#-----------------------------------------------------------------------------
Expand All @@ -21,17 +21,20 @@ project(mod_tile

set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 11 CACHE STRING "Sets the C++ standard.")
set(CMAKE_CXX_STANDARD 14 CACHE STRING "Sets the C++ standard (C++14 minimum; Catch2 v3 requires it).")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)

set(CMAKE_INSTALL_MODULESDIR CACHE PATH "Apache HTTP Server module installation directory")
set(ENABLE_MAN ON CACHE BOOL "Build man pages")
set(ENABLE_TESTS OFF CACHE BOOL "Build test suite")
set(USE_CAIRO ON CACHE BOOL "Add cairo support if available (for `store_ro_composite.c` backend)")
set(USE_CURL ON CACHE BOOL "Add curl support if available (for `store_ro_http_proxy.c` backend)")
set(USE_MEMCACHED ON CACHE BOOL "Add memcached support if available (for `store_memcached.c` backend)")
set(USE_RADOS ON CACHE BOOL "Add rados support if available (for `store_rados.c` backend)")
option(ENABLE_MAN "Build man pages" ON)
option(ENABLE_TESTS "Build test suite" OFF)
option(ENABLE_WARNINGS "Compile with extra warnings (-Wall -Wextra)" OFF)
option(ENABLE_WERROR "Treat compiler warnings as errors (requires ENABLE_WARNINGS)" OFF)
set(ENABLE_SANITIZERS "" CACHE STRING "Comma-separated sanitizers to build with, e.g. 'address,undefined' or 'thread'")
option(USE_CAIRO "Add cairo support if available (for `store_ro_composite.c` backend)" ON)
option(USE_CURL "Add curl support if available (for `store_ro_http_proxy.c` backend)" ON)
option(USE_MEMCACHED "Add memcached support if available (for `store_memcached.c` backend)" ON)
option(USE_RADOS "Add rados support if available (for `store_rados.c` backend)" ON)
set(MALLOC_LIB "libc" CACHE STRING "Memory Management Library for `renderd`")
set_property(CACHE MALLOC_LIB PROPERTY STRINGS "libc" "jemalloc" "mimalloc" "tcmalloc")

Expand All @@ -41,9 +44,11 @@ set_property(CACHE MALLOC_LIB PROPERTY STRINGS "libc" "jemalloc" "mimalloc" "tcm
#
#-----------------------------------------------------------------------------

include(FeatureSummary)
include(GNUInstallDirs)

# Packages
find_package(PkgConfig REQUIRED)
find_package(ICU REQUIRED uc)
find_package(Threads REQUIRED)

Expand All @@ -55,6 +60,10 @@ find_package(LIBMAPNIK 3 REQUIRED)

if(LIBMAPNIK_VERSION VERSION_GREATER_EQUAL 4)
set(CMAKE_CXX_STANDARD 17)
elseif(CMAKE_CXX_STANDARD LESS 14)
# e.g. an existing build directory whose cache still holds the old default of 11
message(WARNING "CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} is too old (Catch2 v3 needs C++14), using 14")
set(CMAKE_CXX_STANDARD 14)
endif()

if(USE_CURL)
Expand Down Expand Up @@ -171,8 +180,12 @@ set(RENDERD_TILE_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/cache/renderd/tiles")
set(RENDERD_PIDFILE "${RENDERD_RUN_DIR}/renderd.pid")
set(RENDERD_SOCKET "${RENDERD_RUN_DIR}/renderd.sock")

set(TILE_LOAD_DIRECTORY "${HTTPD_SYSCONFDIR}")
set(TILE_LOAD_FILENAME "tile.load")
if(NOT DEFINED CACHE{TILE_LOAD_DIRECTORY})
set(TILE_LOAD_DIRECTORY "${HTTPD_SYSCONFDIR}")
endif()
if(NOT DEFINED CACHE{TILE_LOAD_FILENAME})
set(TILE_LOAD_FILENAME "tile.load")
endif()
set(VERSION "${PROJECT_VERSION}")

#-----------------------------------------------------------------------------
Expand All @@ -182,9 +195,10 @@ set(VERSION "${PROJECT_VERSION}")
#-----------------------------------------------------------------------------

# include/config.h.in
# (generated into the build tree so that multiple build directories do not clobber each other)
configure_file(
${PROJECT_SOURCE_DIR}/includes/config.h.in
${PROJECT_SOURCE_DIR}/includes/config.h
${PROJECT_BINARY_DIR}/includes/config.h
)

# etc/apache2/tile.load.in
Expand All @@ -199,6 +213,32 @@ configure_file(
${PROJECT_BINARY_DIR}/renderd.conf
)

#-----------------------------------------------------------------------------
#
# Compiler flags
#
#-----------------------------------------------------------------------------

if(ENABLE_WARNINGS)
add_compile_options(-Wall -Wextra -Wno-unused-parameter)
# Log messages and storage IDs are deliberately truncated to PATH_MAX-sized buffers,
# so GCC's -Wformat-truncation heuristic only produces false positives here.
include(CheckCCompilerFlag)
check_c_compiler_flag(-Wformat-truncation HAVE_WFORMAT_TRUNCATION)
if(HAVE_WFORMAT_TRUNCATION)
add_compile_options(-Wno-format-truncation)
endif()
if(ENABLE_WERROR)
add_compile_options(-Werror)
endif()
endif()

if(ENABLE_SANITIZERS)
message(STATUS "Building with sanitizers: ${ENABLE_SANITIZERS}")
add_compile_options(-fsanitize=${ENABLE_SANITIZERS} -fno-omit-frame-pointer)
add_link_options(-fsanitize=${ENABLE_SANITIZERS})
endif()

#-----------------------------------------------------------------------------
#
# Build
Expand All @@ -214,7 +254,11 @@ add_subdirectory(src)
#-----------------------------------------------------------------------------

# Determine install destination for 'etc/apache2/tile.load.in'
if(EXISTS "/etc/os-release")
# (auto-detected from the build host unless TILE_LOAD_DIRECTORY / TILE_LOAD_FILENAME are set explicitly,
# which packagers and cross-compilers should do)
if(DEFINED CACHE{TILE_LOAD_DIRECTORY} OR DEFINED CACHE{TILE_LOAD_FILENAME})
message(STATUS "Using user-supplied TILE_LOAD_DIRECTORY / TILE_LOAD_FILENAME")
elseif(EXISTS "/etc/os-release")
execute_process(COMMAND sh -c ". /etc/os-release && echo $ID"
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE DISTRO_ID
Expand Down Expand Up @@ -320,3 +364,16 @@ if(ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
endif()

#-----------------------------------------------------------------------------
#
# Summary
#
#-----------------------------------------------------------------------------

add_feature_info(cairo CAIRO_FOUND "store_ro_composite backend")
add_feature_info(curl CURL_FOUND "store_ro_http_proxy backend")
add_feature_info(memcached LIBMEMCACHED_FOUND "store_memcached backend")
add_feature_info(rados LIBRADOS_FOUND "store_rados backend")
add_feature_info(tests ENABLE_TESTS "Catch2 test suite")
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
84 changes: 84 additions & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 21,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Release",
"description": "Release build installed under /usr with FHS paths",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_INSTALL_LOCALSTATEDIR": "/var",
"CMAKE_INSTALL_PREFIX": "/usr",
"CMAKE_INSTALL_RUNSTATEDIR": "/run",
"CMAKE_INSTALL_SYSCONFDIR": "/etc"
}
},
{
"name": "dev",
"displayName": "Development",
"description": "Debug build with tests and extra warnings",
"inherits": "default",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"ENABLE_TESTS": "ON",
"ENABLE_WARNINGS": "ON"
}
},
{
"name": "asan",
"displayName": "AddressSanitizer + UBSan",
"description": "Debug build with tests, instrumented with ASan and UBSan",
"inherits": "dev",
"cacheVariables": {
"ENABLE_SANITIZERS": "address,undefined"
}
},
{
"name": "tsan",
"displayName": "ThreadSanitizer",
"description": "Debug build with tests, instrumented with TSan (for renderd threading)",
"inherits": "dev",
"cacheVariables": {
"ENABLE_SANITIZERS": "thread"
}
}
],
"buildPresets": [
{ "name": "default", "configurePreset": "default" },
{ "name": "dev", "configurePreset": "dev" },
{ "name": "asan", "configurePreset": "asan" },
{ "name": "tsan", "configurePreset": "tsan" }
],
"testPresets": [
{
"name": "dev",
"configurePreset": "dev",
"output": { "outputOnFailure": true }
},
{
"name": "asan",
"configurePreset": "asan",
"output": { "outputOnFailure": true },
"environment": {
"ASAN_OPTIONS": "detect_leaks=0",
"UBSAN_OPTIONS": "halt_on_error=1:print_stacktrace=1"
}
},
{
"name": "tsan",
"configurePreset": "tsan",
"output": { "outputOnFailure": true },
"filter": { "include": { "name": "_test$|^unit_tests$" } },
"environment": {
"TSAN_OPTIONS": "suppressions=${sourceDir}/tests/tsan.supp"
}
}
]
}
Loading
Loading