From ddcbc10c6027926e6102c6ef45201a643e2ba436 Mon Sep 17 00:00:00 2001 From: Vincenzo Eduardo Padulano Date: Thu, 30 Jul 2026 11:56:44 +0200 Subject: [PATCH 1/3] [test] Add ACLiC test from Python post installation --- test/PostInstall/CMakeLists.txt | 19 +++++++++++++++++ test/PostInstall/post_install_test_ACLiC.C | 4 ++++ test/PostInstall/post_install_test_ACLiC.py | 21 +++++++++++++++++++ .../post_install_test_ACLiC_cleanup.cmake | 2 ++ 4 files changed, 46 insertions(+) create mode 100644 test/PostInstall/post_install_test_ACLiC.C create mode 100644 test/PostInstall/post_install_test_ACLiC.py create mode 100644 test/PostInstall/post_install_test_ACLiC_cleanup.cmake diff --git a/test/PostInstall/CMakeLists.txt b/test/PostInstall/CMakeLists.txt index d25749949ca22..b394a82be80ab 100644 --- a/test/PostInstall/CMakeLists.txt +++ b/test/PostInstall/CMakeLists.txt @@ -42,5 +42,24 @@ if(BUILD_TESTING) set_tests_properties(python-package-metadata PROPERTIES ENVIRONMENT PYTHONPATH=${ROOT_LIBRARY_DIR} ) + + # Test ACLiC can be run from Python after installation + configure_file(post_install_test_ACLiC.C ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) + configure_file(post_install_test_ACLiC.py ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) + add_test(NAME python-post-install-ACLiC + COMMAND ${Python3_EXECUTABLE} post_install_test_ACLiC.py + ) + set_tests_properties(python-post-install-ACLiC + PROPERTIES + ENVIRONMENT PYTHONPATH=${ROOT_LIBRARY_DIR} + FIXTURES_REQUIRED python-post-install-ACLiC-fixture + ) + add_test(NAME python-post-install-ACLiC-cleanupfiles + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/post_install_test_ACLiC_cleanup.cmake + ) + set_tests_properties(python-post-install-ACLiC-cleanupfiles + PROPERTIES FIXTURES_CLEANUP python-post-install-ACLiC-fixture + ) + endif() endif() diff --git a/test/PostInstall/post_install_test_ACLiC.C b/test/PostInstall/post_install_test_ACLiC.C new file mode 100644 index 0000000000000..b812012b8c72a --- /dev/null +++ b/test/PostInstall/post_install_test_ACLiC.C @@ -0,0 +1,4 @@ +int post_install_test_ACLiC() +{ + return 42; +} diff --git a/test/PostInstall/post_install_test_ACLiC.py b/test/PostInstall/post_install_test_ACLiC.py new file mode 100644 index 0000000000000..75c0a2af02f19 --- /dev/null +++ b/test/PostInstall/post_install_test_ACLiC.py @@ -0,0 +1,21 @@ +import ctypes + +import cppyy.ll +import ROOT + + +def main(): + err = ctypes.c_int(-1) # A value purposely different than any of the EErrorCode values + err_ptr = cppyy.ll.reinterpret_cast["TInterpreter::EErrorCode*"](ctypes.addressof(err)) + ROOT.gInterpreter.ProcessLine(".L post_install_test_ACLiC.C+", err_ptr) + if err.value != ROOT.TInterpreter.kNoError: + raise RuntimeError(f"Failed to compile post_install_test_ACLiC.C, error code: {err.value}") + + val = ROOT.gInterpreter.MakeInterpreterValue() + ROOT.gInterpreter.Evaluate("post_install_test_ACLiC()", val) + + return 0 if val.GetAsUnsignedLong() == 42 else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/test/PostInstall/post_install_test_ACLiC_cleanup.cmake b/test/PostInstall/post_install_test_ACLiC_cleanup.cmake new file mode 100644 index 0000000000000..1cb2b6fbff83a --- /dev/null +++ b/test/PostInstall/post_install_test_ACLiC_cleanup.cmake @@ -0,0 +1,2 @@ +file(GLOB post_install_test_ACLiC_artifacts "post_install_test_ACLiC_*") +file(REMOVE ${post_install_test_ACLiC_artifacts}) From 2138f40728d4912c98c4318ec25b956c19c6a4c9 Mon Sep 17 00:00:00 2001 From: Vincenzo Eduardo Padulano Date: Thu, 30 Jul 2026 14:16:27 +0200 Subject: [PATCH 2/3] [test] Enable PostInstall on MacOS --- .github/workflows/root-ci.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index 1239bd5cf6c69..7fc8283af3635 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -113,6 +113,7 @@ jobs: env: VIRTUAL_ENV_DIR: ${{ github.workspace }}/ROOT_CI_VENV MACOSX_DEPLOYMENT_TARGET: ${{ matrix.platform == 'mac-beta' && '27' || '' }} # Problems with gfortran falling back 18 (unsupported) + HOME: /Users/sftnight strategy: fail-fast: false @@ -154,7 +155,6 @@ jobs: - name: Pull Request Build if: github.event_name == 'pull_request' env: - HOME: /Users/sftnight INCREMENTAL: ${{ !contains(github.event.pull_request.labels.*.name, 'clean build') && !matrix.platform == 'mac15' && !matrix.platform == 'mac26'}} GITHUB_PR_ORIGIN: ${{ github.event.pull_request.head.repo.clone_url }} OVERRIDES: ${{ join( matrix.overrides, ' ') }} @@ -231,6 +231,30 @@ jobs: path: /Users/sftnight/ROOT-CI/packages/root_v* if-no-files-found: error + - name: Install + env: + BUILD_DIR: ${{ env.HOME }}/ROOT-CI/build + INSTALL_DIR: ${{ env.HOME }}/ROOT-CI/install + if: ${{ !cancelled() && !matrix.is_special }} + run: "cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}" + + - name: Build post-install test project + if: ${{ !cancelled() && !matrix.is_special }} + env: + SOURCE_DIR: ${{ env.HOME }}/ROOT-CI/src/test/PostInstall/ + INSTALL_DIR: ${{ env.HOME }}/ROOT-CI/install + POST_INSTALL_DIR: ${{ env.HOME }}/ROOT-CI/PostInstall + run: | + cmake -S ${{ env.SOURCE_DIR }} -B ${{ env.POST_INSTALL_DIR }} -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }}; + cmake --build ${{ env.POST_INSTALL_DIR }}; + + - name: CTest in post-install test project + if: ${{ !cancelled() && !matrix.is_special }} + env: + POST_INSTALL_DIR: ${{ env.HOME }}/ROOT-CI/PostInstall + working-directory: ${{ env.POST_INSTALL_DIR }} + run: ctest --output-on-failure -j $(sysctl -n hw.logicalcpu) + build-windows: # For any event that is not a PR, the CI will always run. In PRs, the CI From 0c5b114d8044db4f75f4de50154c1969173ef0a1 Mon Sep 17 00:00:00 2001 From: Vincenzo Eduardo Padulano Date: Thu, 30 Jul 2026 12:24:16 +0200 Subject: [PATCH 3/3] [core] Avoid linking against private Apple library with ACLiC `TSystem::CompileMacro`, aka `ACLiC`, can generate a shared library from a ROOT macro. While doing so, it links the target library against all loaded libraries at time of calling. When running from Python e.g. via `ProcessLine(".L macro.C+")`, the loaded libraries include those dynamically loaded by the Python interpreter. When run on a MacOS system, these may also include so-called "private" Apple libraries. These libraries cannot be linked against by third-party code: the linker will fail with an error saying "product being built is not an allowed client of it". One such situation has surfaced in a bug report to the homebrew distribution of ROOT https://github.com/Homebrew/homebrew-core/issues/272324 related to the `libffi` library, but the issue is actually generic. Unfortunately, Apple does not provide an authoritative list of "private" libraries and there is no direct way to ask a library whether linking against it will produce a linker error or not. Aside from manually checking the list of allowed clients of each of the loaded libraries before linking, manually vetoing known "private" libraries seems the next-best thing. This commit proposes to remove the `libffi` library from the list of libraries `ACLiC` will link against when run on MacOS. --- core/base/src/TSystem.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/base/src/TSystem.cxx b/core/base/src/TSystem.cxx index 9eef2c91eaec3..12a79480a7ff9 100644 --- a/core/base/src/TSystem.cxx +++ b/core/base/src/TSystem.cxx @@ -2276,6 +2276,11 @@ const char *TSystem::GetLibraries(const char *regexp, const char *options, static TRegexp separator("[^ \\t\\s]+"); static TRegexp dynload("/lib-dynload/"); + // Skip libffi, it is a private library used by the system. This is visible in the stub .tbd file: + // allowable-clients: + // clients: [ '!' ] + // See https://github.com/Homebrew/homebrew-core/issues/272324#issuecomment-5119880493 for more info + static TRegexp libffiMatch("/usr/lib/libffi"); Ssiz_t start, index, end; start = index = end = 0; @@ -2284,7 +2289,7 @@ const char *TSystem::GetLibraries(const char *regexp, const char *options, index = libs2.Index(separator, &end, start); if (index >= 0) { TString s = libs2(index, end); - if (s.Index(dynload) == kNPOS) { + if (s.Index(dynload) == kNPOS && s.Index(libffiMatch) == kNPOS) { if (!maclibs.IsNull()) maclibs.Append(" "); maclibs.Append(s); }