From a5e436ba2c1a5f678616178c7ed6623748e9f85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Tue, 8 Sep 2026 11:52:08 -0700 Subject: [PATCH 01/22] Reduce Unix CI matrix and improve compiler caching --- .github/workflows/continuous.yaml | 40 ++++++++++++++++++------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index bfa9289e..cffe9382 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -24,21 +24,19 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, macos-15-intel, macos-15] - config: [RelWithDebInfo, Debug] - compiler: [gcc, apple, llvm] - sanitizer: ["Address", "Thread"] # TODO: Add Memory+Undefined Sanitizer - exclude: - - os: macos-15-intel - compiler: gcc - - os: macos-15-intel - sanitizer: Thread - - os: macos-15-intel - config: Debug - - os: macos-15 - compiler: gcc - - os: ubuntu-24.04 - compiler: apple + include: + # Linux release builds exercise tests disabled in Debug mode, and ASan also enables LSan. + - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: Address } + - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: Thread } + - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: Address } + - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: Thread } + + # Keep AppleClang release coverage on Intel and full sanitizer coverage on Apple Silicon. + - { os: macos-15-intel, compiler: apple, config: RelWithDebInfo, sanitizer: Address } + - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: Address } + - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: Thread } + - { os: macos-15, compiler: llvm, config: Debug, sanitizer: Address } + - { os: macos-15, compiler: llvm, config: Debug, sanitizer: Thread } steps: - name: Free Disk Space if: runner.os == 'Linux' @@ -133,7 +131,9 @@ jobs: - name: Ccache uses: hendrikmuhs/ccache-action@v1.2 with: - key: ${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.sanitizer }}-${{ matrix.config }} + key: ${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.config }}-${{ matrix.sanitizer }} + max-size: 500M + verbose: 1 - name: Setup Xcode version if: matrix.os == 'macos-15-intel' @@ -190,6 +190,12 @@ jobs: - name: Tests run: cd ${{ env.build_dir }}; ctest --verbose -j ${{ steps.cpu-cores.outputs.count }} + - name: Clean and report ccache + if: always() + run: | + ccache --cleanup + ccache --show-stats --verbose + #################### # Windows #################### @@ -250,7 +256,7 @@ jobs: id: cpu-cores - name: Sccache - uses: mozilla-actions/sccache-action@v0.0.10 + uses: mozilla-actions/sccache-action@v0.0.11 - name: Set x64 vars if: matrix.os == 'windows-2025' From 9345831c1d8046d49b0aa974b8fdb5c0ba787a73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Tue, 8 Sep 2026 21:45:31 -0700 Subject: [PATCH 02/22] Restore UBSan matrix before resource-pressure fixes --- .github/workflows/continuous.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index cffe9382..6a41ff02 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -26,16 +26,16 @@ jobs: matrix: include: # Linux release builds exercise tests disabled in Debug mode, and ASan also enables LSan. - - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: Address } + - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: "Address;Undefined" } - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: Thread } - - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: Address } + - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: "Address;Undefined" } - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: Thread } # Keep AppleClang release coverage on Intel and full sanitizer coverage on Apple Silicon. - - { os: macos-15-intel, compiler: apple, config: RelWithDebInfo, sanitizer: Address } - - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: Address } + - { os: macos-15-intel, compiler: apple, config: RelWithDebInfo, sanitizer: "Address;Undefined" } + - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: "Address;Undefined" } - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: Thread } - - { os: macos-15, compiler: llvm, config: Debug, sanitizer: Address } + - { os: macos-15, compiler: llvm, config: Debug, sanitizer: "Address;Undefined" } - { os: macos-15, compiler: llvm, config: Debug, sanitizer: Thread } steps: - name: Free Disk Space @@ -133,6 +133,7 @@ jobs: with: key: ${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.config }}-${{ matrix.sanitizer }} max-size: 500M + evict-old-files: job verbose: 1 - name: Setup Xcode version From 9a5492007c8c446ba0728618b77b5785e52af000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Tue, 8 Sep 2026 22:29:16 -0700 Subject: [PATCH 03/22] Limit Embree ISA variants in macOS CI --- .github/workflows/continuous.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 6a41ff02..59bc18f3 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -154,11 +154,11 @@ jobs: - name: Select embree isa (macOS x64) if: runner.os == 'macOS' && runner.arch == 'X64' - run: echo "embree_max_isa=DEFAULT" >> $GITHUB_ENV + run: echo "embree_max_isa=SSE2" >> $GITHUB_ENV - name: Select embree isa (macOS arm64) if: runner.os == 'macOS' && runner.arch == 'ARM64' - run: echo "embree_max_isa=NONE" >> $GITHUB_ENV + run: echo "embree_max_isa=NEON" >> $GITHUB_ENV - name: Get number of CPU cores uses: SimenB/github-actions-cpu-cores@v1 From 01495e71c0d88c567cac036663ab22e8655f284d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 9 Sep 2026 07:22:48 -0700 Subject: [PATCH 04/22] Reduce sanitizer build pressure on Linux --- .github/workflows/continuous.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 59bc18f3..73633cdb 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -171,6 +171,7 @@ jobs: -DCMAKE_BUILD_TYPE=${{ matrix.config }} \ -DLAGRANGE_JENKINS=ON \ -DLAGRANGE_ALL=ON \ + -DLAGRANGE_EXAMPLES=OFF \ -DLAGRANGE_LIMIT_PARALLELISM=ON \ -DOPENVDB_CORE_SHARED=ON \ -DOPENVDB_CORE_STATIC=OFF \ @@ -179,6 +180,12 @@ jobs: -DEMBREE_MAX_ISA=${{ env.embree_max_isa }} \ -DUSE_SANITIZER="${{ matrix.sanitizer }}" + # SSE2 is Embree's lowest x86 ISA, but its large translation units still exhaust + # the Linux runner when four Clang ASan+UBSan compilations run concurrently. + - name: Build Embree with limited parallelism + if: runner.os == 'Linux' && matrix.compiler == 'llvm' && matrix.sanitizer == 'Address;Undefined' + run: cmake --build ${{ env.build_dir }} --target embree -j 2 + - name: Build run: cmake --build ${{ env.build_dir }} -j ${{ steps.cpu-cores.outputs.count }} From 1f3d2972d8dea38fc13003076b404e34db00a1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 9 Sep 2026 07:36:14 -0700 Subject: [PATCH 05/22] Limit Embree compile concurrency on Linux --- .github/workflows/continuous.yaml | 7 ------- cmake/lagrange/lagrange_limit_parallelism.cmake | 2 +- cmake/recipes/external/embree.cmake | 6 ++++++ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 73633cdb..59bc18f3 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -171,7 +171,6 @@ jobs: -DCMAKE_BUILD_TYPE=${{ matrix.config }} \ -DLAGRANGE_JENKINS=ON \ -DLAGRANGE_ALL=ON \ - -DLAGRANGE_EXAMPLES=OFF \ -DLAGRANGE_LIMIT_PARALLELISM=ON \ -DOPENVDB_CORE_SHARED=ON \ -DOPENVDB_CORE_STATIC=OFF \ @@ -180,12 +179,6 @@ jobs: -DEMBREE_MAX_ISA=${{ env.embree_max_isa }} \ -DUSE_SANITIZER="${{ matrix.sanitizer }}" - # SSE2 is Embree's lowest x86 ISA, but its large translation units still exhaust - # the Linux runner when four Clang ASan+UBSan compilations run concurrently. - - name: Build Embree with limited parallelism - if: runner.os == 'Linux' && matrix.compiler == 'llvm' && matrix.sanitizer == 'Address;Undefined' - run: cmake --build ${{ env.build_dir }} --target embree -j 2 - - name: Build run: cmake --build ${{ env.build_dir }} -j ${{ steps.cpu-cores.outputs.count }} diff --git a/cmake/lagrange/lagrange_limit_parallelism.cmake b/cmake/lagrange/lagrange_limit_parallelism.cmake index 8dd9d046..1f67ed19 100644 --- a/cmake/lagrange/lagrange_limit_parallelism.cmake +++ b/cmake/lagrange/lagrange_limit_parallelism.cmake @@ -53,7 +53,7 @@ function(lagrange_limit_parallelism) message(STATUS "Parallelism: Link job memory budget: ${_link_memory} MB (${_build_type})") message(STATUS "Parallelism: Limiting link pool to ${num_link_jobs}") - set_property(GLOBAL PROPERTY JOB_POOLS pool-link=${num_link_jobs}) + set_property(GLOBAL PROPERTY JOB_POOLS pool-link=${num_link_jobs} pool-heavy-compile=1) set(CMAKE_JOB_POOL_LINK "pool-link" CACHE STRING "Job pool for linking" FORCE) endif() endfunction() diff --git a/cmake/recipes/external/embree.cmake b/cmake/recipes/external/embree.cmake index a323f51c..10adf902 100644 --- a/cmake/recipes/external/embree.cmake +++ b/cmake/recipes/external/embree.cmake @@ -129,6 +129,12 @@ function(embree_import_target) unignore_package(TBB) + # Embree has several memory-intensive translation units. Limit their concurrency on + # Linux when the project's memory-aware parallelism policy is enabled. + if(LAGRANGE_LIMIT_PARALLELISM AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_GENERATOR MATCHES "^Ninja") + set_property(TARGET embree PROPERTY JOB_POOL_COMPILE pool-heavy-compile) + endif() + # Disable warnings if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # Embree's subgrid.h is known for causing array subscript out of bound From 2f184af6eaa9747978567d26416872e62f8e92b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 9 Sep 2026 07:40:43 -0700 Subject: [PATCH 06/22] Preserve compiler cache entries across CI runs --- .github/workflows/continuous.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 59bc18f3..30dcce20 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -132,8 +132,10 @@ jobs: uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.config }}-${{ matrix.sanitizer }} - max-size: 500M - evict-old-files: job + # A 500 MB cache retains only about half of this build's cacheable objects. + # Do not evict entries unused by the current job: cancelled jobs would save + # that partially populated cache as the newest restore candidate. + max-size: 750M verbose: 1 - name: Setup Xcode version From e71436226a8f760555b491a6b43d2e70034e5a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 9 Sep 2026 08:00:15 -0700 Subject: [PATCH 07/22] Clarify compiler cache retention --- .github/workflows/continuous.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 30dcce20..aa4ff461 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -132,9 +132,8 @@ jobs: uses: hendrikmuhs/ccache-action@v1.2 with: key: ${{ matrix.os }}-${{ matrix.compiler }}-${{ matrix.config }}-${{ matrix.sanitizer }} - # A 500 MB cache retains only about half of this build's cacheable objects. - # Do not evict entries unused by the current job: cancelled jobs would save - # that partially populated cache as the newest restore candidate. + # Keep more objects, and do not prune untouched entries because cancelled jobs + # would save an incomplete cache as the newest restore candidate. max-size: 750M verbose: 1 From 096ebbc526d32a3178e4e1d2fbec830d0ee6fa2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 9 Sep 2026 08:07:09 -0700 Subject: [PATCH 08/22] Warm compiler caches from main --- .github/workflows/continuous.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index aa4ff461..4f86e4c1 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -4,6 +4,9 @@ on: pull_request: branches: - main + push: + branches: + - main concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -21,6 +24,8 @@ jobs: Unix: name: ${{ matrix.os }} (${{ matrix.compiler }}, ${{ matrix.config }}, ${{ matrix.sanitizer }}Sanitizer) runs-on: ${{ matrix.os }} + env: + CCACHE_COMPRESSLEVEL: 5 strategy: fail-fast: false matrix: From 077d438496fd05ac1e1bb75e82b0169a7fc423c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 9 Sep 2026 08:09:15 -0700 Subject: [PATCH 09/22] Increase sccache compression level --- .github/workflows/continuous.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 4f86e4c1..5d14c93d 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -212,6 +212,7 @@ jobs: runs-on: ${{ matrix.os }} env: SCCACHE_GHA_ENABLED: "true" + SCCACHE_CACHE_ZSTD_LEVEL: "5" strategy: fail-fast: false matrix: From cf9a03d8eb8eda8ec25ab80f3f77b8e2103f9088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 9 Sep 2026 11:03:41 -0700 Subject: [PATCH 10/22] Prebuild Embree with bounded parallelism --- .github/workflows/continuous.yaml | 4 ++++ cmake/lagrange/lagrange_limit_parallelism.cmake | 2 +- cmake/recipes/external/embree.cmake | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 5d14c93d..eae69793 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -185,6 +185,10 @@ jobs: -DEMBREE_MAX_ISA=${{ env.embree_max_isa }} \ -DUSE_SANITIZER="${{ matrix.sanitizer }}" + - name: Build Embree + if: runner.os == 'Linux' + run: cmake --build ${{ env.build_dir }} --target embree -j 2 + - name: Build run: cmake --build ${{ env.build_dir }} -j ${{ steps.cpu-cores.outputs.count }} diff --git a/cmake/lagrange/lagrange_limit_parallelism.cmake b/cmake/lagrange/lagrange_limit_parallelism.cmake index 1f67ed19..ac00e45b 100644 --- a/cmake/lagrange/lagrange_limit_parallelism.cmake +++ b/cmake/lagrange/lagrange_limit_parallelism.cmake @@ -53,7 +53,7 @@ function(lagrange_limit_parallelism) message(STATUS "Parallelism: Link job memory budget: ${_link_memory} MB (${_build_type})") message(STATUS "Parallelism: Limiting link pool to ${num_link_jobs}") - set_property(GLOBAL PROPERTY JOB_POOLS pool-link=${num_link_jobs} pool-heavy-compile=1) + set_property(GLOBAL PROPERTY JOB_POOLS pool-link=${num_link_jobs} pool-heavy-compile=2) set(CMAKE_JOB_POOL_LINK "pool-link" CACHE STRING "Job pool for linking" FORCE) endif() endfunction() diff --git a/cmake/recipes/external/embree.cmake b/cmake/recipes/external/embree.cmake index 10adf902..52b31c8a 100644 --- a/cmake/recipes/external/embree.cmake +++ b/cmake/recipes/external/embree.cmake @@ -133,6 +133,7 @@ function(embree_import_target) # Linux when the project's memory-aware parallelism policy is enabled. if(LAGRANGE_LIMIT_PARALLELISM AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_GENERATOR MATCHES "^Ninja") set_property(TARGET embree PROPERTY JOB_POOL_COMPILE pool-heavy-compile) + set_property(TARGET embree PROPERTY JOB_POOL_LINK pool-link) endif() # Disable warnings From d2adc86a1d25d9093acc13af7c4bee9caca2acf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Thu, 10 Sep 2026 10:16:28 -0700 Subject: [PATCH 11/22] Serialize LLVM sanitizer Embree compilation --- cmake/lagrange/lagrange_limit_parallelism.cmake | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cmake/lagrange/lagrange_limit_parallelism.cmake b/cmake/lagrange/lagrange_limit_parallelism.cmake index ac00e45b..5f543e61 100644 --- a/cmake/lagrange/lagrange_limit_parallelism.cmake +++ b/cmake/lagrange/lagrange_limit_parallelism.cmake @@ -44,6 +44,14 @@ function(lagrange_limit_parallelism) set(num_link_jobs 1) endif() + set(num_heavy_compile_jobs 2) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" + AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND USE_SANITIZER MATCHES "([Aa]ddress)" + AND USE_SANITIZER MATCHES "([Uu]ndefined)") + set(num_heavy_compile_jobs 1) + endif() + if(CMAKE_SCRIPT_MODE_FILE) # Script mode: echo the number of physical cores for use as the -j flag in Jenkins. # Link parallelism is handled separately via Ninja job pools at configure time. @@ -52,8 +60,12 @@ function(lagrange_limit_parallelism) message(STATUS "Parallelism: Total physical memory: ${TOTAL_PHYSICAL_MEMORY} MB") message(STATUS "Parallelism: Link job memory budget: ${_link_memory} MB (${_build_type})") message(STATUS "Parallelism: Limiting link pool to ${num_link_jobs}") + message(STATUS "Parallelism: Limiting heavy compile pool to ${num_heavy_compile_jobs}") - set_property(GLOBAL PROPERTY JOB_POOLS pool-link=${num_link_jobs} pool-heavy-compile=2) + set_property(GLOBAL PROPERTY JOB_POOLS + pool-link=${num_link_jobs} + pool-heavy-compile=${num_heavy_compile_jobs} + ) set(CMAKE_JOB_POOL_LINK "pool-link" CACHE STRING "Job pool for linking" FORCE) endif() endfunction() From 5965a38c21b3f659d8d5170a10d304b4d76dce9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Thu, 10 Sep 2026 10:44:36 -0700 Subject: [PATCH 12/22] Build OpenVDB explicit instantiations in heavy pool --- .github/workflows/continuous.yaml | 6 +++++- cmake/recipes/external/OpenVDB.cmake | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index eae69793..70a5af09 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -180,7 +180,7 @@ jobs: -DLAGRANGE_LIMIT_PARALLELISM=ON \ -DOPENVDB_CORE_SHARED=ON \ -DOPENVDB_CORE_STATIC=OFF \ - -DUSE_EXPLICIT_INSTANTIATION=OFF \ + -DUSE_EXPLICIT_INSTANTIATION=ON \ -DLAGRANGE_POLYSCOPE_MOCK_BACKEND=ON \ -DEMBREE_MAX_ISA=${{ env.embree_max_isa }} \ -DUSE_SANITIZER="${{ matrix.sanitizer }}" @@ -189,6 +189,10 @@ jobs: if: runner.os == 'Linux' run: cmake --build ${{ env.build_dir }} --target embree -j 2 + - name: Build OpenVDB + if: runner.os == 'Linux' + run: cmake --build ${{ env.build_dir }} --target openvdb_shared -j 2 + - name: Build run: cmake --build ${{ env.build_dir }} -j ${{ steps.cpu-cores.outputs.count }} diff --git a/cmake/recipes/external/OpenVDB.cmake b/cmake/recipes/external/OpenVDB.cmake index c0bae13e..b811af08 100644 --- a/cmake/recipes/external/OpenVDB.cmake +++ b/cmake/recipes/external/OpenVDB.cmake @@ -176,6 +176,12 @@ function(openvdb_import_target) # Inject real Boost dependencies instead of dummy Boost:headers one foreach(name IN ITEMS openvdb_static openvdb_shared) if(TARGET ${name}) + if(LAGRANGE_LIMIT_PARALLELISM + AND CMAKE_SYSTEM_NAME STREQUAL "Linux" + AND CMAKE_GENERATOR MATCHES "^Ninja") + set_property(TARGET ${name} PROPERTY JOB_POOL_COMPILE pool-heavy-compile) + set_property(TARGET ${name} PROPERTY JOB_POOL_LINK pool-link) + endif() target_link_libraries(${name} PUBLIC Boost::algorithm From 9445cbf12342b6226f202e592290673167ef7e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Thu, 10 Sep 2026 10:51:08 -0700 Subject: [PATCH 13/22] Scope heavy compile pools to GitHub Actions --- .github/workflows/continuous.yaml | 1 + CMakeLists.txt | 4 +++ .../lagrange/lagrange_limit_parallelism.cmake | 27 ++++++++++--------- cmake/recipes/external/OpenVDB.cmake | 3 +-- cmake/recipes/external/embree.cmake | 5 ++-- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 70a5af09..50dffecb 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -178,6 +178,7 @@ jobs: -DLAGRANGE_JENKINS=ON \ -DLAGRANGE_ALL=ON \ -DLAGRANGE_LIMIT_PARALLELISM=ON \ + -DLAGRANGE_LIMIT_GITHUB_ACTIONS_PARALLELISM=ON \ -DOPENVDB_CORE_SHARED=ON \ -DOPENVDB_CORE_STATIC=OFF \ -DUSE_EXPLICIT_INSTANTIATION=ON \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 56321d21..eb333513 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -258,6 +258,7 @@ option(LAGRANGE_EXAMPLES "Build all examples" option(LAGRANGE_FMT_EIGEN_FIX "Avoid MSVC C1001 error releated to fmt and eigen" ${LAGRANGE_FMT_EIGEN_FIX_DEFAULT}) option(LAGRANGE_INSTALL "Enable installation" ${LAGRANGE_TOPLEVEL_PROJECT}) option(LAGRANGE_LIMIT_PARALLELISM "Limit parallelism according to available cpu/memory" OFF) +option(LAGRANGE_LIMIT_GITHUB_ACTIONS_PARALLELISM "Limit heavy compile jobs on GitHub Actions" OFF) option(LAGRANGE_MORE_WARNINGS "Increase the level of warnings when compiling" OFF) option(LAGRANGE_PROPAGATE_WARNINGS "Propagate warning flags to dependent targets" ON) option(LAGRANGE_PERFORMANCE_TESTS "Build all performance tests" OFF) @@ -441,6 +442,9 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) if(LAGRANGE_LIMIT_PARALLELISM) lagrange_limit_parallelism() endif() +if(LAGRANGE_LIMIT_GITHUB_ACTIONS_PARALLELISM) + lagrange_limit_github_actions_parallelism() +endif() set(TBB_ENABLE_WASM_THREADS ${LAGRANGE_USE_WASM_THREADS}) diff --git a/cmake/lagrange/lagrange_limit_parallelism.cmake b/cmake/lagrange/lagrange_limit_parallelism.cmake index 5f543e61..2cf9c7e5 100644 --- a/cmake/lagrange/lagrange_limit_parallelism.cmake +++ b/cmake/lagrange/lagrange_limit_parallelism.cmake @@ -44,14 +44,6 @@ function(lagrange_limit_parallelism) set(num_link_jobs 1) endif() - set(num_heavy_compile_jobs 2) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux" - AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" - AND USE_SANITIZER MATCHES "([Aa]ddress)" - AND USE_SANITIZER MATCHES "([Uu]ndefined)") - set(num_heavy_compile_jobs 1) - endif() - if(CMAKE_SCRIPT_MODE_FILE) # Script mode: echo the number of physical cores for use as the -j flag in Jenkins. # Link parallelism is handled separately via Ninja job pools at configure time. @@ -60,16 +52,25 @@ function(lagrange_limit_parallelism) message(STATUS "Parallelism: Total physical memory: ${TOTAL_PHYSICAL_MEMORY} MB") message(STATUS "Parallelism: Link job memory budget: ${_link_memory} MB (${_build_type})") message(STATUS "Parallelism: Limiting link pool to ${num_link_jobs}") - message(STATUS "Parallelism: Limiting heavy compile pool to ${num_heavy_compile_jobs}") - set_property(GLOBAL PROPERTY JOB_POOLS - pool-link=${num_link_jobs} - pool-heavy-compile=${num_heavy_compile_jobs} - ) + set_property(GLOBAL PROPERTY JOB_POOLS pool-link=${num_link_jobs}) set(CMAKE_JOB_POOL_LINK "pool-link" CACHE STRING "Job pool for linking" FORCE) endif() endfunction() +function(lagrange_limit_github_actions_parallelism) + set(num_heavy_compile_jobs 2) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" + AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND USE_SANITIZER MATCHES "([Aa]ddress)" + AND USE_SANITIZER MATCHES "([Uu]ndefined)") + set(num_heavy_compile_jobs 1) + endif() + + message(STATUS "Parallelism: Limiting heavy compile pool to ${num_heavy_compile_jobs}") + set_property(GLOBAL APPEND PROPERTY JOB_POOLS pool-heavy-compile=${num_heavy_compile_jobs}) +endfunction() + # If this file is run in script mode, it echoes the number of physical cores for use as # the -j flag for cmake --build and ctest. Link parallelism is not relevant here — it is # enforced by Ninja job pools set during the configure step. diff --git a/cmake/recipes/external/OpenVDB.cmake b/cmake/recipes/external/OpenVDB.cmake index b811af08..cbf41161 100644 --- a/cmake/recipes/external/OpenVDB.cmake +++ b/cmake/recipes/external/OpenVDB.cmake @@ -176,11 +176,10 @@ function(openvdb_import_target) # Inject real Boost dependencies instead of dummy Boost:headers one foreach(name IN ITEMS openvdb_static openvdb_shared) if(TARGET ${name}) - if(LAGRANGE_LIMIT_PARALLELISM + if(LAGRANGE_LIMIT_GITHUB_ACTIONS_PARALLELISM AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_GENERATOR MATCHES "^Ninja") set_property(TARGET ${name} PROPERTY JOB_POOL_COMPILE pool-heavy-compile) - set_property(TARGET ${name} PROPERTY JOB_POOL_LINK pool-link) endif() target_link_libraries(${name} PUBLIC diff --git a/cmake/recipes/external/embree.cmake b/cmake/recipes/external/embree.cmake index 52b31c8a..e159fc6e 100644 --- a/cmake/recipes/external/embree.cmake +++ b/cmake/recipes/external/embree.cmake @@ -131,9 +131,10 @@ function(embree_import_target) # Embree has several memory-intensive translation units. Limit their concurrency on # Linux when the project's memory-aware parallelism policy is enabled. - if(LAGRANGE_LIMIT_PARALLELISM AND CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_GENERATOR MATCHES "^Ninja") + if(LAGRANGE_LIMIT_GITHUB_ACTIONS_PARALLELISM + AND CMAKE_SYSTEM_NAME STREQUAL "Linux" + AND CMAKE_GENERATOR MATCHES "^Ninja") set_property(TARGET embree PROPERTY JOB_POOL_COMPILE pool-heavy-compile) - set_property(TARGET embree PROPERTY JOB_POOL_LINK pool-link) endif() # Disable warnings From ea8d0ec22a64ba97fcf40cdbc0e3ad36484da3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Fri, 11 Sep 2026 09:58:08 -0700 Subject: [PATCH 14/22] Show more disk space. --- .github/workflows/continuous.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 50dffecb..d3b76c68 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -187,13 +187,25 @@ jobs: -DUSE_SANITIZER="${{ matrix.sanitizer }}" - name: Build Embree - if: runner.os == 'Linux' + if: runner.os == 'Linux' || matrix.compiler == 'llvm' run: cmake --build ${{ env.build_dir }} --target embree -j 2 + - name: Show disk space + if: always() + run: | + echo "disk usage:" + df -h + - name: Build OpenVDB - if: runner.os == 'Linux' + if: runner.os == 'Linux' || matrix.compiler == 'llvm' run: cmake --build ${{ env.build_dir }} --target openvdb_shared -j 2 + - name: Show disk space + if: always() + run: | + echo "disk usage:" + df -h + - name: Build run: cmake --build ${{ env.build_dir }} -j ${{ steps.cpu-cores.outputs.count }} From 6da7181cb0af24643320798e650b74fe6ba72b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Fri, 11 Sep 2026 12:30:41 -0700 Subject: [PATCH 15/22] Free disk space on macOS. --- .github/workflows/continuous.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index d3b76c68..7b802084 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -43,7 +43,7 @@ jobs: - { os: macos-15, compiler: llvm, config: Debug, sanitizer: "Address;Undefined" } - { os: macos-15, compiler: llvm, config: Debug, sanitizer: Thread } steps: - - name: Free Disk Space + - name: Free Disk Space (Linux) if: runner.os == 'Linux' uses: jlumbroso/free-disk-space@main with: @@ -60,6 +60,14 @@ jobs: docker-images: true swap-storage: true + - name: Free Disk Space (macOS) + if: runner.os == 'macOS' + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + - name: Set swap space if: runner.os == 'Linux' && matrix.compiler == 'gcc' && matrix.config == 'RelWithDebInfo' uses: pierotofy/set-swap-space@fc79b3f67fa8a838184ce84a674ca12238d2c761 From f64e320b803a97eb53e01ca48cd700a1815f861b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Fri, 11 Sep 2026 17:25:29 -0700 Subject: [PATCH 16/22] Reclaim macOS simulator and Android storage --- .github/workflows/continuous.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 7b802084..c0b7dea6 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -63,10 +63,21 @@ jobs: - name: Free Disk Space (macOS) if: runner.os == 'macOS' run: | - sudo rm -rf /usr/share/dotnet + echo "Disk usage before cleanup:" + df -h + sudo rm -rf /Library/Developer/CoreSimulator + sudo rm -rf "$HOME/Library/Developer/CoreSimulator" + sudo rm -rf /System/Library/AssetsV2/com_apple_MobileAsset_iOSSimulatorRuntime + sudo rm -rf /System/Library/AssetsV2/com_apple_MobileAsset_xrOSSimulatorRuntime + sudo rm -rf /System/Library/AssetsV2/com_apple_MobileAsset_watchOSSimulatorRuntime + sudo rm -rf /System/Library/AssetsV2/com_apple_MobileAsset_appleTVOSSimulatorRuntime + sudo rm -rf "$ANDROID_HOME" + sudo rm -rf /usr/local/share/dotnet sudo rm -rf /opt/ghc sudo rm -rf "/usr/local/share/boost" sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk usage after cleanup:" + df -h - name: Set swap space if: runner.os == 'Linux' && matrix.compiler == 'gcc' && matrix.config == 'RelWithDebInfo' From 3e5fedb7aa205f6fad181db7f83f53cce67dd6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Sat, 12 Sep 2026 07:57:59 -0700 Subject: [PATCH 17/22] Use compressed split DWARF for GCC sanitizer CI --- .github/workflows/continuous.yaml | 3 ++- CMakeLists.txt | 1 + cmake/lagrange/lagrange_global_flags.cmake | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index c0b7dea6..64be9f5e 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -31,7 +31,7 @@ jobs: matrix: include: # Linux release builds exercise tests disabled in Debug mode, and ASan also enables LSan. - - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: "Address;Undefined" } + - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: "Address;Undefined", split_debug: ON } - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: Thread } - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: "Address;Undefined" } - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: Thread } @@ -202,6 +202,7 @@ jobs: -DOPENVDB_CORE_STATIC=OFF \ -DUSE_EXPLICIT_INSTANTIATION=ON \ -DLAGRANGE_POLYSCOPE_MOCK_BACKEND=ON \ + -DLAGRANGE_USE_SPLIT_DEBUG_INFO=${{ matrix.split_debug }} \ -DEMBREE_MAX_ISA=${{ env.embree_max_isa }} \ -DUSE_SANITIZER="${{ matrix.sanitizer }}" diff --git a/CMakeLists.txt b/CMakeLists.txt index eb333513..9d2ed5cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,6 +264,7 @@ option(LAGRANGE_PROPAGATE_WARNINGS "Propagate warning flags to dependent ta option(LAGRANGE_PERFORMANCE_TESTS "Build all performance tests" OFF) option(LAGRANGE_UNIT_TESTS "Build all unit tests" ${LAGRANGE_TOPLEVEL_PROJECT}) option(LAGRANGE_USE_PCH "Enable precompiled headers" OFF) +option(LAGRANGE_USE_SPLIT_DEBUG_INFO "Store debug information in separate compressed files" OFF) option(LAGRANGE_USE_WASM_EXCEPTIONS "Use -fwasm-exception flag with Emscripten" ON) option(LAGRANGE_USE_WASM_THREADS "Enable threads (-pthread) with Emscripten" ON) option(LAGRANGE_WITH_TRACY "Build tracy client with Lagrange" OFF) diff --git a/cmake/lagrange/lagrange_global_flags.cmake b/cmake/lagrange/lagrange_global_flags.cmake index 4f2db768..13aa95c7 100644 --- a/cmake/lagrange/lagrange_global_flags.cmake +++ b/cmake/lagrange/lagrange_global_flags.cmake @@ -49,6 +49,11 @@ else() add_compile_options(${LAGRANGE_GLOBAL_FLAGS}) endif() +if(LAGRANGE_USE_SPLIT_DEBUG_INFO) + add_compile_options(-gsplit-dwarf -gz=zstd) + add_link_options(-Wl,--compress-debug-sections=zstd) +endif() + if(LAGRANGE_WITH_TRACY) include(lagrange_filter_flags) set(LAGRANGE_GLOBAL_FLAGS From b25471d696949f2a377c0e4fbc5c831430dba048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Wed, 16 Sep 2026 03:02:13 +0000 Subject: [PATCH 18/22] Fix Ubuntu UBSan failures --- cmake/lagrange/lagrange_set_sanitizers.cmake | 12 ++++ cmake/recipes/external/embree.cmake | 19 ++++++ cmake/recipes/external/ufbx.cmake | 4 ++ modules/core/src/SurfaceMesh.cpp | 63 +++++++++++--------- 4 files changed, 70 insertions(+), 28 deletions(-) diff --git a/cmake/lagrange/lagrange_set_sanitizers.cmake b/cmake/lagrange/lagrange_set_sanitizers.cmake index 184e7cd7..93a46985 100644 --- a/cmake/lagrange/lagrange_set_sanitizers.cmake +++ b/cmake/lagrange/lagrange_set_sanitizers.cmake @@ -12,6 +12,18 @@ include(sanitizers) +# Some bundled dependencies intentionally use operations rejected by UBSan (for example, +# function-pointer casts in Embree). Keep the sanitizer enabled for Lagrange while allowing +# concrete third-party targets to opt out without propagating the flag to their consumers. +function(lagrange_disable_ubsan_for_external target) + if(TARGET "${target}" AND USE_SANITIZER MATCHES "([Uu]ndefined)") + target_compile_options("${target}" PRIVATE + $<$:-fno-sanitize=undefined> + $<$:-fno-sanitize=undefined> + ) + endif() +endfunction() + if(MSVC AND USE_SANITIZER) # To use sanitizers with MSVC you need to disable incompatible options. See details here: # https://learn.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-170#ide-msbuild diff --git a/cmake/recipes/external/embree.cmake b/cmake/recipes/external/embree.cmake index e159fc6e..46081ec1 100644 --- a/cmake/recipes/external/embree.cmake +++ b/cmake/recipes/external/embree.cmake @@ -154,6 +154,25 @@ function(embree_import_target) target_compile_options(embree PRIVATE "-Wno-unused-but-set-variable") endif() + # Embree intentionally dispatches through ABI-compatible function pointers and uses + # type-punning downcasts internally. Exclude only its implementation from UBSan; consumers + # and Lagrange's raycasting code remain instrumented. + foreach(target IN ITEMS + embree + embree_sse42 + embree_avx + embree_avx2 + embree_avx512 + algorithms + lexers + math + simd + sys + tasking + ) + lagrange_disable_ubsan_for_external(${target}) + endforeach() + # Now we need to do some juggling to propagate the include directory properties # along with the `embree` target add_library(embree::embree INTERFACE IMPORTED GLOBAL) diff --git a/cmake/recipes/external/ufbx.cmake b/cmake/recipes/external/ufbx.cmake index 7ac2c981..d55b097a 100644 --- a/cmake/recipes/external/ufbx.cmake +++ b/cmake/recipes/external/ufbx.cmake @@ -32,6 +32,10 @@ target_include_directories(ufbx PUBLIC set_target_properties(ufbx PROPERTIES FOLDER third_party) set_target_properties(ufbx PROPERTIES POSITION_INDEPENDENT_CODE ON) +# ufbx's bit reader intentionally shifts by the encoded bit count before validating it. +# UBSan diagnoses this in valid FBX inputs, so exclude only the bundled implementation. +lagrange_disable_ubsan_for_external(ufbx) + # Install rules set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME ufbx) install(DIRECTORY ${ufbx_SOURCE_DIR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/modules/core/src/SurfaceMesh.cpp b/modules/core/src/SurfaceMesh.cpp index 9874f50d..41671431 100644 --- a/modules/core/src/SurfaceMesh.cpp +++ b/modules/core/src/SurfaceMesh.cpp @@ -38,6 +38,7 @@ // clang-format on #include +#include #include #include @@ -3697,40 +3698,46 @@ SurfaceMesh to_surface_mesh(const SurfaceMeshInfo& info) if (ai->is_indexed) { switch (value_type) { -#define LA_X_restore_indexed(_, ValueType) \ - case make_attribute_value_type(): { \ - auto values = span( \ - reinterpret_cast(ai->values_bytes.data()), \ - ai->values_bytes.size() / sizeof(ValueType)); \ - auto indices = span( \ - reinterpret_cast(ai->indices_bytes.data()), \ - ai->indices_bytes.size() / sizeof(Index)); \ - id = mesh.template create_attribute_internal( \ - ai->name, \ - element, \ - usage, \ - ai->values_num_channels, \ - values, \ - indices); \ - break; \ +#define LA_X_restore_indexed(_, ValueType) \ + case make_attribute_value_type(): { \ + la_runtime_assert(ai->values_bytes.size() % sizeof(ValueType) == 0); \ + la_runtime_assert(ai->indices_bytes.size() % sizeof(Index) == 0); \ + std::vector values(ai->values_bytes.size() / sizeof(ValueType)); \ + std::vector indices(ai->indices_bytes.size() / sizeof(Index)); \ + if (!values.empty()) { \ + std::memcpy(values.data(), ai->values_bytes.data(), ai->values_bytes.size()); \ + } \ + if (!indices.empty()) { \ + std::memcpy(indices.data(), ai->indices_bytes.data(), ai->indices_bytes.size()); \ + } \ + id = mesh.template create_attribute_internal( \ + ai->name, \ + element, \ + usage, \ + ai->values_num_channels, \ + span(values.data(), values.size()), \ + span(indices.data(), indices.size())); \ + break; \ } LA_ATTRIBUTE_X(restore_indexed, 0) #undef LA_X_restore_indexed } } else { switch (value_type) { -#define LA_X_restore_attr(_, ValueType) \ - case make_attribute_value_type(): { \ - auto data = span( \ - reinterpret_cast(ai->data_bytes.data()), \ - ai->data_bytes.size() / sizeof(ValueType)); \ - id = mesh.template create_attribute_internal( \ - ai->name, \ - element, \ - usage, \ - ai->num_channels, \ - data); \ - break; \ +#define LA_X_restore_attr(_, ValueType) \ + case make_attribute_value_type(): { \ + la_runtime_assert(ai->data_bytes.size() % sizeof(ValueType) == 0); \ + std::vector data(ai->data_bytes.size() / sizeof(ValueType)); \ + if (!data.empty()) { \ + std::memcpy(data.data(), ai->data_bytes.data(), ai->data_bytes.size()); \ + } \ + id = mesh.template create_attribute_internal( \ + ai->name, \ + element, \ + usage, \ + ai->num_channels, \ + span(data.data(), data.size())); \ + break; \ } LA_ATTRIBUTE_X(restore_attr, 0) #undef LA_X_restore_attr From 23caef05be30dd5cf302b9789a5db327e0ce5697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Je=CC=81re=CC=81mie=20Dumas?= Date: Thu, 1 Oct 2026 02:15:35 +0000 Subject: [PATCH 19/22] Narrow UBSan workarounds and test unaligned mesh restoration --- cmake/lagrange/lagrange_set_sanitizers.cmake | 12 -- cmake/recipes/external/embree.cmake | 37 +++--- cmake/recipes/external/ufbx.cmake | 8 +- modules/core/tests/test_surface_mesh_info.cpp | 117 ++++++++++++++++++ 4 files changed, 141 insertions(+), 33 deletions(-) create mode 100644 modules/core/tests/test_surface_mesh_info.cpp diff --git a/cmake/lagrange/lagrange_set_sanitizers.cmake b/cmake/lagrange/lagrange_set_sanitizers.cmake index 93a46985..184e7cd7 100644 --- a/cmake/lagrange/lagrange_set_sanitizers.cmake +++ b/cmake/lagrange/lagrange_set_sanitizers.cmake @@ -12,18 +12,6 @@ include(sanitizers) -# Some bundled dependencies intentionally use operations rejected by UBSan (for example, -# function-pointer casts in Embree). Keep the sanitizer enabled for Lagrange while allowing -# concrete third-party targets to opt out without propagating the flag to their consumers. -function(lagrange_disable_ubsan_for_external target) - if(TARGET "${target}" AND USE_SANITIZER MATCHES "([Uu]ndefined)") - target_compile_options("${target}" PRIVATE - $<$:-fno-sanitize=undefined> - $<$:-fno-sanitize=undefined> - ) - endif() -endfunction() - if(MSVC AND USE_SANITIZER) # To use sanitizers with MSVC you need to disable incompatible options. See details here: # https://learn.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-170#ide-msbuild diff --git a/cmake/recipes/external/embree.cmake b/cmake/recipes/external/embree.cmake index 46081ec1..6e926f93 100644 --- a/cmake/recipes/external/embree.cmake +++ b/cmake/recipes/external/embree.cmake @@ -154,24 +154,25 @@ function(embree_import_target) target_compile_options(embree PRIVATE "-Wno-unused-but-set-variable") endif() - # Embree intentionally dispatches through ABI-compatible function pointers and uses - # type-punning downcasts internally. Exclude only its implementation from UBSan; consumers - # and Lagrange's raycasting code remain instrumented. - foreach(target IN ITEMS - embree - embree_sse42 - embree_avx - embree_avx2 - embree_avx512 - algorithms - lexers - math - simd - sys - tasking - ) - lagrange_disable_ubsan_for_external(${target}) - endforeach() + # Suppress kernel dispatch function casts and AccelSet downcasts, not all UBSan checks. + # Keep exclusions private; GCC has no function-pointer sanitizer. + if(USE_SANITIZER MATCHES "([Uu]ndefined)") + foreach(target IN ITEMS + embree + embree_sse42 + embree_avx + embree_avx2 + embree_avx512 + embree_apx + ) + if(TARGET ${target}) + target_compile_options(${target} PRIVATE + $<$:-fno-sanitize=vptr> + $<$:-fno-sanitize=function> + ) + endif() + endforeach() + endif() # Now we need to do some juggling to propagate the include directory properties # along with the `embree` target diff --git a/cmake/recipes/external/ufbx.cmake b/cmake/recipes/external/ufbx.cmake index d55b097a..b5d7d522 100644 --- a/cmake/recipes/external/ufbx.cmake +++ b/cmake/recipes/external/ufbx.cmake @@ -32,9 +32,11 @@ target_include_directories(ufbx PUBLIC set_target_properties(ufbx PROPERTIES FOLDER third_party) set_target_properties(ufbx PROPERTIES POSITION_INDEPENDENT_CODE ON) -# ufbx's bit reader intentionally shifts by the encoded bit count before validating it. -# UBSan diagnoses this in valid FBX inputs, so exclude only the bundled implementation. -lagrange_disable_ubsan_for_external(ufbx) +# Select UBSan-safe paths explicitly: GCC 13 has no UBSan detection macro. +# https://github.com/ufbx/ufbx/issues/231 +if(USE_SANITIZER MATCHES "([Uu]ndefined)") + target_compile_definitions(ufbx PRIVATE UFBX_UBSAN) +endif() # Install rules set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME ufbx) diff --git a/modules/core/tests/test_surface_mesh_info.cpp b/modules/core/tests/test_surface_mesh_info.cpp new file mode 100644 index 00000000..895fde9f --- /dev/null +++ b/modules/core/tests/test_surface_mesh_info.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2026 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace { + +template +void check_unaligned_attribute_round_trip() +{ + lagrange::SurfaceMesh mesh; + mesh.add_vertices(3, {0, 0, 0, 1, 0, 0, 0, 1, 0}); + mesh.add_triangle(0, 1, 2); + + // Include 64-bit non-indexed values, scalar-valued indexed data, and mesh indices. + const std::vector labels = {11, 22, 33}; + mesh.template create_attribute( + "labels", + lagrange::AttributeElement::Vertex, + lagrange::AttributeUsage::Scalar, + 1, + lagrange::span(labels.data(), labels.size())); + + const std::vector uv_values = {0, 0, 1, 0, 0, 1}; + const std::vector uv_indices = {2, 0, 1}; + mesh.template create_attribute( + "uv", + lagrange::AttributeElement::Indexed, + lagrange::AttributeUsage::UV, + 2, + lagrange::span(uv_values.data(), uv_values.size()), + lagrange::span(uv_indices.data(), uv_indices.size())); + + bool unalign_data = false; + bool unalign_values = false; + bool unalign_indices = false; + SECTION("non-indexed data") + { + unalign_data = true; + } + SECTION("indexed values") + { + unalign_values = true; + } + SECTION("indexed indices") + { + unalign_indices = true; + } + SECTION("all attribute buffers") + { + unalign_data = unalign_values = unalign_indices = true; + } + + auto info = lagrange::internal::from_surface_mesh(mesh); + std::vector> storage; + storage.reserve(info.attributes.size() * 3); + auto unaligned_copy = [&](lagrange::span bytes) { + if (bytes.empty()) return bytes; + auto& buffer = storage.emplace_back( + (bytes.size() + sizeof(std::max_align_t)) / sizeof(std::max_align_t)); + auto* data = reinterpret_cast(buffer.data()) + 1; + REQUIRE(reinterpret_cast(data) % alignof(std::max_align_t) == 1); + std::memcpy(data, bytes.data(), bytes.size()); + return lagrange::span(data, bytes.size()); + }; + + // Byte spans need not be aligned for their value types. Run UBSan with + // halt_on_error=1: x86 can otherwise accept the old unaligned typed reads. + for (auto& attr : info.attributes) { + if (unalign_data) attr.data_bytes = unaligned_copy(attr.data_bytes); + if (unalign_values) attr.values_bytes = unaligned_copy(attr.values_bytes); + if (unalign_indices) attr.indices_bytes = unaligned_copy(attr.indices_bytes); + } + + auto result = lagrange::internal::to_surface_mesh(info); + storage.clear(); // Restoration must own its attribute data, not retain the byte spans. + lagrange::testing::check_meshes_equal(mesh, result); +} + +} // namespace + +TEST_CASE("SurfaceMeshInfo: unaligned byte round-trip", "[core][surface_mesh_info]") +{ + SECTION("float, uint32_t") + { + check_unaligned_attribute_round_trip(); + } + SECTION("double, uint32_t") + { + check_unaligned_attribute_round_trip(); + } + SECTION("float, uint64_t") + { + check_unaligned_attribute_round_trip(); + } + SECTION("double, uint64_t") + { + check_unaligned_attribute_round_trip(); + } +} From 9ec509c6bc95ee8c358672d7dc2ba7b011d94a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Dumas?= Date: Thu, 1 Oct 2026 04:37:00 +0000 Subject: [PATCH 20/22] Fail CI on UBSan reports and avoid restore copies Gate recoverable UBSan reports with reviewed third-party suppressions, probe split-debug flags, and copy restored bytes directly into attribute storage. --- .github/ubsan.suppressions.ini | 17 +++++ .github/workflows/continuous.yaml | 2 + cmake/lagrange/lagrange_global_flags.cmake | 19 +++++- modules/core/src/SurfaceMesh.cpp | 75 ++++++++++++---------- 4 files changed, 77 insertions(+), 36 deletions(-) create mode 100644 .github/ubsan.suppressions.ini diff --git a/.github/ubsan.suppressions.ini b/.github/ubsan.suppressions.ini new file mode 100644 index 00000000..ce113c64 --- /dev/null +++ b/.github/ubsan.suppressions.ini @@ -0,0 +1,17 @@ +# UBSan suppressions for known third-party issues. +# Misha and Shape Gradient Domain use matrix objects in unconstructed heap storage. +vptr:/misha/ +vptr:/shape_gradient_domain/ +# TBB task dispatch reports invalid vptrs for task objects. +vptr:/tbb/ +# Misha packs sparse-matrix entries with #pragma pack(2), so their double values are unaligned. +alignment:/texture_signal_processing/ +alignment:Eigen::Triplet< +# PoissonRecon passes null pointers with zero sizes to memcpy. +nonnull-attribute:/poissonrecon/ +# TinyEXR left-shifts signed 64-bit values. +shift-base:/tinyexr/ +# Clang reports indirect calls through mismatched function types in these dependencies on macOS. +function:/catch2/ +function:/instant-meshes-core/ +function:/openvdb/ diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 64be9f5e..709cb49f 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -26,6 +26,8 @@ jobs: runs-on: ${{ matrix.os }} env: CCACHE_COMPRESSLEVEL: 5 + # Fail on UBSan diagnostics during test discovery and execution, except reviewed dependencies. + UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1:suppressions=${{ github.workspace }}/.github/ubsan.suppressions.ini strategy: fail-fast: false matrix: diff --git a/cmake/lagrange/lagrange_global_flags.cmake b/cmake/lagrange/lagrange_global_flags.cmake index 13aa95c7..f9dd670a 100644 --- a/cmake/lagrange/lagrange_global_flags.cmake +++ b/cmake/lagrange/lagrange_global_flags.cmake @@ -50,8 +50,23 @@ else() endif() if(LAGRANGE_USE_SPLIT_DEBUG_INFO) - add_compile_options(-gsplit-dwarf -gz=zstd) - add_link_options(-Wl,--compress-debug-sections=zstd) + include(lagrange_filter_flags) + set(LAGRANGE_SPLIT_DEBUG_FLAGS + -gsplit-dwarf + -gz=zstd + ) + lagrange_filter_flags(LAGRANGE_SPLIT_DEBUG_FLAGS) + message(STATUS "Adding split debug flags: ${LAGRANGE_SPLIT_DEBUG_FLAGS}") + add_compile_options(${LAGRANGE_SPLIT_DEBUG_FLAGS}) + + # lagrange_filter_flags only checks compile flags; MSVC may ignore GNU linker flags. + if(NOT MSVC) + include(CheckLinkerFlag) + check_linker_flag(CXX "-Wl,--compress-debug-sections=zstd" LAGRANGE_HAS_ZSTD_DEBUG_LINK) + endif() + if(LAGRANGE_HAS_ZSTD_DEBUG_LINK) + add_link_options(-Wl,--compress-debug-sections=zstd) + endif() endif() if(LAGRANGE_WITH_TRACY) diff --git a/modules/core/src/SurfaceMesh.cpp b/modules/core/src/SurfaceMesh.cpp index 41671431..e9d02b43 100644 --- a/modules/core/src/SurfaceMesh.cpp +++ b/modules/core/src/SurfaceMesh.cpp @@ -3657,6 +3657,26 @@ SurfaceMeshInfo from_surface_mesh(const SurfaceMesh& mesh) return info; } +namespace { + +// Copy possibly unaligned bytes directly into owned typed attribute storage. +template +void copy_attribute_bytes(Attribute& attr, span bytes, bool resize) +{ + if (bytes.empty()) return; + const size_t element_size = attr.get_num_channels() * sizeof(ValueType); + la_runtime_assert(bytes.size() % element_size == 0); + const size_t num_elements = bytes.size() / element_size; + if (resize) { + attr.resize_elements(num_elements); + } else { + la_runtime_assert(attr.get_num_elements() == num_elements); + } + std::memcpy(attr.ref_all().data(), bytes.data(), bytes.size()); +} + +} // namespace + template SurfaceMesh to_surface_mesh(const SurfaceMeshInfo& info) { @@ -3698,46 +3718,33 @@ SurfaceMesh to_surface_mesh(const SurfaceMeshInfo& info) if (ai->is_indexed) { switch (value_type) { -#define LA_X_restore_indexed(_, ValueType) \ - case make_attribute_value_type(): { \ - la_runtime_assert(ai->values_bytes.size() % sizeof(ValueType) == 0); \ - la_runtime_assert(ai->indices_bytes.size() % sizeof(Index) == 0); \ - std::vector values(ai->values_bytes.size() / sizeof(ValueType)); \ - std::vector indices(ai->indices_bytes.size() / sizeof(Index)); \ - if (!values.empty()) { \ - std::memcpy(values.data(), ai->values_bytes.data(), ai->values_bytes.size()); \ - } \ - if (!indices.empty()) { \ - std::memcpy(indices.data(), ai->indices_bytes.data(), ai->indices_bytes.size()); \ - } \ - id = mesh.template create_attribute_internal( \ - ai->name, \ - element, \ - usage, \ - ai->values_num_channels, \ - span(values.data(), values.size()), \ - span(indices.data(), indices.size())); \ - break; \ +#define LA_X_restore_indexed(_, ValueType) \ + case make_attribute_value_type(): { \ + id = mesh.template create_attribute_internal( \ + ai->name, \ + element, \ + usage, \ + ai->values_num_channels); \ + auto& attr = mesh.m_attributes->template write_indexed(id); \ + copy_attribute_bytes(attr.values(), ai->values_bytes, true); \ + copy_attribute_bytes(attr.indices(), ai->indices_bytes, false); \ + break; \ } LA_ATTRIBUTE_X(restore_indexed, 0) #undef LA_X_restore_indexed } } else { switch (value_type) { -#define LA_X_restore_attr(_, ValueType) \ - case make_attribute_value_type(): { \ - la_runtime_assert(ai->data_bytes.size() % sizeof(ValueType) == 0); \ - std::vector data(ai->data_bytes.size() / sizeof(ValueType)); \ - if (!data.empty()) { \ - std::memcpy(data.data(), ai->data_bytes.data(), ai->data_bytes.size()); \ - } \ - id = mesh.template create_attribute_internal( \ - ai->name, \ - element, \ - usage, \ - ai->num_channels, \ - span(data.data(), data.size())); \ - break; \ +#define LA_X_restore_attr(_, ValueType) \ + case make_attribute_value_type(): { \ + id = mesh.template create_attribute_internal( \ + ai->name, \ + element, \ + usage, \ + ai->num_channels); \ + auto& attr = mesh.m_attributes->template write(id); \ + copy_attribute_bytes(attr, ai->data_bytes, element == AttributeElement::Value); \ + break; \ } LA_ATTRIBUTE_X(restore_attr, 0) #undef LA_X_restore_attr From 47995f27082673b6270aa04c9bbec0a21222859b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Dumas?= Date: Thu, 1 Oct 2026 14:45:24 +0000 Subject: [PATCH 21/22] Defer UBSan CI and separate compile/link flag filters --- .github/ubsan.suppressions.ini | 17 ---------- .github/workflows/continuous.yaml | 14 ++++----- ...ke => lagrange_filter_compile_flags.cmake} | 2 +- .../lagrange/lagrange_filter_link_flags.cmake | 30 ++++++++++++++++++ cmake/lagrange/lagrange_global_flags.cmake | 31 +++++++++---------- cmake/lagrange/lagrange_warnings.cmake | 6 ++-- 6 files changed, 55 insertions(+), 45 deletions(-) delete mode 100644 .github/ubsan.suppressions.ini rename cmake/lagrange/{lagrange_filter_flags.cmake => lagrange_filter_compile_flags.cmake} (95%) create mode 100644 cmake/lagrange/lagrange_filter_link_flags.cmake diff --git a/.github/ubsan.suppressions.ini b/.github/ubsan.suppressions.ini deleted file mode 100644 index ce113c64..00000000 --- a/.github/ubsan.suppressions.ini +++ /dev/null @@ -1,17 +0,0 @@ -# UBSan suppressions for known third-party issues. -# Misha and Shape Gradient Domain use matrix objects in unconstructed heap storage. -vptr:/misha/ -vptr:/shape_gradient_domain/ -# TBB task dispatch reports invalid vptrs for task objects. -vptr:/tbb/ -# Misha packs sparse-matrix entries with #pragma pack(2), so their double values are unaligned. -alignment:/texture_signal_processing/ -alignment:Eigen::Triplet< -# PoissonRecon passes null pointers with zero sizes to memcpy. -nonnull-attribute:/poissonrecon/ -# TinyEXR left-shifts signed 64-bit values. -shift-base:/tinyexr/ -# Clang reports indirect calls through mismatched function types in these dependencies on macOS. -function:/catch2/ -function:/instant-meshes-core/ -function:/openvdb/ diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 709cb49f..96fcd092 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -26,23 +26,21 @@ jobs: runs-on: ${{ matrix.os }} env: CCACHE_COMPRESSLEVEL: 5 - # Fail on UBSan diagnostics during test discovery and execution, except reviewed dependencies. - UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1:suppressions=${{ github.workspace }}/.github/ubsan.suppressions.ini strategy: fail-fast: false matrix: include: # Linux release builds exercise tests disabled in Debug mode, and ASan also enables LSan. - - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: "Address;Undefined", split_debug: ON } + - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: Address, split_debug: ON } - { os: ubuntu-24.04, compiler: gcc, config: RelWithDebInfo, sanitizer: Thread } - - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: "Address;Undefined" } + - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: Address } - { os: ubuntu-24.04, compiler: llvm, config: Debug, sanitizer: Thread } - # Keep AppleClang release coverage on Intel and full sanitizer coverage on Apple Silicon. - - { os: macos-15-intel, compiler: apple, config: RelWithDebInfo, sanitizer: "Address;Undefined" } - - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: "Address;Undefined" } + # Keep AppleClang release coverage on Intel and ASan/TSan coverage on Apple Silicon. + - { os: macos-15-intel, compiler: apple, config: RelWithDebInfo, sanitizer: Address } + - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: Address } - { os: macos-15, compiler: apple, config: RelWithDebInfo, sanitizer: Thread } - - { os: macos-15, compiler: llvm, config: Debug, sanitizer: "Address;Undefined" } + - { os: macos-15, compiler: llvm, config: Debug, sanitizer: Address } - { os: macos-15, compiler: llvm, config: Debug, sanitizer: Thread } steps: - name: Free Disk Space (Linux) diff --git a/cmake/lagrange/lagrange_filter_flags.cmake b/cmake/lagrange/lagrange_filter_compile_flags.cmake similarity index 95% rename from cmake/lagrange/lagrange_filter_flags.cmake rename to cmake/lagrange/lagrange_filter_compile_flags.cmake index 5a9f574b..8e152585 100644 --- a/cmake/lagrange/lagrange_filter_flags.cmake +++ b/cmake/lagrange/lagrange_filter_compile_flags.cmake @@ -9,7 +9,7 @@ # OF ANY KIND, either express or implied. See the License for the specific language # governing permissions and limitations under the License. # -function(lagrange_filter_flags flags) +function(lagrange_filter_compile_flags flags) include(CheckCXXCompilerFlag) set(output_flags) foreach(FLAG IN ITEMS ${${flags}}) diff --git a/cmake/lagrange/lagrange_filter_link_flags.cmake b/cmake/lagrange/lagrange_filter_link_flags.cmake new file mode 100644 index 00000000..01b4c2e8 --- /dev/null +++ b/cmake/lagrange/lagrange_filter_link_flags.cmake @@ -0,0 +1,30 @@ +# +# Copyright 2026 Adobe. All rights reserved. +# This file is licensed to you under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. You may obtain a copy +# of the License at http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +# OF ANY KIND, either express or implied. See the License for the specific language +# governing permissions and limitations under the License. +# +function(lagrange_filter_link_flags flags) + include(CheckLinkerFlag) + set(output_flags) + foreach(FLAG IN ITEMS ${${flags}}) + string(REPLACE "=" "-" FLAG_VAR "${FLAG}") + if(NOT DEFINED IS_LINK_SUPPORTED_${FLAG_VAR}) + set(check_flags "${FLAG}") + if(MSVC) + # MSVC may ignore unsupported linker options instead of failing. + list(APPEND check_flags /WX) + endif() + check_linker_flag(CXX "${check_flags}" IS_LINK_SUPPORTED_${FLAG_VAR}) + endif() + if(IS_LINK_SUPPORTED_${FLAG_VAR}) + list(APPEND output_flags $<$:${FLAG}>) + endif() + endforeach() + set(${flags} ${output_flags} PARENT_SCOPE) +endfunction() diff --git a/cmake/lagrange/lagrange_global_flags.cmake b/cmake/lagrange/lagrange_global_flags.cmake index f9dd670a..f867e915 100644 --- a/cmake/lagrange/lagrange_global_flags.cmake +++ b/cmake/lagrange/lagrange_global_flags.cmake @@ -31,51 +31,50 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_compile_options(/FS) # Boost::hana requires /EHsc, so we need to enable it globally - include(lagrange_filter_flags) + include(lagrange_filter_compile_flags) set(LAGRANGE_GLOBAL_FLAGS /EHsc # Compatibility with Boost::hana ) - lagrange_filter_flags(LAGRANGE_GLOBAL_FLAGS) + lagrange_filter_compile_flags(LAGRANGE_GLOBAL_FLAGS) message(STATUS "Adding global flags: ${LAGRANGE_GLOBAL_FLAGS}") add_compile_options(${LAGRANGE_GLOBAL_FLAGS}) else() - include(lagrange_filter_flags) + include(lagrange_filter_compile_flags) set(LAGRANGE_GLOBAL_FLAGS -fdiagnostics-color=always # GCC -fcolor-diagnostics # Clang ) - lagrange_filter_flags(LAGRANGE_GLOBAL_FLAGS) + lagrange_filter_compile_flags(LAGRANGE_GLOBAL_FLAGS) message(STATUS "Adding global flags: ${LAGRANGE_GLOBAL_FLAGS}") add_compile_options(${LAGRANGE_GLOBAL_FLAGS}) endif() if(LAGRANGE_USE_SPLIT_DEBUG_INFO) - include(lagrange_filter_flags) + include(lagrange_filter_compile_flags) set(LAGRANGE_SPLIT_DEBUG_FLAGS -gsplit-dwarf -gz=zstd ) - lagrange_filter_flags(LAGRANGE_SPLIT_DEBUG_FLAGS) + lagrange_filter_compile_flags(LAGRANGE_SPLIT_DEBUG_FLAGS) message(STATUS "Adding split debug flags: ${LAGRANGE_SPLIT_DEBUG_FLAGS}") add_compile_options(${LAGRANGE_SPLIT_DEBUG_FLAGS}) - # lagrange_filter_flags only checks compile flags; MSVC may ignore GNU linker flags. - if(NOT MSVC) - include(CheckLinkerFlag) - check_linker_flag(CXX "-Wl,--compress-debug-sections=zstd" LAGRANGE_HAS_ZSTD_DEBUG_LINK) - endif() - if(LAGRANGE_HAS_ZSTD_DEBUG_LINK) - add_link_options(-Wl,--compress-debug-sections=zstd) - endif() + include(lagrange_filter_link_flags) + set(LAGRANGE_SPLIT_DEBUG_LINK_FLAGS + LINKER:--compress-debug-sections=zstd + ) + lagrange_filter_link_flags(LAGRANGE_SPLIT_DEBUG_LINK_FLAGS) + message(STATUS "Adding split debug link flags: ${LAGRANGE_SPLIT_DEBUG_LINK_FLAGS}") + add_link_options(${LAGRANGE_SPLIT_DEBUG_LINK_FLAGS}) endif() if(LAGRANGE_WITH_TRACY) - include(lagrange_filter_flags) + include(lagrange_filter_compile_flags) set(LAGRANGE_GLOBAL_FLAGS "-fno-omit-frame-pointer" "-g" ) - lagrange_filter_flags(LAGRANGE_GLOBAL_FLAGS) + lagrange_filter_compile_flags(LAGRANGE_GLOBAL_FLAGS) message(STATUS "Adding global flags: ${LAGRANGE_GLOBAL_FLAGS}") add_compile_options(${LAGRANGE_GLOBAL_FLAGS}) endif() diff --git a/cmake/lagrange/lagrange_warnings.cmake b/cmake/lagrange/lagrange_warnings.cmake index 21f46527..474ed786 100644 --- a/cmake/lagrange/lagrange_warnings.cmake +++ b/cmake/lagrange/lagrange_warnings.cmake @@ -21,7 +21,7 @@ add_library(lagrange::warnings ALIAS lagrange_warnings) set_target_properties(lagrange_warnings PROPERTIES EXPORT_NAME warnings) install(TARGETS lagrange_warnings EXPORT Lagrange_Targets FILE_SET HEADERS) -include(lagrange_filter_flags) +include(lagrange_filter_compile_flags) # options # More options can be found at: @@ -60,7 +60,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") "/permissive-" ) endif() - lagrange_filter_flags(options) + lagrange_filter_compile_flags(options) target_compile_options(lagrange_warnings INTERFACE ${options}) else() # For non-MSVC compilers, see @@ -217,7 +217,7 @@ else() endif() endif() - lagrange_filter_flags(options) + lagrange_filter_compile_flags(options) target_compile_options(lagrange_warnings INTERFACE ${options}) endif() From 4d6fbf512b4af04cfb3cf6af9c236bbbf2703d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Dumas?= Date: Thu, 1 Oct 2026 15:26:51 +0000 Subject: [PATCH 22/22] Consolidate compile and link flag filters in one module --- .../lagrange_filter_compile_flags.cmake | 25 ------------------- ...lags.cmake => lagrange_filter_flags.cmake} | 19 ++++++++++++-- cmake/lagrange/lagrange_global_flags.cmake | 9 +++---- cmake/lagrange/lagrange_warnings.cmake | 2 +- 4 files changed, 22 insertions(+), 33 deletions(-) delete mode 100644 cmake/lagrange/lagrange_filter_compile_flags.cmake rename cmake/lagrange/{lagrange_filter_link_flags.cmake => lagrange_filter_flags.cmake} (68%) diff --git a/cmake/lagrange/lagrange_filter_compile_flags.cmake b/cmake/lagrange/lagrange_filter_compile_flags.cmake deleted file mode 100644 index 8e152585..00000000 --- a/cmake/lagrange/lagrange_filter_compile_flags.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# -# Copyright 2021 Adobe. All rights reserved. -# This file is licensed to you under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -# OF ANY KIND, either express or implied. See the License for the specific language -# governing permissions and limitations under the License. -# -function(lagrange_filter_compile_flags flags) - include(CheckCXXCompilerFlag) - set(output_flags) - foreach(FLAG IN ITEMS ${${flags}}) - string(REPLACE "=" "-" FLAG_VAR "${FLAG}") - if(NOT DEFINED IS_SUPPORTED_${FLAG_VAR}) - check_cxx_compiler_flag("${FLAG}" IS_SUPPORTED_${FLAG_VAR}) - endif() - if(IS_SUPPORTED_${FLAG_VAR}) - list(APPEND output_flags $<$:${FLAG}>) - endif() - endforeach() - set(${flags} ${output_flags} PARENT_SCOPE) -endfunction() diff --git a/cmake/lagrange/lagrange_filter_link_flags.cmake b/cmake/lagrange/lagrange_filter_flags.cmake similarity index 68% rename from cmake/lagrange/lagrange_filter_link_flags.cmake rename to cmake/lagrange/lagrange_filter_flags.cmake index 01b4c2e8..d59c5bd9 100644 --- a/cmake/lagrange/lagrange_filter_link_flags.cmake +++ b/cmake/lagrange/lagrange_filter_flags.cmake @@ -1,14 +1,29 @@ # -# Copyright 2026 Adobe. All rights reserved. +# Copyright 2021 Adobe. All rights reserved. # This file is licensed to you under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. You may obtain a copy # of the License at http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS # OF ANY KIND, either express or implied. See the License for the specific language # governing permissions and limitations under the License. # +function(lagrange_filter_compile_flags flags) + include(CheckCXXCompilerFlag) + set(output_flags) + foreach(FLAG IN ITEMS ${${flags}}) + string(REPLACE "=" "-" FLAG_VAR "${FLAG}") + if(NOT DEFINED IS_SUPPORTED_${FLAG_VAR}) + check_cxx_compiler_flag("${FLAG}" IS_SUPPORTED_${FLAG_VAR}) + endif() + if(IS_SUPPORTED_${FLAG_VAR}) + list(APPEND output_flags $<$:${FLAG}>) + endif() + endforeach() + set(${flags} ${output_flags} PARENT_SCOPE) +endfunction() + function(lagrange_filter_link_flags flags) include(CheckLinkerFlag) set(output_flags) diff --git a/cmake/lagrange/lagrange_global_flags.cmake b/cmake/lagrange/lagrange_global_flags.cmake index f867e915..32e23ef9 100644 --- a/cmake/lagrange/lagrange_global_flags.cmake +++ b/cmake/lagrange/lagrange_global_flags.cmake @@ -31,7 +31,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") add_compile_options(/FS) # Boost::hana requires /EHsc, so we need to enable it globally - include(lagrange_filter_compile_flags) + include(lagrange_filter_flags) set(LAGRANGE_GLOBAL_FLAGS /EHsc # Compatibility with Boost::hana ) @@ -39,7 +39,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") message(STATUS "Adding global flags: ${LAGRANGE_GLOBAL_FLAGS}") add_compile_options(${LAGRANGE_GLOBAL_FLAGS}) else() - include(lagrange_filter_compile_flags) + include(lagrange_filter_flags) set(LAGRANGE_GLOBAL_FLAGS -fdiagnostics-color=always # GCC -fcolor-diagnostics # Clang @@ -50,7 +50,7 @@ else() endif() if(LAGRANGE_USE_SPLIT_DEBUG_INFO) - include(lagrange_filter_compile_flags) + include(lagrange_filter_flags) set(LAGRANGE_SPLIT_DEBUG_FLAGS -gsplit-dwarf -gz=zstd @@ -59,7 +59,6 @@ if(LAGRANGE_USE_SPLIT_DEBUG_INFO) message(STATUS "Adding split debug flags: ${LAGRANGE_SPLIT_DEBUG_FLAGS}") add_compile_options(${LAGRANGE_SPLIT_DEBUG_FLAGS}) - include(lagrange_filter_link_flags) set(LAGRANGE_SPLIT_DEBUG_LINK_FLAGS LINKER:--compress-debug-sections=zstd ) @@ -69,7 +68,7 @@ if(LAGRANGE_USE_SPLIT_DEBUG_INFO) endif() if(LAGRANGE_WITH_TRACY) - include(lagrange_filter_compile_flags) + include(lagrange_filter_flags) set(LAGRANGE_GLOBAL_FLAGS "-fno-omit-frame-pointer" "-g" diff --git a/cmake/lagrange/lagrange_warnings.cmake b/cmake/lagrange/lagrange_warnings.cmake index 474ed786..349b0055 100644 --- a/cmake/lagrange/lagrange_warnings.cmake +++ b/cmake/lagrange/lagrange_warnings.cmake @@ -21,7 +21,7 @@ add_library(lagrange::warnings ALIAS lagrange_warnings) set_target_properties(lagrange_warnings PROPERTIES EXPORT_NAME warnings) install(TARGETS lagrange_warnings EXPORT Lagrange_Targets FILE_SET HEADERS) -include(lagrange_filter_compile_flags) +include(lagrange_filter_flags) # options # More options can be found at: