From dd7cb07d96982335a129c5dcd17f0cf91589f1fa 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 bd84268f2d64b..f894d95fbf46d 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 7fc8283af3635..ec41430f2e156 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -132,6 +132,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 0cb8b127bc39b0ba433781ce5c90cb97744ddd06 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 | 4 +- config/RConfigure.in | 16 -- core/base/CMakeLists.txt | 44 +++- core/base/inc/TROOT.h | 15 ++ 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 | 2 +- tree/ntuple/test/CMakeLists.txt | 4 +- 22 files changed, 170 insertions(+), 428 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f6b90b10453..08b36a201456a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -505,7 +505,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 @@ -538,10 +538,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 @@ -567,7 +567,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 864f5f6c81a2d..d61ad0a4c4d3e 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..a0ed6bfab2e2f 100644 --- a/cmake/scripts/compiledata.win32.in +++ b/cmake/scripts/compiledata.win32.in @@ -5,11 +5,11 @@ #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 a15e94a8262e3..0edbe6c9fd6ea 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 f6b8a0be390cd..496bdf629eb84 100644 --- a/core/base/CMakeLists.txt +++ b/core/base/CMakeLists.txt @@ -242,20 +242,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..ac3571a00ce74 100644 --- a/core/base/inc/TROOT.h +++ b/core/base/inc/TROOT.h @@ -397,6 +397,21 @@ namespace ROOT { inline Bool_t RequiresCleanup(TObject &obj) { return obj.TestBit(kIsReferenced) && obj.GetUniqueID() == 0; } + + struct ResourceDirs { + 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..21dff2084801d 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() +{ + // The initialization of this static is thread-safe (guaranteed by the C++11 + // standard for function-local statics), so the in-place lambda initializes + // the resource directories exactly once. + static const ResourceDirs dirs = []() { + // Initialize resource directories with the correct relative paths for the + // build tree or the install tree. + ResourceDirs dirs; + 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); + return dirs; + }(); + + 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 12a79480a7ff9..954631fe219f6 100644 --- a/core/base/src/TSystem.cxx +++ b/core/base/src/TSystem.cxx @@ -3772,6 +3772,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 993c3f0cb6ab6..1f83c8fcfc848 100644 --- a/io/io/src/TFile.cxx +++ b/io/io/src/TFile.cxx @@ -3247,6 +3247,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 3134f42508411..95b5b46eb2c18 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 d6ceb48cedb86..4b73e7b8eda9f 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() + target_include_directories(root PRIVATE ${CMAKE_SOURCE_DIR}/core/foundation/inc ${CMAKE_SOURCE_DIR}/core/base/inc diff --git a/rootx/src/rootx.cxx b/rootx/src/rootx.cxx index 2c640e05183bb..73e2ad723b8ad 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); } @@ -158,21 +139,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; @@ -237,14 +203,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 6d26ffe1e7ad2..d6a895426c4cb 100644 --- a/tmva/sofie/test/CMakeLists.txt +++ b/tmva/sofie/test/CMakeLists.txt @@ -89,7 +89,7 @@ if (ONNX_MODELS_DIR) endif() ROOTTEST_ADD_TEST(SofieCompileModels_ONNX - COMMAND ${CMAKE_COMMAND} -E env ROOTIGNOREPREFIX=1 ./emitFromONNX + COMMAND ./emitFromONNX FIXTURES_REQUIRED ${compile_models_fixtures} FIXTURES_SETUP sofie-compile-models-onnx ) diff --git a/tree/ntuple/test/CMakeLists.txt b/tree/ntuple/test/CMakeLists.txt index eb245a818a836..e6b90c2ba7849 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 8f629e68914a36c0b3054e01640c3f62a65e0100 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 21dff2084801d..5246a18d0351a 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);