From 874f02d717935af8c4bf10e54d141a42d5574fbb Mon Sep 17 00:00:00 2001 From: Jakob Blomer Date: Wed, 8 Jul 2026 16:10:36 +0200 Subject: [PATCH 1/8] [ci] fix picking up ASAN configuration --- .../buildconfig/{alma10-minimal-asan.txt => alma10-asan.txt} | 0 .github/workflows/root-ci.yml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/root-ci-config/buildconfig/{alma10-minimal-asan.txt => alma10-asan.txt} (100%) diff --git a/.github/workflows/root-ci-config/buildconfig/alma10-minimal-asan.txt b/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt similarity index 100% rename from .github/workflows/root-ci-config/buildconfig/alma10-minimal-asan.txt rename to .github/workflows/root-ci-config/buildconfig/alma10-asan.txt diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index bc8557afbedd3..31677a325a8fe 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -430,7 +430,7 @@ jobs: overrides: ["CMAKE_CXX_STANDARD=20"] # Asan build - image: alma10 - platform_config: alma10-minimal + platform_config: alma10-asan is_special: true property: "asan" overrides: ["ROOT_CTEST_CUSTOM_FLAGS=-E@\\(^tutorial-\\|cppinterop-CppInterOpTest\\|roottest-cling-specialobj-runf02$\\|roottest-root-collection-DeleteWarning$\\|roottest-root-io-evolution-fixarr2$\\|roottest-root-meta-rlibmap$\\|roottest-root-treeproxy-vectorint-vectorint$\\)"] From d8ac61b36ae6fdd085ab55753a66a5d1da6b0075 Mon Sep 17 00:00:00 2001 From: Jakob Blomer Date: Mon, 29 Jun 2026 11:00:47 +0200 Subject: [PATCH 2/8] [ci] fix up asan build config --- .../workflows/root-ci-config/buildconfig/alma10-asan.txt | 6 +++--- .github/workflows/root-ci.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt b/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt index d9e43b53bbff4..6cf8a854af8fb 100644 --- a/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt +++ b/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt @@ -1,12 +1,12 @@ -CMAKE_BUILD_TYPE=Debug -CMAKE_CXX_FLAGS_DEBUG="-Og -g" +CMAKE_BUILD_TYPE=RelWithDebInfo asan=ON ccache=ON builtin_civetweb=ON builtin_vdt=ON +ccache=ON fail-on-missing=ON minimal=ON roottest=ON testing=ON LSAN_OPTIONS=verbosity=1:log_threads=1 -ROOT_CTEST_CUSTOM_FLAGS="-E \(cppinterop-CppInterOpTest\|roottest-cling-specialobj-runf02$\|roottest-root-collection-DeleteWarning$\|roottest-root-io-evolution-fixarr2$\|roottest-root-meta-rlibmap$\|roottest-root-treeproxy-vectorint-vectorint$\)" +ROOT_CTEST_CUSTOM_FLAGS="-E \(cppinterop-CppInterOpTest\|gtest-core-metacling-TClingTest$\|roottest-cling-specialobj-runf02$\|roottest-root-collection-DeleteWarning$\|roottest-root-io-evolution-fixarr2$\|roottest-root-meta-rlibmap$\|roottest-root-tree-cloning-runtreeCloneTest2$\|roottest-root-treeproxy-vectorint-vectorint$\)" diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index 31677a325a8fe..6525ff006e047 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -433,7 +433,7 @@ jobs: platform_config: alma10-asan is_special: true property: "asan" - overrides: ["ROOT_CTEST_CUSTOM_FLAGS=-E@\\(^tutorial-\\|cppinterop-CppInterOpTest\\|roottest-cling-specialobj-runf02$\\|roottest-root-collection-DeleteWarning$\\|roottest-root-io-evolution-fixarr2$\\|roottest-root-meta-rlibmap$\\|roottest-root-treeproxy-vectorint-vectorint$\\)"] + overrides: ["ROOT_CTEST_CUSTOM_FLAGS=-E@\\(^tutorial-\\|cppinterop-CppInterOpTest\\|gtest-core-metacling-TClingTest$\\|roottest-cling-specialobj-runf02$\\|roottest-root-collection-DeleteWarning$\\|roottest-root-io-evolution-fixarr2$\\|roottest-root-meta-rlibmap$\\|roottest-root-tree-cloning-runtreeCloneTest2$\\|roottest-root-treeproxy-vectorint-vectorint$\\)"] # Fedora Rawhide with Python freethreading+debug build - image: rawhide python_venv: "/py-venv-3.15td/ROOT-CI" From 2d516b372deb1e1d5b6eda3b79f96e06501d1ecc Mon Sep 17 00:00:00 2001 From: silverweed Date: Tue, 28 Jul 2026 09:14:29 +0200 Subject: [PATCH 3/8] Revert "[core] do not interrupt build when an asan error is found" This reverts commit d8695bed46d79d69025d8b369b8d4251fcae116b. --- core/sanitizer/SanitizerSetup.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/sanitizer/SanitizerSetup.cxx b/core/sanitizer/SanitizerSetup.cxx index 2e4b1340900dc..a638ccae054f5 100644 --- a/core/sanitizer/SanitizerSetup.cxx +++ b/core/sanitizer/SanitizerSetup.cxx @@ -31,8 +31,7 @@ const char* __asan_default_options() { ":detect_container_overflow=1" ":alloc_dealloc_mismatch=0" DETECT_LEAKS - ":verify_asan_link_order=0" - ":halt_on_error=0"; + ":verify_asan_link_order=0"; } /// Default options when leak sanitizer starts up in ROOT executables. From dce37c1bdbcb7438ec80f0e34ef7ecd08dbcfd1b Mon Sep 17 00:00:00 2001 From: silverweed Date: Tue, 28 Jul 2026 14:27:29 +0200 Subject: [PATCH 4/8] [CI] re-enable some tests in the asan build --- .github/workflows/root-ci-config/buildconfig/alma10-asan.txt | 2 +- .github/workflows/root-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt b/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt index 6cf8a854af8fb..65f3fd63f791a 100644 --- a/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt +++ b/.github/workflows/root-ci-config/buildconfig/alma10-asan.txt @@ -9,4 +9,4 @@ minimal=ON roottest=ON testing=ON LSAN_OPTIONS=verbosity=1:log_threads=1 -ROOT_CTEST_CUSTOM_FLAGS="-E \(cppinterop-CppInterOpTest\|gtest-core-metacling-TClingTest$\|roottest-cling-specialobj-runf02$\|roottest-root-collection-DeleteWarning$\|roottest-root-io-evolution-fixarr2$\|roottest-root-meta-rlibmap$\|roottest-root-tree-cloning-runtreeCloneTest2$\|roottest-root-treeproxy-vectorint-vectorint$\)" +ROOT_CTEST_CUSTOM_FLAGS="-E \(gtest-core-metacling-TClingTest$\|roottest-cling-specialobj-runf02$\|roottest-root-collection-DeleteWarning$\|roottest-root-io-evolution-fixarr2$\|roottest-root-meta-rlibmap$\|roottest-root-tree-cloning-runtreeCloneTest2$\|roottest-root-treeproxy-vectorint-vectorint$\)" diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index 6525ff006e047..e6400a2d19e62 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -433,7 +433,7 @@ jobs: platform_config: alma10-asan is_special: true property: "asan" - overrides: ["ROOT_CTEST_CUSTOM_FLAGS=-E@\\(^tutorial-\\|cppinterop-CppInterOpTest\\|gtest-core-metacling-TClingTest$\\|roottest-cling-specialobj-runf02$\\|roottest-root-collection-DeleteWarning$\\|roottest-root-io-evolution-fixarr2$\\|roottest-root-meta-rlibmap$\\|roottest-root-tree-cloning-runtreeCloneTest2$\\|roottest-root-treeproxy-vectorint-vectorint$\\)"] + overrides: ["ROOT_CTEST_CUSTOM_FLAGS=-E@\\(^tutorial-\\|gtest-core-metacling-TClingTest$\\|roottest-cling-specialobj-runf02$\\|roottest-root-collection-DeleteWarning$\\|roottest-root-io-evolution-fixarr2$\\|roottest-root-meta-rlibmap$\\|roottest-root-tree-cloning-runtreeCloneTest2$\\|roottest-root-treeproxy-vectorint-vectorint$\\)"] # Fedora Rawhide with Python freethreading+debug build - image: rawhide python_venv: "/py-venv-3.15td/ROOT-CI" From 43596e06d2ee73702ee751210b68e969a50d63a4 Mon Sep 17 00:00:00 2001 From: silverweed Date: Thu, 30 Jul 2026 09:20:55 +0200 Subject: [PATCH 5/8] [CI] disable odr violation asan checks for now There are too many failing tests with it --- core/sanitizer/SanitizerSetup.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/sanitizer/SanitizerSetup.cxx b/core/sanitizer/SanitizerSetup.cxx index a638ccae054f5..e4d783ace554b 100644 --- a/core/sanitizer/SanitizerSetup.cxx +++ b/core/sanitizer/SanitizerSetup.cxx @@ -31,7 +31,8 @@ const char* __asan_default_options() { ":detect_container_overflow=1" ":alloc_dealloc_mismatch=0" DETECT_LEAKS - ":verify_asan_link_order=0"; + ":verify_asan_link_order=0" + ":detect_odr_violation=0"; } /// Default options when leak sanitizer starts up in ROOT executables. From 5e2629a4169246927e4e9df4730f9ca66c031c29 Mon Sep 17 00:00:00 2001 From: silverweed Date: Wed, 5 Aug 2026 09:14:44 +0200 Subject: [PATCH 6/8] [io] Fix roottest failing with Asan --- roottest/root/io/heritstl/SEIdAltLItem.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roottest/root/io/heritstl/SEIdAltLItem.h b/roottest/root/io/heritstl/SEIdAltLItem.h index f5b06fae1822f..84d102893d27a 100644 --- a/roottest/root/io/heritstl/SEIdAltLItem.h +++ b/roottest/root/io/heritstl/SEIdAltLItem.h @@ -34,10 +34,10 @@ class SEIdAltLItem { fSigLin(siglin), fSigCorr(sigcorr), fTime(time) { }; - SEIdAltLItem(const SEIdAltLItem& that) // copy ctor - { *this = that; } + SEIdAltLItem(const SEIdAltLItem &) = default; + SEIdAltLItem &operator=(const SEIdAltLItem &) = default; - virtual ~SEIdAltLItem() { ; } // dtor + virtual ~SEIdAltLItem() = default; Int_t GetSEId() const { return fStripEndId; } Float_t GetWeight() const { return fWeight; } From 74074cf299a6667ab8bc32b5fd58c1cf977ec3ca Mon Sep 17 00:00:00 2001 From: silverweed Date: Wed, 5 Aug 2026 14:22:46 +0200 Subject: [PATCH 7/8] [core] some ACLiC cleanups 3 main changes: 1. use an ofstream rather than system("echo >") 2. insert failure points in between the system() calls to have a clearer error in case of failure 3. make sure the pseudo-devnull file is always removed if necessary --- core/base/src/TSystem.cxx | 104 ++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/core/base/src/TSystem.cxx b/core/base/src/TSystem.cxx index 12a79480a7ff9..5bdddc7e40501 100644 --- a/core/base/src/TSystem.cxx +++ b/core/base/src/TSystem.cxx @@ -2593,38 +2593,44 @@ static void R__WriteDependencyFile(const TString & build_loc, const TString &dep // standard include directories, bool needToUnlinkTempFile = false; -#ifndef WIN32 - const char * stderrfile = "/dev/null"; -#else + TString devnullfile = "/dev/null"; +#ifdef WIN32 // Determine the null device based on the shell in use. // COMSPEC unset or pointing to cmd.exe -> NUL // COMSPEC pointing to powershell -> $null // Anything else (e.g. bash/sh on Windows) -> `depfilename`.stderr.tmp - TString stderrfile; const char *comspec = gSystem->Getenv("COMSPEC"); if (!comspec || !comspec[0]) { - stderrfile = "NUL"; + devnullfile = "NUL"; } else { TString comspecStr(comspec); comspecStr.ToLower(); if (comspecStr.EndsWith("cmd.exe")) { - stderrfile = "NUL"; + devnullfile = "NUL"; } else if (comspecStr.Contains("powershell.exe")) { - stderrfile = "$null"; + devnullfile = "$null"; } else { needToUnlinkTempFile = true; - stderrfile = depfilename + ".stderr.tmp"; - gSystem->PrependPathName(build_loc, stderrfile); + devnullfile = depfilename + ".stderr.tmp"; + gSystem->PrependPathName(build_loc, devnullfile); } } #endif TString bakdepfilename = depfilename + ".bak"; -#ifdef WIN32 - TString touch = "echo # > "; touch += "\"" + depfilename + "\""; -#else - TString touch = "echo > "; touch += "\"" + depfilename + "\""; -#endif + struct Defer { + bool fNeedToUnlinkTempFile; + const TString &fDevNullFile, &fBakdepfilename; + ~Defer() + { + if (fNeedToUnlinkTempFile) { + // Remove the temporary stderr file if it was created. + gSystem->Unlink(fDevNullFile); + } + gSystem->Unlink(fBakdepfilename); + } + } deferGuard{needToUnlinkTempFile, devnullfile, bakdepfilename}; + TString builddep = "rmkdepend"; gSystem->PrependPathName(TROOT::GetBinDir(), builddep); builddep += " \"-f"; @@ -2675,10 +2681,10 @@ static void R__WriteDependencyFile(const TString & build_loc, const TString &dep builddep += "-t"; R__AddPath(builddep, targetname); builddep += "\" > "; - builddep += stderrfile; + builddep += devnullfile; builddep += " 2>&1 "; - TString adddictdep = "echo "; + TString adddictdep; R__AddPath(adddictdep,targetname); adddictdep += ": "; #if defined(R__HAS_CLING_DICTVERSION) @@ -2694,19 +2700,27 @@ static void R__WriteDependencyFile(const TString & build_loc, const TString &dep } #endif { - const char *dictHeaders[] = { "RVersion.h", "ROOT/RConfig.hxx", "TClass.h", - "TDictAttributeMap.h","TInterpreter.h","TROOT.h","TBuffer.h", - "TMemberInspector.h","TError.h","RtypesImp.h","TIsAProxy.h", - "TFileMergeInfo.h","TCollectionProxyInfo.h"}; - - for (unsigned int h=0; h < sizeof(dictHeaders)/sizeof(dictHeaders[0]); ++h) - { - char *rootVersion = gSystem->Which(incPath,dictHeaders[h]); + constexpr const char *dictHeaders[] = {"RVersion.h", + "ROOT/RConfig.hxx", + "TClass.h", + "TDictAttributeMap.h", + "TInterpreter.h", + "TROOT.h", + "TBuffer.h", + "TMemberInspector.h", + "TError.h", + "RtypesImp.h", + "TIsAProxy.h", + "TFileMergeInfo.h", + "TCollectionProxyInfo.h"}; + + for (const char *header : dictHeaders) { + char *rootVersion = gSystem->Which(incPath, header); if (rootVersion) { R__AddPath(adddictdep,rootVersion); delete [] rootVersion; } else { - R__AddPath(adddictdep,rootsysInclude + "/" + dictHeaders[h]); + R__AddPath(adddictdep, rootsysInclude + "/" + header); } adddictdep += " "; } @@ -2720,32 +2734,36 @@ static void R__WriteDependencyFile(const TString & build_loc, const TString &dep delete [] rootCling; } } - adddictdep += " >> \""+depfilename+"\""; - TString addversiondep( "echo "); - addversiondep += libname + version_var_prefix + " \"" + ROOT_RELEASE + "\" >> \""+depfilename+"\""; + { + std::ofstream depFile(depfilename, std::ios::out | std::ios::trunc); + if (!depFile) { + ::Warning("ACLiC", "Failed to open dependency file %s for %s", depfilename.Data(), library.Data()); + return; + } +#ifdef WIN32 + depFile << "#\n"; +#endif + } - if (gDebug > 4) { - ::Info("ACLiC", "%s", touch.Data()); + if (gDebug > 4) { ::Info("ACLiC", "%s", builddep.Data()); ::Info("ACLiC", "%s", adddictdep.Data()); } + bool depbuiltOk = !gSystem->Exec(builddep); + if (!depbuiltOk) { + ::Warning("ACLiC", "Failed to run rmkdepend for %s", library.Data()); + return; + } - Int_t depbuilt = !gSystem->Exec(touch); - if (depbuilt) depbuilt = !gSystem->Exec(builddep); - if (depbuilt) depbuilt = !gSystem->Exec(adddictdep); - if (depbuilt) depbuilt = !gSystem->Exec(addversiondep); - - if (!depbuilt) { - ::Warning("ACLiC","Failed to generate the dependency file for %s", + std::ofstream depFile(depfilename, std::ios::out | std::ios::app); + if (!depFile) { + ::Warning("ACLiC", "Failed to open dependency file %s for %s after running rmkdepend", depfilename.Data(), library.Data()); - } else { - if (needToUnlinkTempFile) { - // Remove the temporary stderr file if it was created. - gSystem->Unlink(stderrfile); - } - gSystem->Unlink(bakdepfilename); + return; } + depFile << adddictdep << "\n"; + depFile << libname << version_var_prefix << " \"" << ROOT_RELEASE << "\"\n"; } //////////////////////////////////////////////////////////////////////////////// From 9b1661bb98890f2519df169cd40a015fd1d70c68 Mon Sep 17 00:00:00 2001 From: silverweed Date: Fri, 7 Aug 2026 14:01:18 +0200 Subject: [PATCH 8/8] [cmake] Blacklist driveRedirect.py from asan LD_PRELOAD In ROOTTEST_ADD_TEST we have a piece of code that adds the Asan libraries to LD_PRELOAD for python scripts, but it excludes a few special cases. Among these there is driveTabCom.py, which is mentioned explicitly by driveRedirect.py to have a similar functionality (that functionality being driving the root.exe process through a pseudo-interactive tty to test some stuff). For reasons that are not entirely clear to me, these scripts don't want the asan libraries in LD_PRELOAD. Possibly because, not having an `import ROOT`, they don't benefit from the tweaks added by this PR: https://github.com/root-project/root/pull/11621 which prevents Asan libraries from leaking into the ROOT process. This is just a guess. This change fixes a problem with roottest-root-rint-Redirect, which otherwise fails on Alma10 with: ld.so: object '/usr/lib/gcc/x86_64-redhat-linux/14/libasan.so' from LD_PRELOAD cannot be preloaded (file too short): ignored. Notably, this failure doesn't happen on Archlinux. As far as I can tell, the main difference is that on Alma, libasan.so is actually a linker script redirecting to /usr/lib64/libasan.so.8.0.0, whereas in Arch the preloaded library (/usr/lib/libasan.so in that case) is an actual shared object. To be ascertained whether this fix is the "proper" one, but it surely is in line with the way we fixed driveTabCom.py, so if this is to be revised, that one likely needs to be as well. --- cmake/modules/RootMacros.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index b811226e9687e..7d938f6883548 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -1777,6 +1777,7 @@ function(ROOT_ADD_TEST test) list(FILTER theCommand INCLUDE REGEX "^-DCMD=.*python[0-9.]*[\\^]") if((theCommand AND NOT (_command MATCHES XMLParsing/parseXMLs.py OR + _command MATCHES roottest/root/rint/driveRedirect.py OR _command MATCHES roottest/root/rint/driveTabCom.py)) OR (_command MATCHES roottest/python/cmdLineUtils AND NOT _command MATCHES MakeNameCyclesRootmvInput))