Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions cmake/modules/RootTestDriver.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,33 @@ endif()

#---Set environment --------------------------------------------------------------------------------
if(ENV)
string(REPLACE "#" ";" _env ${ENV})
# CMake treats ';' as a list separator. Since ENV values may legitimately
# contain semicolons (e.g. PATH-like variables), we must escape them here
# so they survive the upcoming list expansion in `foreach()`.
string(REPLACE ";" "\\;" _env "${ENV}")

# Deserialize the ENV string into a CMake list.
# This is the inverse operation of ROOT_ADD_TEST, which serializes the list
# by joining elements with '#'.
string(REPLACE "#" ";" _env "${_env}")

foreach(pair ${_env})
string(REGEX REPLACE "^([^=]+)=(.*)$" "\\1;\\2" pair ${pair})

# During list expansion, CMake has already consumed any previous '\;'
# escaping. Re-escape semicolons so the key=value split below treats the
# value as a single string.
string(REPLACE ";" "\\;" pair "${pair}")

# Split KEY=VALUE into a 2-element CMake list: [KEY;VALUE]
string(REGEX REPLACE "^([^=]+)=(.*)$" "\\1;\\2" pair "${pair}")

list(GET pair 0 var)
list(GET pair 1 val)
set(ENV{${var}} ${val})

# As before, quoting is important here so the semicolons are not
# interpreted as list separators.
set(ENV{${var}} "${val}")

if(DBG)
message(STATUS "testdriver[ENV]:${var}==>${val}")
endif()
Expand Down
9 changes: 8 additions & 1 deletion core/clingutils/src/TClingUtils.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -5219,9 +5219,16 @@ void ROOT::TMetaUtils::SetPathsForRelocatability(std::vector<std::string>& cling

if (!envInclPath)
return;

#ifdef _WIN32
constexpr char kPathSep = ';';
#else
constexpr char kPathSep = ':';
#endif

std::istringstream envInclPathsStream(envInclPath);
std::string inclPath;
while (std::getline(envInclPathsStream, inclPath, ':')) {
while (std::getline(envInclPathsStream, inclPath, kPathSep)) {
// Can't use TSystem in here; re-implement TSystem::ExpandPathName().
replaceEnvVars("ROOT_INCLUDE_PATH", inclPath);
if (!inclPath.empty()) {
Expand Down
12 changes: 11 additions & 1 deletion roottest/root/io/transient/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,18 @@ else()
set(outref_suffix "ZLIB")
endif()

set(root_includepaths
${CMAKE_CURRENT_SOURCE_DIR}
${DEFAULT_ROOT_INCLUDE_PATH}
)
cmake_path(CONVERT "${root_includepaths}" TO_NATIVE_PATH_LIST root_includepaths_native)
# Like in tutorials/CMakeLists.txt, we use this generator expression to make
# sure the semicolon in Windows path separators doesn't get used up for list
# parsing at configuration time in ROOTTEST_ADD_TEST.
string(REPLACE ";" "$<SEMICOLON>" root_includepaths_escaped "${root_includepaths_native}")

ROOTTEST_ADD_TEST(hadd_autoload
COMMAND hadd -f data_merge.root data1.root data2.root
OUTREF hadd_autoload${outref_suffix}.ref
FIXTURES_REQUIRED root-io-transient-base-WriteFile-fixture
ENVIRONMENT ROOT_INCLUDE_PATH=${CMAKE_CURRENT_SOURCE_DIR}:${DEFAULT_ROOT_INCLUDE_PATH})
ENVIRONMENT ROOT_INCLUDE_PATH=${root_includepaths_escaped})
18 changes: 9 additions & 9 deletions tutorials/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ set(root_includepaths
)
cmake_path(CONVERT "${root_includepaths}" TO_NATIVE_PATH_LIST root_includepaths_native)

# The environment argument gets serialized in ROOT_ADD_TEST for the
# RootTestDriver, and then deserialized in the latter with multiple steps of
# string parsing. We have to escape the semicolon excessively, because in every
# parsing step we lose one semicolon. Also we use the SEMICOLON generator
# expression, so at least we don't have to worry about escaping for the initial
# configuration stage.
# TODO: consider improving ROOT_ADD_TEST and RootTestDriver.cmake so this is not necessary.
string(REPLACE ";" "\\\\\\\\$<SEMICOLON>" pythonpaths_escaped "${pythonpaths_native}")
string(REPLACE ";" "\\\\\\\\$<SEMICOLON>" root_includepaths_escaped "${root_includepaths_native}")
# Use the $<SEMICOLON> generator expression to defer semicolon expansion until
# generator-expression evaluation. This avoids CMake interpreting literal ';'
# as list separators during argument handling.
#
# ROOT_ADD_TEST performs multiple string-to-list conversions when serializing
# the test command, which makes it unsafe to pass literal semicolons in
# arguments until this behavior is fixed.
string(REPLACE ";" "$<SEMICOLON>" pythonpaths_escaped "${pythonpaths_native}")
string(REPLACE ";" "$<SEMICOLON>" root_includepaths_escaped "${root_includepaths_native}")

# - Set the environment for the tutorials, which also contains some environment
# variables related to limiting the number of threads used by NumPy and
Expand Down
Loading