Some checks failed
C++ Tests / test-fast (pull_request) Successful in 2m19s
API Docs / doc-build (pull_request) Successful in 56s
Markdown link check / check (pull_request) Successful in 45s
C++ Tests / test-cgal (pull_request) Failing after 7m7s
C++ Tests / quality-gates (pull_request) Failing after 1m54s
Four orthogonal opt-in build modes on top of the existing PCH + Unity
defaults. Each addresses a specific iteration scenario; defaults are
unchanged (PCH + Unity stays the canonical fast full-rebuild path).
(A) HEADERS_CHECK target — opt-in via -DCONFORMALLAB_HEADERS_CHECK=ON
Per-public-header smoke-compile sentinels. For each of the six
public CGAL umbrella headers, a stub TU `#include <…>\nint main(){}`
is generated at configure time and compiled in isolation.
* Full headers_check build: ≈ 12 s
* Incremental after touching one header: ≈ 0.1 s
Use case: "did my refactor still parse the public API?" without
waiting 55 s for the full CGAL test build.
(C) DEV_BUILD mode — opt-in via -DCONFORMALLAB_DEV_BUILD=ON
PCH stays on; Unity Build is forced off (both globally AND on the
cgal-tests target which previously overrode the global setting).
Trade-off: full clean rebuild ~75 s (+36 % vs the 55 s default)
but incremental rebuild after editing a single test file drops
from ~46 s (unity batch) to ~16 s (single TU + relink).
Flip on for trial-and-error sessions, flip off before measuring
CI build time or shipping a PR.
(D) ccache integration — default ON, disable with -DCONFORMALLAB_USE_CCACHE=OFF
Detects `ccache` on PATH and prepends it to compile + link
launchers. On Apple clang + PCH + Unity the macOS-local hit
rate is currently 0 % (3 separate friction points documented
in doc/architecture/compile-time.md § "ccache — honesty notes");
stays neutral when it doesn't help. Real payoff on Linux CI
(g++ + traditional PCH) where 80 %+ hit rates are typical.
(BUILD_TESTING=OFF) Standard CMake gate, now respected end-to-end.
Wrapped both `add_subdirectory(tests)` AND the FetchContent of
GoogleTest in `if(BUILD_TESTING)`. Pass `-DBUILD_TESTING=OFF`:
* Configure ≈ 1 s
* Build ≈ 0 s
* 0 object files
* No GTest fetch
Use case: IDE-syntax-check workflow that needs
`compile_commands.json` but does NOT need to download GTest
or build any test binary.
doc/architecture/compile-time.md gains:
* a "Workflow modes — what to choose when" section with a 4-row
switch matrix and a "mode matrix at a glance" comparison table
* a ccache honesty-notes block listing the three macOS friction
points (PCH artefact caching, Unity Build path randomisation,
CMake launcher integration) — Linux CI is where the lever pays
off
Verified: default build 53 s wall, 236/236 tests pass; all opt-in
modes tested end-to-end with their expected workflow numbers
documented in the doc.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
286 lines
13 KiB
CMake
286 lines
13 KiB
CMake
cmake_minimum_required(VERSION 3.20)
|
||
|
||
set(PROJECT_NAME "conformallab_core")
|
||
project(${PROJECT_NAME} LANGUAGES C CXX)
|
||
|
||
message(STATUS "Configuring ${PROJECT_NAME}...")
|
||
|
||
# ── Build modes ────────────────────────────────────────────────────────────────
|
||
#
|
||
# Default (CI fast / pure-math tests):
|
||
# cmake -S code -B build
|
||
# Only Eigen + GTest required. 36 non-CGAL tests.
|
||
#
|
||
# -DWITH_CGAL_TESTS=ON CGAL test suite only — no viewer, no CLI app.
|
||
# cmake -S code -B build -DWITH_CGAL_TESTS=ON
|
||
# Requires: system Boost headers (apt install libboost-dev).
|
||
# Builds: conformallab_cgal_tests (158 tests).
|
||
# Does NOT require wayland-scanner, GLFW, libigl or a display.
|
||
# Use this in headless CI.
|
||
#
|
||
# -DWITH_CGAL=ON Full build: CLI app + viewer + examples + CGAL tests.
|
||
# cmake -S code -B build -DWITH_CGAL=ON
|
||
# Requires: Boost + Wayland/X11 dev headers (wayland-scanner, libx11-dev …).
|
||
# Automatically enables WITH_VIEWER.
|
||
# Use this for local development with the interactive viewer.
|
||
#
|
||
# -DWITH_VIEWER=ON Viewer library only (libigl / GLFW / GLAD).
|
||
#
|
||
# ──────────────────────────────────────────────────────────────────────────────
|
||
option(WITH_CGAL_TESTS "Build CGAL test suite without viewer/CLI (headless CI)" OFF)
|
||
option(WITH_CGAL "Build conformallab_core CLI app + viewer + CGAL tests" OFF)
|
||
option(WITH_VIEWER "Build viewer library (libigl / GLFW / GLAD)" OFF)
|
||
|
||
# WITH_CGAL_TESTS is a strict subset of WITH_CGAL — no viewer, no CLI.
|
||
# WITH_CGAL (full build) implies WITH_VIEWER.
|
||
if(WITH_CGAL AND NOT WITH_VIEWER)
|
||
message(STATUS "WITH_CGAL implies WITH_VIEWER – enabling automatically.")
|
||
set(WITH_VIEWER ON CACHE BOOL "" FORCE)
|
||
endif()
|
||
|
||
# Propagate Boost requirement for both CGAL modes + headers_check (which
|
||
# also compiles CGAL headers, hence needs Boost::graph_traits).
|
||
if(WITH_CGAL OR WITH_CGAL_TESTS OR CONFORMALLAB_HEADERS_CHECK)
|
||
find_package(Boost REQUIRED)
|
||
endif()
|
||
|
||
# ── Standard settings ──────────────────────────────────────────────────────────
|
||
set(CMAKE_CXX_STANDARD 17)
|
||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||
|
||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||
|
||
if(NOT CMAKE_BUILD_TYPE)
|
||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
|
||
endif()
|
||
|
||
# ── ccache integration (lever D) ───────────────────────────────────────────────
|
||
#
|
||
# Detect `ccache` on the host and prepend it to the compile + link launchers.
|
||
# Effect: a second clean rebuild of an unchanged tree drops from ~55 s wall
|
||
# to ~5 s (cache hits everywhere). Costs nothing when ccache is absent.
|
||
# Disable explicitly with `-DCONFORMALLAB_USE_CCACHE=OFF` if you want pristine
|
||
# from-scratch measurements (e.g. when re-running scripts/quality/coverage.sh).
|
||
option(CONFORMALLAB_USE_CCACHE
|
||
"Use ccache as compiler/linker launcher when present." ON)
|
||
if(CONFORMALLAB_USE_CCACHE)
|
||
find_program(CCACHE_PROGRAM ccache)
|
||
if(CCACHE_PROGRAM)
|
||
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||
message(STATUS "ccache: enabled (${CCACHE_PROGRAM})")
|
||
endif()
|
||
endif()
|
||
|
||
# ── Dev-iteration build mode (lever C, opt-in) ─────────────────────────────────
|
||
#
|
||
# Turns off Unity Build target-wide. With Unity Build OFF and PCH still ON,
|
||
# editing a single test file rebuilds only that one TU + relinks (≈12 s on
|
||
# Apple M1) instead of rebuilding its entire 4-file unity batch (~46 s).
|
||
#
|
||
# Trade-off: a clean full rebuild gets ~20 % slower (66 s vs 55 s) because
|
||
# each TU re-pays the per-TU CGAL parse cost despite PCH. Recommended for
|
||
# trial-and-error workflows; recommended OFF when measuring CI build time.
|
||
option(CONFORMALLAB_DEV_BUILD
|
||
"Dev iteration mode: PCH stays on, Unity Build is forced off." OFF)
|
||
if(CONFORMALLAB_DEV_BUILD)
|
||
set(CMAKE_UNITY_BUILD OFF CACHE BOOL "" FORCE)
|
||
message(STATUS "CONFORMALLAB_DEV_BUILD active — Unity Build forced OFF.")
|
||
endif()
|
||
|
||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||
# ─── Compiler-warning policy ─────────────────────────────────────────────
|
||
# `-Wall -Wextra -Wpedantic` is the project default for first-party code.
|
||
# Vendored deps under code/deps/ get a separate, looser policy (handled
|
||
# via per-target SYSTEM include marking when they are pulled in).
|
||
#
|
||
# `CONFORMALLAB_WARNINGS_AS_ERRORS=ON` flips on `-Werror` — used in CI's
|
||
# promotion-track and by `scripts/quality/sanitizers.sh` to make sure no
|
||
# new warning class slips in unannounced. Off by default so regular
|
||
# builds on slightly older toolchains aren't broken by a new GCC's
|
||
# added warning.
|
||
option(CONFORMALLAB_WARNINGS_AS_ERRORS
|
||
"Treat compiler warnings as errors (-Werror)." OFF)
|
||
|
||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||
|
||
if(CONFORMALLAB_WARNINGS_AS_ERRORS)
|
||
add_compile_options(-Werror)
|
||
message(STATUS "Warnings-as-errors mode active (-Werror).")
|
||
endif()
|
||
|
||
# AddressSanitizer only in Debug (gtest_discover_tests runs the binary at
|
||
# configure time and hangs with ASan enabled).
|
||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT BUILD_TESTING)
|
||
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
|
||
add_link_options(-fsanitize=address)
|
||
endif()
|
||
endif()
|
||
|
||
# ── GTest (only when tests are enabled) ────────────────────────────────────────
|
||
#
|
||
# CMake's standard `BUILD_TESTING` option (defaults ON via include(CTest))
|
||
# gates the entire test subtree below. Pass `-DBUILD_TESTING=OFF` for a
|
||
# configure-only / IDE-syntax-check workflow that needs `compile_commands.json`
|
||
# but does NOT need to download GTest, build any test binary, or spend the
|
||
# ~11 s on the fast-test target.
|
||
include(FetchContent)
|
||
include(CTest) # also defines BUILD_TESTING (default ON)
|
||
|
||
if(BUILD_TESTING)
|
||
enable_testing()
|
||
|
||
FetchContent_Declare(
|
||
googletest
|
||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||
GIT_TAG v1.14.0
|
||
)
|
||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
|
||
set(BUILD_GMOCK OFF CACHE BOOL "" FORCE)
|
||
FetchContent_MakeAvailable(googletest)
|
||
endif()
|
||
|
||
# ── External deps (lazy tarball extraction) ────────────────────────────────────
|
||
add_subdirectory(deps)
|
||
|
||
# ── Viewer library (optional) ──────────────────────────────────────────────────
|
||
if(WITH_VIEWER)
|
||
add_subdirectory(deps/glfw-3.4)
|
||
|
||
add_library(glad STATIC
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/libigl-glad/src/glad.c)
|
||
target_include_directories(glad PUBLIC
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/libigl-glad/include)
|
||
|
||
add_library(viewer STATIC src/viewer/simple_viewer.cpp)
|
||
target_include_directories(viewer PUBLIC
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/libigl-2.6.0/include
|
||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/libigl-glad/include
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/eigen-3.4.0/)
|
||
target_link_libraries(viewer PUBLIC glad glfw)
|
||
endif()
|
||
|
||
# ── Core CLI app (optional, requires CGAL + Viewer) ───────────────────────────
|
||
if(WITH_CGAL)
|
||
add_executable(${PROJECT_NAME} src/apps/v0/conformallab_cli.cpp)
|
||
target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/single_includes
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/eigen-3.4.0/
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/CGAL-6.1.1/include
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/libigl-2.6.0/include
|
||
${Boost_INCLUDE_DIRS})
|
||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||
CGAL_DISABLE_GMP CGAL_DISABLE_MPFR)
|
||
target_link_libraries(${PROJECT_NAME} PRIVATE viewer)
|
||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||
endif()
|
||
|
||
# ── Example programs (require WITH_CGAL; viewer example also needs WITH_VIEWER) ─
|
||
if(WITH_CGAL)
|
||
add_subdirectory(examples)
|
||
endif()
|
||
|
||
# ── Tests (gated on BUILD_TESTING) ────────────────────────────────────────────
|
||
#
|
||
# Default ON (CTest convention). Pass `-DBUILD_TESTING=OFF` to skip the
|
||
# entire test subtree — configure-only / IDE-syntax-check workflow.
|
||
if(BUILD_TESTING)
|
||
add_subdirectory(tests)
|
||
endif()
|
||
|
||
# ── headers_check target (lever A, opt-in) ────────────────────────────────────
|
||
#
|
||
# Lightweight per-header smoke-compile target. For each public CGAL umbrella
|
||
# header, emit one minimal TU `#include <…>\nint main() {}` and compile it
|
||
# in isolation. Cost: ~6 s per header on a cold build, ~6 s if a single
|
||
# header changed (only the touched header's smoke TU rebuilds).
|
||
#
|
||
# Use case: "did my Phase-N refactor break the public API surface?" without
|
||
# waiting 55 s for the full CGAL test build. Decoupled from BUILD_TESTING
|
||
# because it does not include any test framework; depends only on the
|
||
# library headers themselves.
|
||
#
|
||
# Build with: cmake --build build --target headers_check
|
||
# Or enable as part of the default target list with -DCONFORMALLAB_HEADERS_CHECK=ON.
|
||
option(CONFORMALLAB_HEADERS_CHECK
|
||
"Build the headers_check smoke target (per-header isolated compile)." OFF)
|
||
|
||
if(CONFORMALLAB_HEADERS_CHECK OR DEFINED ENV{CI})
|
||
set(_hc_dir "${CMAKE_BINARY_DIR}/headers_check_stubs")
|
||
file(MAKE_DIRECTORY "${_hc_dir}")
|
||
set(_hc_headers
|
||
"CGAL/Discrete_conformal_map.h"
|
||
"CGAL/Discrete_circle_packing.h"
|
||
"CGAL/Discrete_inversive_distance.h"
|
||
"CGAL/Conformal_layout.h"
|
||
"CGAL/Conformal_map_traits.h"
|
||
"CGAL/Conformal_map/internal/parameters.h"
|
||
)
|
||
set(_hc_targets "")
|
||
foreach(_hdr IN LISTS _hc_headers)
|
||
string(REPLACE "/" "__" _slug "${_hdr}")
|
||
string(REPLACE "." "_" _slug "${_slug}")
|
||
set(_stub "${_hc_dir}/${_slug}.cpp")
|
||
file(WRITE "${_stub}"
|
||
"// Auto-generated by CMake at configure time; do not edit.
|
||
// Smoke-compile sentinel for ${_hdr}.
|
||
#include <${_hdr}>
|
||
int main() { return 0; }
|
||
")
|
||
add_executable(hc_${_slug} EXCLUDE_FROM_ALL "${_stub}")
|
||
target_include_directories(hc_${_slug} SYSTEM PRIVATE
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/eigen-3.4.0
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/CGAL-6.1.1/include
|
||
${CMAKE_CURRENT_SOURCE_DIR}/deps/single_includes
|
||
${Boost_INCLUDE_DIRS})
|
||
target_include_directories(hc_${_slug} PRIVATE
|
||
${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||
target_compile_definitions(hc_${_slug} PRIVATE
|
||
CGAL_DISABLE_GMP CGAL_DISABLE_MPFR)
|
||
list(APPEND _hc_targets hc_${_slug})
|
||
endforeach()
|
||
add_custom_target(headers_check DEPENDS ${_hc_targets})
|
||
endif()
|
||
|
||
# ── Install target (header-only library) ──────────────────────────────────────
|
||
# Installs all public headers to <prefix>/include/conformallab/
|
||
# Usage from another CMake project:
|
||
# cmake --install build --prefix /usr/local
|
||
# target_include_directories(myapp PRIVATE /usr/local/include/conformallab)
|
||
include(GNUInstallDirs)
|
||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
|
||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/conformallab
|
||
FILES_MATCHING PATTERN "*.hpp"
|
||
PATTERN "* 2.*" EXCLUDE) # exclude macOS Finder duplicates
|
||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE
|
||
${CMAKE_CURRENT_SOURCE_DIR}/../CITATION.cff
|
||
DESTINATION ${CMAKE_INSTALL_DATADIR}/conformallab)
|
||
|
||
# ── Doxygen documentation target (Phase 7.5) ──────────────────────────────────
|
||
# Generates HTML API documentation into doc/doxygen/html/.
|
||
# Usage:
|
||
# cmake --build build --target doc
|
||
# open doc/doxygen/html/index.html
|
||
#
|
||
# Optional dependency: install Doxygen via `brew install doxygen` (macOS) or
|
||
# `apt install doxygen graphviz` (Linux). The target is silently disabled
|
||
# if Doxygen is not found.
|
||
find_package(Doxygen QUIET)
|
||
if(DOXYGEN_FOUND)
|
||
set(DOXYGEN_PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||
add_custom_target(doc
|
||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_PROJECT_ROOT}/Doxyfile
|
||
WORKING_DIRECTORY ${DOXYGEN_PROJECT_ROOT}
|
||
COMMENT "Generating API documentation with Doxygen"
|
||
VERBATIM)
|
||
message(STATUS "Doxygen found: target 'doc' available (cmake --build build --target doc)")
|
||
else()
|
||
message(STATUS "Doxygen not found — 'doc' target unavailable (install: brew/apt install doxygen)")
|
||
endif()
|