diff --git a/core/base/src/TPluginManager.cxx b/core/base/src/TPluginManager.cxx index 9a20d9207be80..4ad5a77936cb1 100644 --- a/core/base/src/TPluginManager.cxx +++ b/core/base/src/TPluginManager.cxx @@ -97,6 +97,8 @@ TFile etc. functionality. #include "TObjArray.h" #include "ThreadLocalStorage.h" +#include + #include #include @@ -513,11 +515,7 @@ void TPluginManager::LoadHandlersFromPluginDirs(const char *base) plugindirs = "plugins"; gSystem->PrependPathName(TROOT::GetEtcDir(), plugindirs); } -#ifdef WIN32 - TObjArray *dirs = plugindirs.Tokenize(";"); -#else - TObjArray *dirs = plugindirs.Tokenize(":"); -#endif + TObjArray *dirs = plugindirs.Tokenize(TString(ROOT::FoundationUtils::GetEnvPathSeparator())); TString d; for (Int_t i = 0; i < dirs->GetEntriesFast(); i++) { d = ((TObjString*)dirs->At(i))->GetString(); diff --git a/core/base/src/TROOT.cxx b/core/base/src/TROOT.cxx index 397d04d376295..0dcb42aa195c3 100644 --- a/core/base/src/TROOT.cxx +++ b/core/base/src/TROOT.cxx @@ -2934,18 +2934,14 @@ const char *TROOT::GetMacroPath() TString ¯oPath = ROOT::GetMacroPath(); if (macroPath.Length() == 0) { + const TString sep(ROOT::FoundationUtils::GetEnvPathSeparator()); macroPath = gEnv->GetValue("Root.MacroPath", (char*)nullptr); -#if defined(R__WIN32) - macroPath.ReplaceAll("; ", ";"); -#else - macroPath.ReplaceAll(": ", ":"); -#endif + // Drop the blank that may follow a separator in the rootrc value, so that + // "Root.MacroPath: .: $(HOME)/macros" does not yield a path element + // starting with a space. + macroPath.ReplaceAll(sep + " ", sep); if (macroPath.Length() == 0) -#if !defined(R__WIN32) - macroPath = ".:" + TROOT::GetMacroDir(); -#else - macroPath = ".;" + TROOT::GetMacroDir(); -#endif + macroPath = "." + sep + TROOT::GetMacroDir(); } return macroPath; diff --git a/core/base/test/CMakeLists.txt b/core/base/test/CMakeLists.txt index c60aa85eb9992..6e16d3214ee86 100644 --- a/core/base/test/CMakeLists.txt +++ b/core/base/test/CMakeLists.txt @@ -26,7 +26,8 @@ ROOT_ADD_GTEST(CoreEnvTests TEnvTests.cxx LIBRARIES Core) ROOT_ADD_GTEST(CoreErrorTests TErrorTests.cxx LIBRARIES Core) -ROOT_ADD_GTEST(CoreSystemTests TSystemTests.cxx LIBRARIES Core) +# INCLUDE_DIRS: for ROOT/FoundationUtils.hxx +ROOT_ADD_GTEST(CoreSystemTests TSystemTests.cxx LIBRARIES Core INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/core/foundation/res) ROOT_ADD_GTEST(CoreCryptoRandomTest CryptoRandomTest.cxx LIBRARIES Core) diff --git a/core/base/test/TSystemTests.cxx b/core/base/test/TSystemTests.cxx index b33ddbf253fde..5d48ed53d4a5a 100644 --- a/core/base/test/TSystemTests.cxx +++ b/core/base/test/TSystemTests.cxx @@ -2,6 +2,9 @@ #include "TSystem.h" #include "TString.h" +#include "TROOT.h" + +#include #include #include @@ -9,6 +12,8 @@ #include #include +static const char kPathSep = ROOT::FoundationUtils::GetEnvPathSeparator(); + TEST(TSystem, TempFile) { TString fname = "root_test_"; @@ -31,6 +36,63 @@ TEST(TSystem, TempFile) gSystem->Unlink(fname); } +// Count occurrences of `dir` as a full path component of `path`. +static int CountPathComponent(const TString &path, const TString &dir) +{ + int count = 0; + TString token; + Ssiz_t from = 0; + while (path.Tokenize(token, from, TString::Format("%c", kPathSep))) + if (token == dir) + ++count; + return count; +} + +// Exercise the interplay of Get/Add/SetDynamicPath as a single scenario: +// the dynamic path is process-global state, so the steps are order dependent. +TEST(TSystem, DynamicPath) +{ + const TString defaultPath = gSystem->GetDynamicPath(); + // The ROOT library directory is always part of the default path. + EXPECT_TRUE(defaultPath.Contains(TROOT::GetLibDir())) + << "default path: " << defaultPath; + + // AddDynamicPath appends the directory (at the end) ... + // (the directories do not need to exist; use names that cannot already be + // part of the default path) + const TString extraDir1 = TString::Format("%s/root-dynpath-gtest-1", gSystem->TempDirectory()); + gSystem->AddDynamicPath(extraDir1); + TString path = gSystem->GetDynamicPath(); + EXPECT_TRUE(path.EndsWith(extraDir1)) << "path: " << path; + EXPECT_EQ(1, CountPathComponent(path, extraDir1)) << "path: " << path; + // ... and keeps the rest of the path intact. + EXPECT_TRUE(path.BeginsWith(defaultPath)) << "path: " << path; + + // Appended directories accumulate in order. + const TString extraDir2 = TString::Format("%s/root-dynpath-gtest-2", gSystem->TempDirectory()); + gSystem->AddDynamicPath(extraDir2); + path = gSystem->GetDynamicPath(); + EXPECT_TRUE(path.EndsWith(TString::Format("%s%c%s", extraDir1.Data(), kPathSep, extraDir2.Data()))) + << "path: " << path; + + // AddDynamicPath(nullptr) is a no-op. + gSystem->AddDynamicPath(nullptr); + EXPECT_STREQ(path, gSystem->GetDynamicPath()); + + // SetDynamicPath freezes the path to exactly the given value. + const TString userPath = TString::Format("%s%c%s", extraDir2.Data(), kPathSep, extraDir1.Data()); + gSystem->SetDynamicPath(userPath); + EXPECT_STREQ(userPath, gSystem->GetDynamicPath()); + + // SetDynamicPath(nullptr) resets to the default: the explicitly set value + // and the previously appended directories are gone, the ROOT library + // directory is back. + gSystem->SetDynamicPath(nullptr); + path = gSystem->GetDynamicPath(); + EXPECT_TRUE(path.Contains(TROOT::GetLibDir())) << "path: " << path; + EXPECT_EQ(0, CountPathComponent(path, extraDir2)) << "path: " << path; +} + TEST(TSystem, TempFileSuffix) { TString fname = "root_suffix_test_"; diff --git a/core/clingutils/src/TClingUtils.cxx b/core/clingutils/src/TClingUtils.cxx index 416b8e8ad0a5d..6d1e08f672ee9 100644 --- a/core/clingutils/src/TClingUtils.cxx +++ b/core/clingutils/src/TClingUtils.cxx @@ -5208,11 +5208,7 @@ void ROOT::TMetaUtils::SetPathsForRelocatability(std::vector& cling if (!envInclPath) return; -#ifdef _WIN32 - constexpr char kPathSep = ';'; -#else - constexpr char kPathSep = ':'; -#endif + const char kPathSep = ROOT::FoundationUtils::GetEnvPathSeparator(); std::istringstream envInclPathsStream(envInclPath); std::string inclPath; diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index ac7400214fa20..414133088f5c7 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -5937,11 +5937,7 @@ Int_t TCling::LoadLibraryMap(const char* rootmapfile) TString ldpath = gSystem->GetDynamicPath(); if (ldpath != fRootmapLoadPath) { fRootmapLoadPath = ldpath; -#ifdef WIN32 - TObjArray* paths = ldpath.Tokenize(";"); -#else - TObjArray* paths = ldpath.Tokenize(":"); -#endif + TObjArray *paths = ldpath.Tokenize(TString(ROOT::FoundationUtils::GetEnvPathSeparator())); TString d; for (Int_t i = 0; i < paths->GetEntriesFast(); i++) { d = ((TObjString *)paths->At(i))->GetString(); diff --git a/core/rint/src/TTabCom.cxx b/core/rint/src/TTabCom.cxx index 8af741db377fb..5af69cacf2f2c 100644 --- a/core/rint/src/TTabCom.cxx +++ b/core/rint/src/TTabCom.cxx @@ -148,15 +148,13 @@ #include "Rstrstream.h" #include "strlcpy.h" +#include + #define BUF_SIZE 1024 // must be smaller than/equal to fgLineBufSize in Getline.cxx and // lineBufSize in cppcompleter.py #define IfDebug(x) if(gDebug==TTabCom::kDebug) x -#ifdef R__WIN32 -const char kDelim = ';'; -#else -const char kDelim = ':'; -#endif +const char kDelim = ROOT::FoundationUtils::GetEnvPathSeparator(); // ---------------------------------------------------------------------------- diff --git a/core/unix/src/TUnixSystem.cxx b/core/unix/src/TUnixSystem.cxx index 5fe7ac64446e5..b264566e6ab62 100644 --- a/core/unix/src/TUnixSystem.cxx +++ b/core/unix/src/TUnixSystem.cxx @@ -4552,7 +4552,8 @@ int TUnixSystem::UnixSend(int sock, const void *buffer, int length, int flag) /// path" | sed 's/.*=//g' | awk '//{print $1}')` This might be useful in scenarios, where ROOT is instantiated many /// times. -static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFALSE) +static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFALSE, + const char *addpath = nullptr) { static TString dynpath_full; static std::atomic initialized(kFALSE); @@ -4560,11 +4561,24 @@ static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFA // If we have not seen Cling but the result has been initialized and gCling // is still nullptr, the result won't change. - if (newpath == nullptr && !reset && (seenCling || (initialized && gCling == nullptr))) + if (newpath == nullptr && addpath == nullptr && !reset && + (seenCling || (initialized && gCling == nullptr))) return dynpath_full; R__LOCKGUARD2(gSystemMutex); + // Directories appended via `addpath` (TUnixSystem::AddDynamicPath). They are + // kept separately from the automatically assembled part so that they survive + // a re-assembly (e.g. the deferred insertion of the Cling provided parts). + static TString addedPaths; + + if (reset) { + addedPaths = ""; + // Re-arm the deferred insertion of the Cling provided parts (it is set + // again below if gCling is already available). + seenCling = kFALSE; + } + if (newpath) { dynpath_full = newpath; // Don't erase the user given path at the next call. @@ -4579,6 +4593,23 @@ static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFA return dynpath_full; } + if (addpath && *addpath) { + // Record the extra directory and append it to the current value. Contrary + // to a path set explicitly via SetDynamicPath (`newpath`), this must not + // set seenCling: the automatically assembled part can still be updated + // (to insert the Cling provided parts) and the extra directories are then + // re-appended below. + addedPaths += ":"; + addedPaths += addpath; + if (initialized) { + if (!dynpath_full.EndsWith(":")) + dynpath_full += ":"; + dynpath_full += addpath; + } + // Fall through: if the path was not yet assembled, or if the Cling + // provided parts can now be inserted, do it now (extrapath included). + } + // Another thread might have updated this. Even-though this is executed at the // start of the process, we might get there if the user is explicitly // 'resetting' the value. @@ -4668,7 +4699,7 @@ static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFA #endif } - if (!initialized || (!seenCling && gCling)) { + if (reset || !initialized || (!seenCling && gCling)) { dynpath_full = dynpath_envpart; if (!dynpath_full.EndsWith(":")) dynpath_full += ":"; if (gCling) { @@ -4678,6 +4709,7 @@ static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFA seenCling = kTRUE; } dynpath_full += dynpath_syspart; + dynpath_full += addedPaths; // entries carry a leading ':' initialized = kTRUE; if (gDebug > 0) std::cout << "dynpath = " << dynpath_full.Data() << std::endl; @@ -4692,10 +4724,7 @@ static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFA void TUnixSystem::AddDynamicPath(const char *path) { if (path) { - TString oldpath = DynamicPath(nullptr, kFALSE); - oldpath.Append(":"); - oldpath.Append(path); - DynamicPath(oldpath); + DynamicPath(nullptr, kFALSE, path); } } diff --git a/core/winnt/src/TWinNTSystem.cxx b/core/winnt/src/TWinNTSystem.cxx index 98d10a5319893..165a9b5cb1957 100644 --- a/core/winnt/src/TWinNTSystem.cxx +++ b/core/winnt/src/TWinNTSystem.cxx @@ -356,17 +356,45 @@ namespace { ///////////////////////////////////////////////////////////////////////////// /// Get shared library search path. - - static const char *DynamicPath(const char *newpath = 0, Bool_t reset = kFALSE) + /// The path is assembled from the ROOT_LIBRARY_PATH environment variable, + /// the PATH environment variable, the `Root.DynamicPath` resource of + /// .rootrc and the ROOT library directory. Directories can be appended via + /// `addpath` (used by TWinNTSystem::AddDynamicPath). The path may first be + /// assembled before gEnv is available (TWinNTSystem::Init() adds the + /// directory of libCore.dll); in that case it is re-assembled - keeping the + /// appended directories - as soon as gEnv exists, so that the + /// `Root.DynamicPath` resource is taken into account. + /// A path set explicitly through `newpath` (TWinNTSystem::SetDynamicPath) + /// is used verbatim and is never amended. + + static const char *DynamicPath(const char *newpath = 0, Bool_t reset = kFALSE, + const char *addpath = 0) { static TString dynpath; + static TString addedPaths; // directories appended via `addpath` + static Bool_t userpath = kFALSE; // dynpath explicitly set via SetDynamicPath() + static Bool_t sawEnv = kFALSE; // dynpath was assembled with gEnv available - if (reset || newpath) { + if (reset) { dynpath = ""; + addedPaths = ""; + userpath = kFALSE; + sawEnv = kFALSE; } if (newpath) { dynpath = newpath; - } else if (dynpath == "") { + userpath = kTRUE; + } else if (addpath && *addpath) { + if (!dynpath.IsNull()) { + dynpath += ";"; dynpath += addpath; + } + addedPaths += ";"; addedPaths += addpath; + } + if (!userpath && (dynpath.IsNull() || (!sawEnv && gEnv))) { + // (Re)assemble the path. A path assembled while gEnv was not yet + // available misses the Root.DynamicPath resource, so it is rebuilt + // here once gEnv exists. + sawEnv = (gEnv != nullptr); dynpath = gSystem->Getenv("ROOT_LIBRARY_PATH"); TString rdynpath = gEnv ? gEnv->GetValue("Root.DynamicPath", (char*)0) : ""; rdynpath.ReplaceAll("; ", ";"); // in case DynamicPath was extended @@ -384,9 +412,10 @@ namespace { dynpath += ";"; dynpath += rdynpath; } - } - if (!dynpath.Contains(TROOT::GetLibDir())) { - dynpath += ";"; dynpath += TROOT::GetLibDir(); + if (!dynpath.Contains(TROOT::GetLibDir())) { + dynpath += ";"; dynpath += TROOT::GetLibDir(); + } + dynpath += addedPaths; // entries carry a leading ';' } return dynpath; @@ -4073,10 +4102,7 @@ Int_t TWinNTSystem::RedirectOutput(const char *file, const char *mode, void TWinNTSystem::AddDynamicPath(const char *dir) { if (dir) { - TString oldpath = DynamicPath(0, kFALSE); - oldpath.Append(";"); - oldpath.Append(dir); - DynamicPath(oldpath); + DynamicPath(0, kFALSE, dir); } } diff --git a/roottest/root/core/dynamicpath/CMakeLists.txt b/roottest/root/core/dynamicpath/CMakeLists.txt new file mode 100644 index 0000000000000..6213899807483 --- /dev/null +++ b/roottest/root/core/dynamicpath/CMakeLists.txt @@ -0,0 +1,13 @@ +# Check the way the dynamic (shared library) search path is assembled from +# ROOT_LIBRARY_PATH, the system loader path and the .rootrc resource. +# +# This used to be implemented by the shell script `test_dynpath_setup.sh`; it +# is now driven by the CMake script `dynamicPathSetup.cmake` so that the test +# also runs on Windows. + +ROOTTEST_ADD_TEST(DynPathSetup + COMMAND ${CMAKE_COMMAND} + -DROOT_EXE=${ROOT_root_CMD} + -DWORKDIR=${CMAKE_CURRENT_BINARY_DIR}/rootlibpath_test + -P ${CMAKE_CURRENT_SOURCE_DIR}/dynamicPathSetup.cmake + ENVIRONMENT ROOTENV_NO_HOME=1) diff --git a/roottest/root/core/dynamicpath/dynamicPathSetup.cmake b/roottest/root/core/dynamicpath/dynamicPathSetup.cmake new file mode 100644 index 0000000000000..75fe5bda2f910 --- /dev/null +++ b/roottest/root/core/dynamicpath/dynamicPathSetup.cmake @@ -0,0 +1,242 @@ +# CMake re-implementation of the former `test_dynpath_setup.sh` shell script. +# +# It verifies the way `TSystem::GetDynamicPath()` is assembled out of +# * the `ROOT_LIBRARY_PATH` environment variable, +# * the platform specific loader search path +# (`LD_LIBRARY_PATH` / `DYLD_LIBRARY_PATH` / `PATH`), +# * the `.*.Root.DynamicPath` resource of the local `.rootrc`, +# * and the ROOT library (resp. binary) directory. +# +# Being a plain CMake script (executed via `cmake -P`) it runs on all the +# platforms supported by ROOT, including Windows. +# +# Required arguments (passed with -D): +# ROOT_EXE : full path to the `root` executable +# WORKDIR : scratch directory used to run the checks + +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) + +foreach(var ROOT_EXE WORKDIR) + if(NOT DEFINED ${var}) + message(FATAL_ERROR "${var} must be defined (cmake -D${var}=...)") + endif() +endforeach() + +#---Platform dependent settings------------------------------------------------- +# `sep` : separator used in path lists +# `rc_key` : name of the .rootrc resource holding the dynamic path +# `ld_var` : environment variable used by the system loader +if(WIN32) + set(sep ";") + set(rc_key "WinNT.*.Root.DynamicPath") + set(ld_var "PATH") +elseif(APPLE) + set(sep ":") + set(rc_key "Unix.*.Root.DynamicPath") + set(ld_var "DYLD_LIBRARY_PATH") +else() + set(sep ":") + set(rc_key "Unix.*.Root.DynamicPath") + set(ld_var "LD_LIBRARY_PATH") +endif() + +#---Helpers--------------------------------------------------------------------- + +# Run `root` on the given C++ expression and return the last line of its output. +# The statement is wrapped into a block so that `root` exits with 0 rather than +# with the (truncated) value of the last expression. +function(root_eval expr outvar) + set(stmt "{ std::cout << ${expr} << std::endl; }") + execute_process(COMMAND "${ROOT_EXE}" -b -q -l -e "${stmt}" + WORKING_DIRECTORY "${WORKDIR}" + OUTPUT_VARIABLE out + ERROR_VARIABLE err + RESULT_VARIABLE rc) + if(NOT rc EQUAL 0) + message(FATAL_ERROR "'${ROOT_EXE} -b -q -l -e ${stmt}' failed with ${rc}\n${out}\n${err}") + endif() + string(REPLACE "\r" "" out "${out}") + string(STRIP "${out}" out) + # Keep only the last line (the value printed by `expr`), like `tail -1`. + string(REGEX MATCH "[^\n]*$" out "${out}") + set(${outvar} "${out}" PARENT_SCOPE) +endfunction() + +# Write the local .rootrc with the requested dynamic path; an empty value +# results in the resource being commented out. +function(set_rootrc value) + if("${value}" STREQUAL "") + file(WRITE "${WORKDIR}/.rootrc" "# ${rc_key}:\n") + else() + file(WRITE "${WORKDIR}/.rootrc" "${rc_key}: ${value}\n") + endif() +endfunction() + +# Query the dynamic path as seen by ROOT. +macro(get_dynpath) + root_eval("gSystem->GetDynamicPath()" cur_dynpath) +endmacro() + +# The dynamic path must start with `expected`. +function(check_begin expected) + string(FIND "${cur_dynpath}${sep}" "${expected}${sep}" pos) + if(NOT pos EQUAL 0) + message(FATAL_ERROR "dynamic path: ${cur_dynpath}\n" + "dynamic path should start with: ${expected}") + endif() +endfunction() + +# The dynamic path must contain `expected` as a whole sequence of entries. +function(check_mid expected) + string(FIND "${sep}${cur_dynpath}${sep}" "${sep}${expected}${sep}" pos) + if(pos EQUAL -1) + message(FATAL_ERROR "dynamic path: ${cur_dynpath}\n" + "dynamic path should contain: ${expected}") + endif() +endfunction() + +function(check_begin_and_mid begin mid) + check_begin("${begin}") + check_mid("${mid}") +endfunction() + +# Assemble the "environment part" of the dynamic path the same way +# TUnixSystem/TWinNTSystem do, i.e. ROOT_LIBRARY_PATH, followed by the loader +# search path, followed by the `.rootrc` entries. +function(root_env_part rdynpath outvar) + if(WIN32) + # `ld_prefix` accounts for the bin directory prepended to PATH by the + # TWinNTSystem constructor (see below). + set(ld "${ld_prefix}$ENV{PATH}") + elseif(APPLE) + # On macOS ROOT concatenates all three of these. + set(ld "$ENV{DYLD_LIBRARY_PATH}${sep}$ENV{LD_LIBRARY_PATH}${sep}$ENV{DYLD_FALLBACK_LIBRARY_PATH}") + else() + set(ld "$ENV{LD_LIBRARY_PATH}") + endif() + set(${outvar} "$ENV{ROOT_LIBRARY_PATH}${sep}${ld}${sep}${rdynpath}" PARENT_SCOPE) +endfunction() + +# ROOT guarantees that the library directory is part of the dynamic path. It is +# appended right after the `.rootrc` entries, but *only* if it does not already +# occur in the environment derived part -- see the +# `if (!dynpath_envpart.Contains(TROOT::GetLibDir()))` guard in TUnixSystem.cxx +# (a plain substring test, hence the plain string(FIND) below). +# +# Both situations occur in practice: when the test is run through ctest the +# driver puts $ROOTSYS/lib into (DY)LD_LIBRARY_PATH, so the library directory is +# already present and nothing gets appended. +function(check_rootrc_then_libdir rdynpath) + root_env_part("${rdynpath}" envpart) + string(FIND "${envpart}" "${libdir}" pos) + if(pos EQUAL -1) + # Not seen yet: it must be appended right behind the .rootrc entries. + check_mid("${rdynpath}${sep}${libdir}") + else() + # Already provided by the environment: both must still be present. + check_mid("${rdynpath}") + check_mid("${libdir}") + endif() +endfunction() + +#---Set up the scratch area----------------------------------------------------- +file(REMOVE_RECURSE "${WORKDIR}") +file(MAKE_DIRECTORY "${WORKDIR}") +file(MAKE_DIRECTORY "${WORKDIR}/rootlibpath") +file(MAKE_DIRECTORY "${WORKDIR}/rootrcpath") +file(MAKE_DIRECTORY "${WORKDIR}/ldpath") + +set(rootlibpath "${WORKDIR}/rootlibpath") +set(rootrcpath "${WORKDIR}/rootrcpath") +set(ldpath "${WORKDIR}/ldpath") + +# Start from a well defined state. +unset(ENV{ROOT_LIBRARY_PATH}) +set_rootrc("") + +root_eval("TROOT::GetLibDir()" libdir) +if(WIN32) + # On Windows the built-in default is `.;` while on Unix it is + # `.:` (see TWinNTSystem.cxx / TUnixSystem.cxx). + root_eval("TROOT::GetBinDir()" defaultdir) +else() + set(defaultdir "${libdir}") +endif() + +message(STATUS "ROOT library directory: ${libdir}") +message(STATUS "default dynamic path : .${sep}${defaultdir}") + +# On Windows the TWinNTSystem constructor prepends "\bin;" to the +# PATH environment variable itself (so that ROOT's DLLs are always found), +# *before* the dynamic path is assembled. Hence the loader part of the +# dynamic path is "\bin;". The prepend +# does not happen for every build flavour (e.g. ROOTPREFIX builds), so detect +# the actual prefix by comparing the PATH seen by ROOT with our own. +set(ld_prefix "") +if(WIN32) + root_eval("gSystem->Getenv(\"PATH\")" root_path) + string(LENGTH "${root_path}" root_path_len) + string(LENGTH "$ENV{PATH}" env_path_len) + if(root_path_len GREATER env_path_len) + math(EXPR prefix_len "${root_path_len} - ${env_path_len}") + string(SUBSTRING "${root_path}" ${prefix_len} -1 root_path_tail) + if(root_path_tail STREQUAL "$ENV{PATH}") + string(SUBSTRING "${root_path}" 0 ${prefix_len} ld_prefix) + message(STATUS "PATH prefix added by ROOT: ${ld_prefix}") + endif() + endif() +endif() + +#---Checks without an explicit loader search path------------------------------- + +set(ENV{ROOT_LIBRARY_PATH} "${rootlibpath}") +set_rootrc("${rootrcpath}") +get_dynpath() +check_begin("${rootlibpath}") +check_rootrc_then_libdir("${rootrcpath}") + +set(ENV{ROOT_LIBRARY_PATH} "${rootlibpath}") +set_rootrc("${libdir}${sep}${rootrcpath}") +get_dynpath() +check_begin_and_mid("${rootlibpath}" "${libdir}${sep}${rootrcpath}") + +set(ENV{ROOT_LIBRARY_PATH} "${rootlibpath}") +set_rootrc("") +get_dynpath() +check_begin_and_mid("${rootlibpath}" ".${sep}${defaultdir}") + +unset(ENV{ROOT_LIBRARY_PATH}) +set_rootrc("") +get_dynpath() +check_mid(".${sep}${defaultdir}") + +#---Checks with the loader search path (LD_LIBRARY_PATH & Co.)------------------ + +if(DEFINED ENV{${ld_var}} AND NOT "$ENV{${ld_var}}" STREQUAL "") + set(ENV{${ld_var}} "${ldpath}${sep}$ENV{${ld_var}}") +else() + set(ENV{${ld_var}} "${ldpath}") +endif() + +set(ENV{ROOT_LIBRARY_PATH} "${rootlibpath}") +set_rootrc("${rootrcpath}") +get_dynpath() +check_begin("${rootlibpath}${sep}${ld_prefix}${ldpath}") +check_rootrc_then_libdir("${rootrcpath}") + +set(ENV{ROOT_LIBRARY_PATH} "${rootlibpath}") +set_rootrc("${libdir}${sep}${rootrcpath}") +get_dynpath() +check_begin_and_mid("${rootlibpath}${sep}${ld_prefix}${ldpath}" "${libdir}${sep}${rootrcpath}") + +set(ENV{ROOT_LIBRARY_PATH} "${rootlibpath}") +set_rootrc("") +get_dynpath() +check_begin_and_mid("${rootlibpath}${sep}${ld_prefix}${ldpath}" ".${sep}${defaultdir}") + +unset(ENV{ROOT_LIBRARY_PATH}) +set_rootrc("") +get_dynpath() +check_begin_and_mid("${ld_prefix}${ldpath}" ".${sep}${defaultdir}") + +message(STATUS "dynamic path setup: all checks passed") diff --git a/tree/treeplayer/src/TTreeGeneratorBase.cxx b/tree/treeplayer/src/TTreeGeneratorBase.cxx index 9879749cd4585..76bd31ece0a80 100644 --- a/tree/treeplayer/src/TTreeGeneratorBase.cxx +++ b/tree/treeplayer/src/TTreeGeneratorBase.cxx @@ -24,6 +24,8 @@ #include "TVirtualCollectionProxy.h" #include "TVirtualStreamerInfo.h" +#include + /** \class ROOT::Internal::TTreeGeneratorBase Base class for code generators like TTreeProxyGenerator and TTreeReaderGenerator */ @@ -107,21 +109,13 @@ namespace Internal { if (!filename) return; -#ifdef R__WIN32 - TString inclPath("include;prec_stl"); // GetHtml()->GetIncludePath()); -#else - TString inclPath("include:prec_stl"); // GetHtml()->GetIncludePath()); -#endif + const TString pdelim(ROOT::FoundationUtils::GetEnvPathSeparator()); + const char ddelim = ROOT::FoundationUtils::GetPathSeparator()[0]; + // GetHtml()->GetIncludePath()); + const TString inclPath("include" + pdelim + "prec_stl"); Ssiz_t posDelim = 0; TString inclDir; TString sIncl(filename); -#ifdef R__WIN32 - const char* pdelim = ";"; - static const char ddelim = '\\'; -#else - const char* pdelim = ":"; - static const char ddelim = '/'; -#endif while (inclPath.Tokenize(inclDir, posDelim, pdelim)) { if (sIncl.BeginsWith(inclDir)) {