From 48db1205a6f3bb603075ded4223748248da3d9d6 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sun, 12 Apr 2026 17:18:45 +0200 Subject: [PATCH 1/3] [ci] Enable `gnuinstall` in the `march=native` build This is to test that ROOT doesn't get confused at build and test time by the fact that the structure of the install tree and build tree is different, which is the case for `gnuinstall=ON`. --- .../root-ci-config/buildconfig/opensuse16-march_native.txt | 1 + .github/workflows/root-ci.yml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt b/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt index 2a3ba74ab02bf..030a4f31ca136 100644 --- a/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt +++ b/.github/workflows/root-ci-config/buildconfig/opensuse16-march_native.txt @@ -5,6 +5,7 @@ builtin_tbb=ON builtin_vdt=ON builtin_xrootd=ON builtin_unuran=ON +gnuinstall=ON test_distrdf_dask=OFF test_distrdf_pyspark=OFF tmva-pymva=ON diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index b971d8d14e1b4..2913fd5b6eff1 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -130,6 +130,11 @@ jobs: - platform: mac-beta is_special: true arch: ARM64 + # For the PR and incremental builds, we set gnuinstall=ON to check + # if CMake can deal with the different structure of the install and + # build tree at build and test time. We don't care how the install + # actually looks like. + overrides: ["gnuinstall=ON"] runs-on: - self-hosted From b096f09445b06e50b0263521e58cc637e864de15 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sun, 25 Jan 2026 14:57:41 +0100 Subject: [PATCH 2/3] [core] Use automatic build tree detection for all resource directories This follows up on a5b1ed9080a, using the mechanism to automatically detect if we are in the build tree or install tree also for the other resource directories (so far it was only implemented for the `include` directory as a first test). Several fallbacks and hacks can be removed because of this change, like the `ROOTIGNOREPREFIX` variable that was needed to run ROOT in the build tree for the tests. --- CMakeLists.txt | 7 +- cmake/modules/CheckCompiler.cmake | 4 - cmake/modules/RootMacros.cmake | 9 +- cmake/scripts/compiledata.win32.in | 5 +- config/RConfigure.in | 16 -- core/base/CMakeLists.txt | 44 +++- core/base/inc/TROOT.h | 16 ++ core/base/src/TROOT.cxx | 215 ++++++------------ core/base/src/TSystem.cxx | 4 + core/base/test/IncludePathTest.cxx | 1 - core/foundation/res/ROOT/FoundationUtils.hxx | 10 - core/foundation/src/FoundationUtils.cxx | 83 ------- core/unix/src/TUnixSystem.cxx | 52 +---- core/winnt/src/TWinNTSystem.cxx | 51 +---- io/io/src/TFile.cxx | 6 + main/src/rmain.cxx | 10 +- roottest/CMakeLists.txt | 5 - roottest/root/io/namespacedict/CMakeLists.txt | 4 +- rootx/CMakeLists.txt | 4 + rootx/src/rootx.cxx | 48 +--- tmva/sofie/test/CMakeLists.txt | 4 +- tree/ntuple/test/CMakeLists.txt | 4 +- 22 files changed, 172 insertions(+), 430 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45219da785fdd..91a86ac451e9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -507,7 +507,7 @@ else() ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/cmake/unix/makepchinput.py ${CMAKE_SOURCE_DIR} . ${pyroot_legacy} ${__cling_pch} COMMAND - ${CMAKE_COMMAND} -E env ROOTIGNOREPREFIX=1 ${Python3_EXECUTABLE} + ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/etc/dictpch/makepch.py etc/allDict.cxx.pch ${__allIncludes} -I${CMAKE_BINARY_DIR}/include -I${CMAKE_SOURCE_DIR}/core DEPENDS @@ -541,10 +541,10 @@ if(runtime_cxxmodules) get_property(modules_idx_deps GLOBAL PROPERTY modules_idx_deps_property) if(WIN32) set(modules_idx_cmd COMMAND ${CMAKE_COMMAND} -E env PATH="${library_output_dir}\\\;%PATH%" - ROOTIGNOREPREFIX=1 ROOT_HIST=0 $ -l -q -b) + ROOT_HIST=0 $ -l -q -b) else() set(modules_idx_cmd COMMAND ROOT_INCLUDE_PATH=${DEFAULT_ROOT_INCLUDE_PATH} - ROOTIGNOREPREFIX=1 ROOT_HIST=0 $ -l -q -b) + ROOT_HIST=0 $ -l -q -b) endif() add_custom_command(OUTPUT ${library_output_dir}/modules.idx COMMAND ${CMAKE_COMMAND} -E remove -f modules.idx modules.timestamp @@ -570,7 +570,6 @@ if(NOT CMAKE_CROSSCOMPILING) COMMAND ${CMAKE_COMMAND} -E env ROOT_INCLUDE_PATH=${DEFAULT_ROOT_INCLUDE_PATH} - ROOTIGNOREPREFIX=1 ROOT_HIST=0 $ -q -b -n -e ".L ${CMAKE_SOURCE_DIR}/tutorials/hsimple.C" diff --git a/cmake/modules/CheckCompiler.cmake b/cmake/modules/CheckCompiler.cmake index b9a91d046f612..f96fd8acce6ae 100644 --- a/cmake/modules/CheckCompiler.cmake +++ b/cmake/modules/CheckCompiler.cmake @@ -255,7 +255,3 @@ if(gcctoolchain) set(CMAKE_CXX_FLAGS "--gcc-toolchain=${gcctoolchain} ${CMAKE_CXX_FLAGS}") endif() endif() - -if(gnuinstall) - set(R__HAVE_CONFIG 1) -endif() diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index 567aaf8bc3a00..48cbe1b1e7d29 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -653,12 +653,12 @@ function(ROOT_GENERATE_DICTIONARY dictionary) else() if(CMAKE_PROJECT_NAME STREQUAL ROOT) if(MSVC AND CMAKE_ROOTTEST_DICT) - set(command ${CMAKE_COMMAND} -E env "ROOTIGNOREPREFIX=1" ${CMAKE_BINARY_DIR}/bin/rootcling.exe -rootbuild) + set(command ${CMAKE_BINARY_DIR}/bin/rootcling.exe -rootbuild) else() if(APPLE) - set(command ${CMAKE_COMMAND} -E env "ROOTIGNOREPREFIX=1" SDKROOT=${CMAKE_OSX_SYSROOT} $ -rootbuild) + set(command ${CMAKE_COMMAND} -E env SDKROOT=${CMAKE_OSX_SYSROOT} $ -rootbuild) else() - set(command ${CMAKE_COMMAND} -E env "ROOTIGNOREPREFIX=1" $ -rootbuild) + set(command $ -rootbuild) endif() # Modules need RConfigure.h copied into include/. set(ROOTCLINGDEP rootcling rconfigure) @@ -1819,9 +1819,6 @@ function(ROOT_ADD_TEST test) set_property(TEST ${test} PROPERTY ENVIRONMENT ROOT_DIR=${CMAKE_BINARY_DIR}) else() add_test(NAME ${test} COMMAND ${_command}) - if (gnuinstall) - set_property(TEST ${test} PROPERTY ENVIRONMENT ROOTIGNOREPREFIX=1) - endif() endif() #- provided fixtures and resource lock are set here diff --git a/cmake/scripts/compiledata.win32.in b/cmake/scripts/compiledata.win32.in index 15bb379bbf89c..e2d9d2218193a 100644 --- a/cmake/scripts/compiledata.win32.in +++ b/cmake/scripts/compiledata.win32.in @@ -1,15 +1,14 @@ -/* This file is automatically generated */ #define BUILD_ARCH "@ROOT_ARCHITECTURE@" #define BUILD_NODE "@BuildNodeInfo@" #define CXX "@CMAKE_CXX_COMPILER@" #define COMPILER "@CMAKE_CXX_COMPILER@" #define COMPILERVERS "@CMAKE_CXX_COMPILER_ID@ @CMAKE_CXX_COMPILER_VERSION@" #define COMPILERVERSSTR "@CMAKE_CXX_COMPILER_ID@ @CMAKE_CXX_COMPILER_VERSION@" -#define MAKESHAREDLIB "cl $Opt -nologo -TP -c @CMAKE_CXX_FLAGS@ @BLDCXXFLAGS@ $IncludePath $SourceFiles -Fo$ObjectFiles && bindexplib $LibName $ObjectFiles > $BuildDir\\$LibName.def && lib -nologo -MACHINE:@MACHINE_ARCH@ -out:$BuildDir\\$LibName.lib $ObjectFiles -def:$BuildDir\\$LibName.def && link -nologo $ObjectFiles -DLL -out:$BuildDir\\$LibName.dll $BuildDir\\$LibName.exp -LIBPATH:%ROOTSYS%\\lib $LinkedLibs libCore.lib kernel32.lib advapi32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib && if EXIST \"$BuildDir\\$LibName.dll.manifest\" ( mt -nologo -manifest \"$BuildDir\\$LibName.dll.manifest\" \"-outputresource:$BuildDir\\$LibName.dll;2\" && del \"$BuildDir\\$LibName.dll.manifest\" )" +#define MAKESHAREDLIB "cl $Opt -nologo -TP -c @CMAKE_CXX_FLAGS@ @BLDCXXFLAGS@ $IncludePath $SourceFiles -Fo$ObjectFiles && $BinDir\\bindexplib $LibName $ObjectFiles > $BuildDir\\$LibName.def && lib -nologo -MACHINE:@MACHINE_ARCH@ -out:$BuildDir\\$LibName.lib $ObjectFiles -def:$BuildDir\\$LibName.def && link -nologo $ObjectFiles -DLL -out:$BuildDir\\$LibName.dll $BuildDir\\$LibName.exp -LIBPATH:$LibDir $LinkedLibs libCore.lib kernel32.lib advapi32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib && if EXIST \"$BuildDir\\$LibName.dll.manifest\" ( mt -nologo -manifest \"$BuildDir\\$LibName.dll.manifest\" \"-outputresource:$BuildDir\\$LibName.dll;2\" && del \"$BuildDir\\$LibName.dll.manifest\" )" #define MAKEEXE "cl -nologo -TP -Iinclude -I..\\include -c $Opt @CMAKE_CXX_FLAGS@ @BLDCXXFLAGS@ $IncludePath $SourceFiles && link -opt:ref @CMAKE_EXE_FLAGS@ $ObjectFiles $LinkedLibs advapi32.lib -out:$ExeName && if EXIST \"$ExeName.exe.manifest\" ( mt -nologo -manifest \"$ExeName.exe.manifest\" \"-outputresource:$ExeName.exe;1\" && del \"$ExeName.exe.manifest\" )" #define CXXOPT "@CMAKE_CXX_FLAGS_RELEASE@ @BLDCXXFLAGS@" #define CXXDEBUG "@CMAKE_CXX_FLAGS_DEBUG@ @BLDCXXFLAGS@" #define ROOTBUILD "release" -#define LINKEDLIBS "-LIBPATH:%ROOTSYS%\\lib libCore.lib " +#define LINKEDLIBS "-LIBPATH:$LibDir libCore.lib " #define OBJEXT "obj" #define SOEXT "dll" diff --git a/config/RConfigure.in b/config/RConfigure.in index 2b2370256b4ed..b5c79b6da6006 100644 --- a/config/RConfigure.in +++ b/config/RConfigure.in @@ -3,22 +3,6 @@ /* Configurations file for @architecture@ */ -#cmakedefine R__HAVE_CONFIG - -#ifdef R__HAVE_CONFIG -#define ROOTPREFIX "@prefix@" -#define ROOTBINDIR "@bindir@" -#define ROOTLIBDIR "@libdir@" -#define ROOTETCDIR "@etcdir@" -#define ROOTDATADIR "@datadir@" -#define ROOTDOCDIR "@docdir@" -#define ROOTMACRODIR "@macrodir@" -#define ROOTTUTDIR "@tutdir@" -#define ROOTSRCDIR "@srcdir@" -#define ROOTICONPATH "@iconpath@" -#define TTFFONTDIR "@ttffontdir@" -#endif - #define EXTRAICONPATH "@extraiconpath@" #define ROOT__cplusplus @__cplusplus@ diff --git a/core/base/CMakeLists.txt b/core/base/CMakeLists.txt index e31697bcebe70..85b03b6293bdf 100644 --- a/core/base/CMakeLists.txt +++ b/core/base/CMakeLists.txt @@ -241,20 +241,48 @@ endif() # Absolue CMAKE_INSTALL_ paths are discouraged in CMake, but some # packagers use them anyway. So we support it. -if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR}) - set(install_lib_to_include "${CMAKE_INSTALL_INCLUDEDIR}") -else() - if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR}) - set(libdir "${CMAKE_INSTALL_LIBDIR}") + +function(install_path_from_libdir out_var install_dir) + if(IS_ABSOLUTE "${install_dir}") + set(install_dir_absolute "${install_dir}") else() - set(libdir "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + set(install_dir_absolute "${CMAKE_INSTALL_PREFIX}/${install_dir}") endif() - file(RELATIVE_PATH install_lib_to_include "${libdir}" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") - unset(libdir) + file(RELATIVE_PATH _rel "${libdir}" "${install_dir_absolute}") + set("${out_var}" "${_rel}" PARENT_SCOPE) +endfunction() + +if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(libdir "${CMAKE_INSTALL_LIBDIR}") +else() + set(libdir "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") endif() +install_path_from_libdir(install_lib_to_bin "${CMAKE_INSTALL_BINDIR}") +install_path_from_libdir(install_lib_to_data "${CMAKE_INSTALL_DATADIR}") +install_path_from_libdir(install_lib_to_docs "${CMAKE_INSTALL_DOCDIR}") +install_path_from_libdir(install_lib_to_etc "${CMAKE_INSTALL_SYSCONFDIR}") +install_path_from_libdir(install_lib_to_fonts "${CMAKE_INSTALL_FONTDIR}") +install_path_from_libdir(install_lib_to_icons "${CMAKE_INSTALL_ICONDIR}") +install_path_from_libdir(install_lib_to_include "${CMAKE_INSTALL_INCLUDEDIR}") +install_path_from_libdir(install_lib_to_macros "${CMAKE_INSTALL_MACRODIR}") +install_path_from_libdir(install_lib_to_rootsys "${CMAKE_INSTALL_PREFIX}") +install_path_from_libdir(install_lib_to_src "${CMAKE_INSTALL_SRCDIR}") +install_path_from_libdir(install_lib_to_tutorials "${CMAKE_INSTALL_TUTDIR}") + +unset(libdir) + target_compile_options(Core PRIVATE -DLIB_CORE_NAME=${full_core_filename} + -DINSTALL_LIB_TO_BIN="${install_lib_to_bin}" + -DINSTALL_LIB_TO_DATA="${install_lib_to_data}" + -DINSTALL_LIB_TO_DOCS="${install_lib_to_docs}" + -DINSTALL_LIB_TO_ETC="${install_lib_to_etc}" + -DINSTALL_LIB_TO_FONTS="${install_lib_to_fonts}" + -DINSTALL_LIB_TO_ICONS="${install_lib_to_icons}" -DINSTALL_LIB_TO_INCLUDE="${install_lib_to_include}" + -DINSTALL_LIB_TO_MACROS="${install_lib_to_macros}" + -DINSTALL_LIB_TO_ROOTSYS="${install_lib_to_rootsys}" + -DINSTALL_LIB_TO_TUTORIALS="${install_lib_to_tutorials}" ) add_dependencies(Core ensure_build_tree_marker) diff --git a/core/base/inc/TROOT.h b/core/base/inc/TROOT.h index db22582c668cd..0514f5e9f5fa2 100644 --- a/core/base/inc/TROOT.h +++ b/core/base/inc/TROOT.h @@ -397,6 +397,22 @@ namespace ROOT { inline Bool_t RequiresCleanup(TObject &obj) { return obj.TestBit(kIsReferenced) && obj.GetUniqueID() == 0; } + + struct ResourceDirs { + bool initialized = false; + TString bin; + TString data; + TString docs; + TString etc; + TString fonts; + TString icons; + TString include; + TString macros; + TString rootsys; + TString tutorials; + }; + + ResourceDirs const &GetResourceDirs(); } /// \brief call RecursiveRemove for obj if gROOT is valid diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 9ee7e332ca2b3..51c3a56dd5518 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -3127,42 +3127,67 @@ const char**& TROOT::GetExtraInterpreterArgs() { //////////////////////////////////////////////////////////////////////////////// -#ifdef ROOTPREFIX -static Bool_t IgnorePrefix() { - static Bool_t ignorePrefix = gSystem->Getenv("ROOTIGNOREPREFIX"); - return ignorePrefix; +namespace { + +std::string GetRelativeToSharedLibDir(const char *buildTreePath, const char *installTreePath) +{ + namespace fs = std::filesystem; + + // The shared library directory can be found automatically, because the + // libCore is loaded by definition when using TROOT. It's used as the anchor + // to resolve other resource directories, using the correct relative path + // for either the build or install tree. + fs::path libPath = TROOT::GetSharedLibDir().Data(); + + // Check if we are in the build tree using the build tree marker file + static const bool isBuildTree = fs::exists(libPath / "root-build-tree-marker"); + + fs::path outputPath = libPath / (isBuildTree ? buildTreePath : installTreePath); + + // Normalize to get rid of the "../" in relative paths + return outputPath.lexically_normal().string(); +} + +} // namespace + +ROOT::Internal::ResourceDirs const &ROOT::Internal::GetResourceDirs() +{ + static ResourceDirs dirs; + + if (dirs.initialized) + return dirs; + + // Initialize resource directories with the correct relative paths for the + // build tree or the install tree. + dirs.bin = GetRelativeToSharedLibDir("../bin", INSTALL_LIB_TO_BIN); + dirs.data = GetRelativeToSharedLibDir("..", INSTALL_LIB_TO_DATA); + dirs.docs = GetRelativeToSharedLibDir("..", INSTALL_LIB_TO_DOCS); + dirs.etc = GetRelativeToSharedLibDir("../etc", INSTALL_LIB_TO_ETC); + dirs.fonts = GetRelativeToSharedLibDir("../fonts", INSTALL_LIB_TO_FONTS); + dirs.icons = GetRelativeToSharedLibDir("../icons", INSTALL_LIB_TO_ICONS); + dirs.include = GetRelativeToSharedLibDir("../include", INSTALL_LIB_TO_INCLUDE); + dirs.macros = GetRelativeToSharedLibDir("../macros", INSTALL_LIB_TO_MACROS); + dirs.rootsys = GetRelativeToSharedLibDir("..", INSTALL_LIB_TO_ROOTSYS); + dirs.tutorials = GetRelativeToSharedLibDir("../tutorials", INSTALL_LIB_TO_TUTORIALS); + + dirs.initialized = true; + + return dirs; } -#endif //////////////////////////////////////////////////////////////////////////////// /// Get the rootsys directory in the installation. Static utility function. const TString& TROOT::GetRootSys() { - // Avoid returning a reference to a temporary because of the conversion - // between std::string and TString. - const static TString rootsys = ROOT::FoundationUtils::GetRootSys(); - return rootsys; + return ROOT::Internal::GetResourceDirs().rootsys; } //////////////////////////////////////////////////////////////////////////////// /// Get the binary directory in the installation. Static utility function. -const TString& TROOT::GetBinDir() { -#ifdef ROOTBINDIR - if (IgnorePrefix()) { -#endif - static TString rootbindir; - if (rootbindir.IsNull()) { - rootbindir = "bin"; - gSystem->PrependPathName(GetRootSys(), rootbindir); - } - return rootbindir; -#ifdef ROOTBINDIR - } else { - const static TString rootbindir = ROOTBINDIR; - return rootbindir; - } -#endif +const TString &TROOT::GetBinDir() +{ + return ROOT::Internal::GetResourceDirs().bin; } //////////////////////////////////////////////////////////////////////////////// @@ -3338,6 +3363,9 @@ const TString &TROOT::GetSharedLibDir() #endif + // Normalize to get rid of the "../" in relative paths + rootlibdir = fs::path{rootlibdir.Data()}.lexically_normal().string().c_str(); + return rootlibdir; } @@ -3346,117 +3374,46 @@ const TString &TROOT::GetSharedLibDir() const TString &TROOT::GetIncludeDir() { - static TString rootincdir; - - if (!rootincdir.IsNull()) - return rootincdir; - - namespace fs = std::filesystem; - - // The shared library directory can be found automatically, because the - // libCore is loaded by definition when using TROOT. It's used as the anchor - // to resolve the ROOT include directory, using the correct relative path - // for either the build or install tree. - fs::path libPath = GetSharedLibDir().Data(); - - // Check if we are in the build tree using the build tree marker file - const bool isBuildTree = fs::exists(libPath / "root-build-tree-marker"); - - fs::path includePath = isBuildTree ? "../include" : INSTALL_LIB_TO_INCLUDE; - - // The INSTALL_LIB_TO_INCLUDE might already be absolute - if (!includePath.is_absolute()) { - includePath = libPath / includePath; - } - - // Normalize to get rid of the "../" in relative paths - rootincdir = includePath.lexically_normal().string(); - - return rootincdir; + return ROOT::Internal::GetResourceDirs().include; } //////////////////////////////////////////////////////////////////////////////// /// Get the sysconfig directory in the installation. Static utility function. -const TString& TROOT::GetEtcDir() { - // Avoid returning a reference to a temporary because of the conversion - // between std::string and TString. - const static TString etcdir = ROOT::FoundationUtils::GetEtcDir(); - return etcdir; +const TString &TROOT::GetEtcDir() +{ + return ROOT::Internal::GetResourceDirs().etc; } //////////////////////////////////////////////////////////////////////////////// /// Get the data directory in the installation. Static utility function. const TString& TROOT::GetDataDir() { -#ifdef ROOTDATADIR - if (IgnorePrefix()) { -#endif - return GetRootSys(); -#ifdef ROOTDATADIR - } else { - const static TString rootdatadir = ROOTDATADIR; - return rootdatadir; - } -#endif + return ROOT::Internal::GetResourceDirs().data; } //////////////////////////////////////////////////////////////////////////////// /// Get the documentation directory in the installation. Static utility function. -const TString& TROOT::GetDocDir() { -#ifdef ROOTDOCDIR - if (IgnorePrefix()) { -#endif - return GetRootSys(); -#ifdef ROOTDOCDIR - } else { - const static TString rootdocdir = ROOTDOCDIR; - return rootdocdir; - } -#endif +const TString &TROOT::GetDocDir() +{ + return ROOT::Internal::GetResourceDirs().docs; } //////////////////////////////////////////////////////////////////////////////// /// Get the macro directory in the installation. Static utility function. -const TString& TROOT::GetMacroDir() { -#ifdef ROOTMACRODIR - if (IgnorePrefix()) { -#endif - static TString rootmacrodir; - if (rootmacrodir.IsNull()) { - rootmacrodir = "macros"; - gSystem->PrependPathName(GetRootSys(), rootmacrodir); - } - return rootmacrodir; -#ifdef ROOTMACRODIR - } else { - const static TString rootmacrodir = ROOTMACRODIR; - return rootmacrodir; - } -#endif +const TString &TROOT::GetMacroDir() +{ + return ROOT::Internal::GetResourceDirs().macros; } //////////////////////////////////////////////////////////////////////////////// /// Get the tutorials directory in the installation. Static utility function. -const TString& TROOT::GetTutorialDir() { -#ifdef ROOTTUTDIR - if (IgnorePrefix()) { -#endif - static TString roottutdir; - if (roottutdir.IsNull()) { - roottutdir = "tutorials"; - gSystem->PrependPathName(GetRootSys(), roottutdir); - } - return roottutdir; -#ifdef ROOTTUTDIR - } else { - const static TString roottutdir = ROOTTUTDIR; - return roottutdir; - } -#endif +const TString &TROOT::GetTutorialDir() +{ + return ROOT::Internal::GetResourceDirs().tutorials; } //////////////////////////////////////////////////////////////////////////////// @@ -3483,43 +3440,17 @@ const TString& TROOT::GetSourceDir() { //////////////////////////////////////////////////////////////////////////////// /// Get the icon path in the installation. Static utility function. -const TString& TROOT::GetIconPath() { -#ifdef ROOTICONPATH - if (IgnorePrefix()) { -#endif - static TString rooticonpath; - if (rooticonpath.IsNull()) { - rooticonpath = "icons"; - gSystem->PrependPathName(GetRootSys(), rooticonpath); - } - return rooticonpath; -#ifdef ROOTICONPATH - } else { - const static TString rooticonpath = ROOTICONPATH; - return rooticonpath; - } -#endif +const TString &TROOT::GetIconPath() +{ + return ROOT::Internal::GetResourceDirs().icons; } //////////////////////////////////////////////////////////////////////////////// /// Get the fonts directory in the installation. Static utility function. -const TString& TROOT::GetTTFFontDir() { -#ifdef TTFFONTDIR - if (IgnorePrefix()) { -#endif - static TString ttffontdir; - if (ttffontdir.IsNull()) { - ttffontdir = "fonts"; - gSystem->PrependPathName(GetRootSys(), ttffontdir); - } - return ttffontdir; -#ifdef TTFFONTDIR - } else { - const static TString ttffontdir = TTFFONTDIR; - return ttffontdir; - } -#endif +const TString &TROOT::GetTTFFontDir() +{ + return ROOT::Internal::GetResourceDirs().fonts; } //////////////////////////////////////////////////////////////////////////////// diff --git a/core/base/src/TSystem.cxx b/core/base/src/TSystem.cxx index 9eef2c91eaec3..abad5278ff71d 100644 --- a/core/base/src/TSystem.cxx +++ b/core/base/src/TSystem.cxx @@ -3767,6 +3767,10 @@ int TSystem::CompileMacro(const char *filename, Option_t *opt, // libraries via ACLiC in the same session and have them depend on each other without // the need to set further environment variables. cmd.ReplaceAll("$RPath", "-Wl,-rpath," + gROOT->GetSharedLibDir() + " -Wl,-rpath," + build_loc); + // $BinDir and $LibDir are not mentioned in the docs, but used internally to + // build the absolute paths to ROOTs directories + cmd.ReplaceAll("$BinDir", gROOT->GetBinDir()); + cmd.ReplaceAll("$LibDir", gROOT->GetLibDir()); TString optdebFlags; if (mode & kDebug) optdebFlags = fFlagsDebug + " "; diff --git a/core/base/test/IncludePathTest.cxx b/core/base/test/IncludePathTest.cxx index b5f0fbadb0fd2..0cf8d952e0458 100644 --- a/core/base/test/IncludePathTest.cxx +++ b/core/base/test/IncludePathTest.cxx @@ -8,7 +8,6 @@ TEST(TSystem, IncludePath) ASSERT_TRUE(gSystem); gSystem->AddIncludePath("-I /some/path/with-xin-it -I ./some/relative-path"); - gSystem->AddIncludePath("-I %ROOTSYS%\\include -I ${ROOTSYS}/include"); #ifdef WIN32 gSystem->AddIncludePath( "-I \"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.38.33130\\include\""); diff --git a/core/foundation/res/ROOT/FoundationUtils.hxx b/core/foundation/res/ROOT/FoundationUtils.hxx index c4ba65cfe093c..1e69f3b43a3da 100644 --- a/core/foundation/res/ROOT/FoundationUtils.hxx +++ b/core/foundation/res/ROOT/FoundationUtils.hxx @@ -60,16 +60,6 @@ namespace FoundationUtils { return gEnvPathSeparator; } - ///\returns the fallback directory in the installation (eg. /usr/local/root/). - const std::string& GetFallbackRootSys(); - - ///\returns the rootsys directory in the installation. - /// - const std::string& GetRootSys(); - - ///\returns the sysconfig directory in the installation. - const std::string& GetEtcDir(); - ///\returns true if lowercase \c value is 1, on, true, 0, off, false bool CanConvertEnvValueToBool(const std::string& value); diff --git a/core/foundation/src/FoundationUtils.cxx b/core/foundation/src/FoundationUtils.cxx index b1b73b2d47fe6..a099f0c0eab14 100644 --- a/core/foundation/src/FoundationUtils.cxx +++ b/core/foundation/src/FoundationUtils.cxx @@ -109,89 +109,6 @@ void ConvertToUnixPath(std::string& Path) { std::replace(Path.begin(), Path.end(), '\\', '/'); } -const std::string& GetFallbackRootSys() { - static std::string fallback; - if (!fallback.empty()) - return fallback; - -#if defined(WIN32) || defined(__FreeBSD__) - auto parent_path = [](std::string path) { - return path.substr(0, path.find_last_of("/\\")); - }; -#endif - -#ifdef WIN32 - static char lpFilename[_MAX_PATH]; - if (::GetModuleFileNameA( - NULL, // handle to module to find filename for - lpFilename, // pointer to buffer to receive module path - sizeof(lpFilename))) { // size of buffer, in characters - fallback = parent_path(parent_path(lpFilename)); - } -#elif defined __FreeBSD__ - procstat* ps = procstat_open_sysctl(); // - kinfo_proc* kp = kinfo_getproc(getpid()); - - char lpFilename[PATH_MAX] = ""; - if (kp!=NULL) { - procstat_getpathname(ps, kp, lpFilename, sizeof(lpFilename)); - fallback = parent_path(parent_path({lpFilename})); - } - - free(kp); - procstat_close(ps); -#else - // FIXME: We should not hardcode this path. We can use a similar to the - // windows technique to get the path to the executable. The easiest way - // to do this is to depend on LLVMSupport and use getMainExecutable. - fallback = "/usr/local/root"; -#endif - return fallback; -} - -#ifdef ROOTPREFIX -static bool IgnorePrefix() { - static bool ignorePrefix = std::getenv("ROOTIGNOREPREFIX"); - return ignorePrefix; -} -#endif - -const std::string& GetRootSys() { -#ifdef ROOTPREFIX - if (!IgnorePrefix()) { - const static std::string rootsys = ROOTPREFIX; - return rootsys; - } -#endif - static std::string rootsys; - if (rootsys.empty()) { - if (const char* envValue = std::getenv("ROOTSYS")) { - rootsys = envValue; -#ifndef WIN32 - // We cannot use gSystem->UnixPathName. - ConvertToUnixPath(rootsys); -#endif - } - } - // FIXME: Should this also call UnixPathName for consistency? - if (rootsys.empty()) - rootsys = GetFallbackRootSys(); - return rootsys; -} - -const std::string& GetEtcDir() { -#ifdef ROOTETCDIR - if (!IgnorePrefix()) { - const static std::string rootetcdir = ROOTETCDIR; - return rootetcdir; - } -#endif - - const static std::string rootetcdir = - GetRootSys() + GetPathSeparator() + "etc" + GetPathSeparator(); - return rootetcdir; -} - static std::string str_tolower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); }); diff --git a/core/unix/src/TUnixSystem.cxx b/core/unix/src/TUnixSystem.cxx index 17efe4c182da5..12628992bb474 100644 --- a/core/unix/src/TUnixSystem.cxx +++ b/core/unix/src/TUnixSystem.cxx @@ -464,32 +464,6 @@ static const char *GetExePath() return exepath; } -#if defined(HAVE_DLADDR) && !defined(R__MACOSX) -//////////////////////////////////////////////////////////////////////////////// - -static void SetRootSys() -{ -#ifdef ROOTPREFIX - if (gSystem->Getenv("ROOTIGNOREPREFIX")) { -#endif - void *addr = (void *)SetRootSys; - Dl_info info; - if (dladdr(addr, &info) && info.dli_fname && info.dli_fname[0]) { - char respath[kMAXPATHLEN]; - if (!realpath(info.dli_fname, respath)) { - if (!gSystem->Getenv("ROOTSYS")) - ::SysError("TUnixSystem::SetRootSys", "error getting realpath of libCore, please set ROOTSYS in the shell"); - } else { - TString rs = gSystem->GetDirName(respath); - gSystem->Setenv("ROOTSYS", gSystem->GetDirName(rs.Data()).Data()); - } - } -#ifdef ROOTPREFIX - } -#endif -} -#endif - #if defined(R__MACOSX) static TString gLinkedDylibs; @@ -513,24 +487,6 @@ static void DylibAdded(const struct mach_header *mh, intptr_t /* vmaddr_slide */ TRegexp sovers = "libCore\\.[0-9]+\\.*[0-9]*\\.*[0-9]*\\.so"; TRegexp dyvers = "libCore\\.[0-9]+\\.*[0-9]*\\.*[0-9]*\\.dylib"; -#ifdef ROOTPREFIX - if (gSystem->Getenv("ROOTIGNOREPREFIX")) { -#endif - if (lib.EndsWith("libCore.dylib") || lib.EndsWith("libCore.so") || - lib.Index(sovers) != kNPOS || lib.Index(dyvers) != kNPOS) { - char respath[kMAXPATHLEN]; - if (!realpath(lib, respath)) { - if (!gSystem->Getenv("ROOTSYS")) - ::SysError("TUnixSystem::DylibAdded", "error getting realpath of libCore, please set ROOTSYS in the shell"); - } else { - TString rs = gSystem->GetDirName(respath); - gSystem->Setenv("ROOTSYS", gSystem->GetDirName(rs.Data()).Data()); - } - } -#ifdef ROOTPREFIX - } -#endif - // when libSystem.B.dylib is loaded we have finished loading all dylibs // explicitly linked against the executable. Additional dylibs // come when they are explicitly linked against loaded so's, currently @@ -616,16 +572,10 @@ Bool_t TUnixSystem::Init() UnixSignal(kSigUser2, SigHandler); #if defined(R__MACOSX) - // trap loading of all dylibs to register dylib name, - // sets also ROOTSYS if built without ROOTPREFIX + // trap loading of all dylibs to register dylib name _dyld_register_func_for_add_image(DylibAdded); -#elif defined(HAVE_DLADDR) - SetRootSys(); #endif - // This is a fallback in case TROOT::GetRootSys() can't determine ROOTSYS - gRootDir = ROOT::FoundationUtils::GetFallbackRootSys().c_str(); - return kFALSE; } diff --git a/core/winnt/src/TWinNTSystem.cxx b/core/winnt/src/TWinNTSystem.cxx index 98d10a5319893..ad9df24e27322 100644 --- a/core/winnt/src/TWinNTSystem.cxx +++ b/core/winnt/src/TWinNTSystem.cxx @@ -827,9 +827,7 @@ namespace { // determine the fileopen.C file path: TString fileopen = "fileopen.C"; - TString rootmacrodir = "macros"; - sys->PrependPathName(std::getenv("ROOTSYS"), rootmacrodir); - sys->PrependPathName(rootmacrodir.Data(), fileopen); + sys->PrependPathName(gROOT->GetMacroDir().Data(), fileopen); if (regROOTwrite) { // only write to registry if fileopen.C is readable @@ -1004,40 +1002,6 @@ TWinNTSystem::TWinNTSystem() : TSystem("WinNT", "WinNT System") char *buf = new char[MAX_MODULE_NAME32 + 1]; -#ifdef ROOTPREFIX - if (gSystem->Getenv("ROOTIGNOREPREFIX")) { -#endif - // set ROOTSYS - HMODULE hModCore = ::GetModuleHandle("libCore.dll"); - if (hModCore) { - ::GetModuleFileName(hModCore, buf, MAX_MODULE_NAME32 + 1); - char *pLibName = strstr(buf, "libCore.dll"); - if (pLibName) { - --pLibName; // skip trailing \\ or / - while (--pLibName >= buf && *pLibName != '\\' && *pLibName != '/'); - *pLibName = 0; // replace trailing \\ or / with 0 - TString check_path = buf; - check_path += "\\etc"; - // look for $ROOTSYS (it should contain the "etc" subdirectory) - while (buf[0] && GetFileAttributes(check_path.Data()) == INVALID_FILE_ATTRIBUTES) { - while (--pLibName >= buf && *pLibName != '\\' && *pLibName != '/'); - *pLibName = 0; - check_path = buf; - check_path += "\\etc"; - } - if (buf[0]) { - Setenv("ROOTSYS", buf); - TString path = buf; - path += "\\bin;"; - path += Getenv("PATH"); - Setenv("PATH", path.Data()); - } - } - } -#ifdef ROOTPREFIX - } -#endif - UpdateRegistry(this, buf); delete [] buf; @@ -1104,9 +1068,6 @@ Bool_t TWinNTSystem::Init() fSigcnt = 0; - // This is a fallback in case TROOT::GetRootSys() can't determine ROOTSYS - gRootDir = ROOT::FoundationUtils::GetFallbackRootSys().c_str(); - // Increase the accuracy of Sleep() without needing to link to winmm.lib typedef UINT (WINAPI* LPTIMEBEGINPERIOD)( UINT uPeriod ); HINSTANCE hInstWinMM = LoadLibrary( "winmm.dll" ); @@ -4318,6 +4279,16 @@ const char *TWinNTSystem::GetLibraries(const char *regexp, const char *options, while (getline(libenv, str, ';')) { libpaths.push_back(str); } + // Also seed libpaths with ROOT's own import-library directory and the + // shared-library directory. + if (gROOT) { + TString rootLibDir = gROOT->GetLibDir(); + if (!rootLibDir.IsNull()) + libpaths.push_back(rootLibDir.Data()); + TString rootSharedLibDir = gROOT->GetSharedLibDir(); + if (!rootSharedLibDir.IsNull() && rootSharedLibDir != rootLibDir) + libpaths.push_back(rootSharedLibDir.Data()); + } // now get the list of libraries std::stringstream libraries(libs.Data()); while (getline(libraries, str, ' ')) { diff --git a/io/io/src/TFile.cxx b/io/io/src/TFile.cxx index bc94d64afc7ab..a6f30498b9b00 100644 --- a/io/io/src/TFile.cxx +++ b/io/io/src/TFile.cxx @@ -3245,6 +3245,12 @@ void TFile::MakeProject(const char *dirname, const char * /*classes*/, cmd.ReplaceAll("$LibName",sdirname); cmd.ReplaceAll("$BuildDir","."); cmd.ReplaceAll("$RPath", "-Wl,-rpath," + gROOT->GetSharedLibDir()); + // $BinDir and $LibDir are not mentioned in the docs, but used internally + // (e.g. on Windows MAKESHAREDLIB) to build absolute paths to ROOT's + // directories. TSystem::CompileMacro substitutes them, we mirror that here + // so the MAKEP script generated by MakeProject is self-contained. + cmd.ReplaceAll("$BinDir", gROOT->GetBinDir()); + cmd.ReplaceAll("$LibDir", gROOT->GetLibDir()); TString sOpt; TString rootbuild = ROOTBUILD; if (rootbuild.Index("debug",0,TString::kIgnoreCase)==kNPOS) { diff --git a/main/src/rmain.cxx b/main/src/rmain.cxx index a4236e028216f..9aa2a07f44536 100644 --- a/main/src/rmain.cxx +++ b/main/src/rmain.cxx @@ -17,6 +17,7 @@ // // ////////////////////////////////////////////////////////////////////////// +#include "TROOT.h" // for ROOT::Internal::GetResourceDirs() #include "TRint.h" #include "RConfigure.h" #include "snprintf.h" @@ -40,14 +41,7 @@ void handle_notebook_option(int argc, char **argv) } if (notebook > 0) { // Build command -#ifdef ROOTBINDIR - if (std::getenv("ROOTIGNOREPREFIX")) -#endif - snprintf(arg0, sizeof(arg0), "%s/bin/%s", std::getenv("ROOTSYS"), ROOTNBBINARY); -#ifdef ROOTBINDIR - else - snprintf(arg0, sizeof(arg0), "%s/%s", ROOTBINDIR, ROOTNBBINARY); -#endif + snprintf(arg0, sizeof(arg0), "%s/%s", ROOT::Internal::GetResourceDirs().bin.Data(), ROOTNBBINARY); int numnbargs = 1 + (argc - notebook); diff --git a/roottest/CMakeLists.txt b/roottest/CMakeLists.txt index 25421595079eb..f314b1c6b14d5 100644 --- a/roottest/CMakeLists.txt +++ b/roottest/CMakeLists.txt @@ -87,11 +87,6 @@ else() message(FATAL_ERROR "-- Check for bitness: no support for ${CMAKE_SIZEOF_VOID_P}*8 bit processors.") endif() -# Setup environment. -if (gnuinstall) - set(ROOTTEST_ENVIRONMENT ROOTIGNOREPREFIX=1) -endif() - # Set some variables that customizes the behaviour of the ROOT macros set(CMAKE_ROOTTEST_DICT ON) diff --git a/roottest/root/io/namespacedict/CMakeLists.txt b/roottest/root/io/namespacedict/CMakeLists.txt index 5814242d81b44..39a43229ab736 100644 --- a/roottest/root/io/namespacedict/CMakeLists.txt +++ b/roottest/root/io/namespacedict/CMakeLists.txt @@ -1,8 +1,8 @@ if(MSVC) - set(command ${CMAKE_COMMAND} -E env "ROOTIGNOREPREFIX=1" ${CMAKE_BINARY_DIR}/bin/rootcling.exe) + set(command ${CMAKE_BINARY_DIR}/bin/rootcling.exe) else() set(command ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib:$ENV{LD_LIBRARY_PATH}" - "ROOTIGNOREPREFIX=1" ${CMAKE_BINARY_DIR}/bin/rootcling) + ${CMAKE_BINARY_DIR}/bin/rootcling) endif() ROOTTEST_ADD_TEST(HeaderDict diff --git a/rootx/CMakeLists.txt b/rootx/CMakeLists.txt index 959281902454e..57abdb794cfb3 100644 --- a/rootx/CMakeLists.txt +++ b/rootx/CMakeLists.txt @@ -15,6 +15,10 @@ if (CMAKE_SYSTEM_NAME MATCHES FreeBSD) target_link_libraries(root PRIVATE util procstat) endif() +if(ROOT_NEED_STDCXXFS) + target_link_libraries(root PRIVATE stdc++fs) +endif() + generateHeader(root ${CMAKE_SOURCE_DIR}/core/base/src/root-argparse.py ${CMAKE_BINARY_DIR}/ginclude/rootCommandLineOptionsHelp.h diff --git a/rootx/src/rootx.cxx b/rootx/src/rootx.cxx index a2eba1603752e..4089dd6131626 100644 --- a/rootx/src/rootx.cxx +++ b/rootx/src/rootx.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -95,26 +96,6 @@ static const char *GetExePath() return exepath.c_str(); } -static void SetRootSys() -{ - const char *exepath = GetExePath(); - if (exepath && *exepath) { - std::string epStr{exepath}; - char *ep = epStr.data(); - char *s; - if ((s = strrchr(ep, '/'))) { - *s = 0; - if ((s = strrchr(ep, '/'))) { - *s = 0; - int l2 = strlen(ep) + 10; - char *env = new char[l2]; - snprintf(env, l2, "ROOTSYS=%s", ep); - putenv(env); // NOLINT: allocated memory now used by environment variable - } - } - } -} - extern "C" { static void SigTerm(int); } @@ -163,21 +144,6 @@ int main(int argc, char **argv) char **argvv; char arg0[kMAXPATHLEN]; -#ifdef ROOTPREFIX - if (std::getenv("ROOTIGNOREPREFIX")) { -#endif - // Try to set ROOTSYS depending on pathname of the executable - SetRootSys(); - - if (!std::getenv("ROOTSYS")) { - fprintf(stderr, "%s: ROOTSYS not set. Set it before trying to run %s.\n", - argv[0], argv[0]); - return 1; - } -#ifdef ROOTPREFIX - } -#endif - // In batch mode don't show splash screen, idem for no logo mode, // in about mode show always splash screen int i; @@ -242,14 +208,10 @@ int main(int argc, char **argv) // Build argv vector argvv = new char* [argc+1]; -#ifdef ROOTBINDIR - if (std::getenv("ROOTIGNOREPREFIX")) -#endif - snprintf(arg0, sizeof(arg0), "%s/bin/%s", std::getenv("ROOTSYS"), ROOTBINARY); -#ifdef ROOTBINDIR - else - snprintf(arg0, sizeof(arg0), "%s/%s", ROOTBINDIR, ROOTBINARY); -#endif + // The root binary is in the same path by construction + namespace fs = std::filesystem; + fs::path p{GetExePath()}; + snprintf(arg0, sizeof(arg0), "%s/%s", p.parent_path().string().c_str(), ROOTBINARY); argvv[0] = arg0; for (i = 1; i < argc; i++) diff --git a/tmva/sofie/test/CMakeLists.txt b/tmva/sofie/test/CMakeLists.txt index 2012cb32b90a0..73c3b0e7e78e0 100644 --- a/tmva/sofie/test/CMakeLists.txt +++ b/tmva/sofie/test/CMakeLists.txt @@ -40,7 +40,7 @@ ROOTTEST_GENERATE_EXECUTABLE(emitFromONNX EmitFromONNX_all.cxx target_compile_options(emitFromONNX PRIVATE -Wno-unused-parameter -Wno-array-bounds) ROOTTEST_ADD_TEST(SofieCompileModels_ONNX - COMMAND ${CMAKE_COMMAND} -E env ROOTIGNOREPREFIX=1 ./emitFromONNX ${onnx_file} ${CMAKE_CURRENT_BINARY_DIR}/${fname} + COMMAND ./emitFromONNX ${onnx_file} ${CMAKE_CURRENT_BINARY_DIR}/${fname} FIXTURES_REQUIRED sofie-compile-models-onnx-build FIXTURES_SETUP sofie-compile-models-onnx ) @@ -75,7 +75,7 @@ target_compile_options(emitFromROOT PRIVATE -Wno-unused-parameter -Wno-array-bou # Automatic compilation of headers from root files ROOTTEST_ADD_TEST(SofieCompileModels_ROOT - COMMAND ${CMAKE_COMMAND} -E env ROOTIGNOREPREFIX=1 ./emitFromROOT + COMMAND ./emitFromROOT FIXTURES_REQUIRED sofie-compile-models-onnx-root FIXTURES_SETUP sofie-compile-models-root ) diff --git a/tree/ntuple/test/CMakeLists.txt b/tree/ntuple/test/CMakeLists.txt index 96b080a35f1ee..2167dd16acc95 100644 --- a/tree/ntuple/test/CMakeLists.txt +++ b/tree/ntuple/test/CMakeLists.txt @@ -115,10 +115,10 @@ ROOT_GENERATE_DICTIONARY(StreamerFieldDict ${CMAKE_CURRENT_SOURCE_DIR}/StreamerF DEPENDENCIES RIO) if(MSVC) - set(command ${CMAKE_COMMAND} -E env "ROOTIGNOREPREFIX=1" $) + set(command $) else() set(command ${CMAKE_COMMAND} -E env "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib:$ENV{LD_LIBRARY_PATH}" - "ROOTIGNOREPREFIX=1" $) + $) endif() configure_file(StreamerFieldXML.h . COPYONLY) From 16434dfacea09646b5926ebf0b12ad2c17680ec3 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sat, 18 Apr 2026 16:01:55 +0200 Subject: [PATCH 3/3] [core] Warn if auto-determined RootSys differs from `ROOTSYS` env var Example of how the warning looks like if it happens: ```txt [I] omen nix-shell ~/c/r/root_build > ROOTSYS=/home/rembserj/ ./bin/root Warning in : ROOTSYS is set but inconsistent with detected ROOT installation: ROOTSYS=/home/rembserj/ Detected=/home/rembserj/code/root/root_build ROOT will use the detected installation. ------------------------------------------------------------------ | Welcome to ROOT 6.41.01 https://root.cern | | (c) 1995-2025, The ROOT Team; conception: R. Brun, F. Rademakers | | Built for linuxx8664gcc on Jan 01 1980, 00:00:00 | | From heads/ignoreprefix@v6-99-99-368-gd1d33ba04be | | With clang version 21.1.8 std202002 | | Try '.help'/'.?', '.demo', '.license', '.credits', '.quit'/'.q' | ------------------------------------------------------------------ root [0] ``` --- core/base/src/TROOT.cxx | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 51c3a56dd5518..4e4312a87023a 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -783,6 +783,40 @@ Int_t gDebug; TROOT::TROOT() : TDirectory() {} +namespace { + +void WarnIfInconsistentRootSys() +{ + namespace fs = std::filesystem; + + const char *envRootSys = std::getenv("ROOTSYS"); + if (!envRootSys) { + return; + } + bool isConsistent = false; + // Should never throw because it's a valid path + fs::path detected = fs::canonical(TROOT::GetRootSys().Data()); + try { + fs::path fromEnv = fs::canonical(envRootSys); + if (detected == fromEnv) { + isConsistent = true; + } + } catch (const fs::filesystem_error &) { + // Happens if the path in ROOTSYS fails to canonicalize, e.g. because the + // directory doesn't exist. In that case, we also print the warning. + } + if (isConsistent) + return; + Warning("TROOT", + "ROOTSYS is set but inconsistent with detected ROOT installation:\n" + " ROOTSYS=%s\n" + " Detected=%s\n" + "ROOT will use the detected installation.", + envRootSys, detected.string().c_str()); +} + +} // namespace + //////////////////////////////////////////////////////////////////////////////// /// Initialize the ROOT system. The creation of the TROOT object initializes /// the ROOT system. It must be the first ROOT related action that is @@ -839,6 +873,10 @@ TROOT::TROOT(const char *name, const char *title, VoidFuncPtr_t *initfunc) : TDi GetIconPath(); GetTTFFontDir(); + // Warn if ROOTSYS is in the environment, but it's inconsistent with what + // TROOT has figured out. + WarnIfInconsistentRootSys(); + gRootDir = GetRootSys().Data(); TDirectory::BuildDirectory(nullptr, nullptr);