Skip to content

Clean up usage of TSystem::PrependPathName - #23928

Draft
pcanal wants to merge 15 commits into
root-project:masterfrom
pcanal:prepend_pathname
Draft

pcanal wants to merge 15 commits into
root-project:masterfrom
pcanal:prepend_pathname

Conversation

@pcanal

@pcanal pcanal commented Oct 30, 2026

Copy link
Copy Markdown
Member

As prompted by the PR #23796, review usage of TSystem::PrependPathName which is (intentionally) updating its argument rather than just returning a new value.

In addition to the case addressed in the original PR, we also address two related issues in TSystem.cxx and TCling.cxx.

The original PR test has been significantly extended (It was failing on windows failure due to joining directories with :, whereas the implementation uses ; on Windows. The cleanup was oncomplete cleanup due fatal assertions bypassing cleanup. An originally empty or absent Root.PluginPath is never restored, leaving it pointing at deleted directories.
Cache-dependent coverage: built-in handlers already loaded into the global plugin manager can mask the bug. Prefer controlled temporary plugin directories, unique base names, and isolated plugin-manager state.)

Regression test for the two other issues has been added.

@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Test Results

    21 files      21 suites   3d 7h 29m 6s ⏱️
 3 882 tests  3 882 ✅ 0 💤 0 ❌
72 226 runs  72 226 ✅ 0 💤 0 ❌

Results for commit f350934.

♻️ This comment has been updated with latest results.

JainAyush-01 and others added 6 commits November 5, 2026 14:10
When Root.PluginPath (or ROOT_PLUGIN_PATH) contains multiple colon-separated
directories, TPluginManager::LoadHandlersFromPluginDirs loops over each
directory to load handler macros for the requested base class.

In commit e60640c (root-project#18859), ConcatFileName was replaced by PrependPathName.
Unlike ConcatFileName which returned a new heap-allocated string,
PrependPathName modifies its second parameter in-place. Because `sbase`
was passed directly:
    const char *p = gSystem->PrependPathName(d, sbase);
the variable `sbase` was mutated in-place across iterations (e.g., from
"TArchiveFile" to "/dir1/TArchiveFile", then to "/dir2//dir1/TArchiveFile").
Consequently, subsequent search directories received a mangled path and
ROOT could not find its own built-in plugins whenever a custom plugin
directory was prepended.

Fix this by copying `sbase` to a temporary `TString temp = sbase;` on each
iteration, matching the pattern used in the rest of TPluginManager.cxx.

Fixes root-project#23536. Added regression test in core/base/test/TPluginManagerTests.cxx.
Use three temporary plugin directories and a fresh plugin manager so
cached built-in handlers cannot hide path accumulation. Select the path
separator for the platform and restore global state and remove temporary
files in fixture teardown, including after fatal assertions.

Validation: clang-format --dry-run --Werror and git diff --check passed.
Runtime validation remains pending because VS Code CMake build and test
tools failed and unit-test discovery found no tests.

Assisted-by: Copilot SDK:GPT 6 astra
Avoid prefixing the dependency output path twice. Advance the parent
component while reconstructing dictionary header paths, and stop at the
filesystem root.

Assisted-by: Copilot SDK:GPT 6 astra
Exercise the ACLiC dependency generation path with a cmd.exe alias so
that the nonstandard COMSPEC branch is covered without changing the
actual shell behavior.

Assisted-by: Copilot SDK:GPT 6 astra
Exercise TCling_GenerateDictionary with an unknown class declared in a
nested ROOT header path to cover directory traversal while generating a
dictionary.

Assisted-by: Copilot SDK:GPT 6 astra
Use a rootcling-generated test class with a stable nested include path so
the dictionary path traversal test behaves consistently across platforms.
Guard empty directory paths before calling BaseName and stop at drive
roots when walking the declaration path.

Assisted-by: Copilot SDK:GPT 6 astra
Inspect the generated AutoDict source directly so the regression test validates path reconstruction without depending on ACLiC compilation succeeding on every platform.

Avoid Cling JIT in dictionary regression test

Call the dictionary generator from compiled test code instead of materializing std::vector<std::string> globals in a JIT module. Keep the test strict about ACLiC success and verify the generated header include.

Assisted-by: Copilot SDK:GPT 6 astra
Invoke the internal dictionary helper through the interpreter, keeping vector objects local to a lambda so they do not require JIT static initialization. Check the interpreter error code and dictionary result.

Assisted-by: Copilot SDK:GPT 6 astra
Use a quoted COMSPEC path to exercise the nonstandard-shell branch without copying the protected Windows system executable.

Assisted-by: Copilot SDK:GPT 6 astra
@pcanal pcanal closed this Nov 6, 2026
@pcanal pcanal added the clean build Ask CI to do non-incremental build on PR label Nov 6, 2026
@pcanal pcanal reopened this Nov 6, 2026
Assisted-by: copilot-sdk:GPT-6 Luna
Do not run ACLiC with a quoted cmd.exe COMSPEC path: Windows process launch fails in that configuration. Retain coverage using the normal unquoted cmd.exe path.

Assisted-by: Copilot SDK:GPT 6 astra

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI-Assisted clean build Ask CI to do non-incremental build on PR in:Core Libraries

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants