Skip to content

Clean up usage of TSystem::PrependPathName - #23928

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

pcanal wants to merge 5 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.

JainAyush-01 and others added 5 commits October 30, 2026 15:30
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
@github-actions

Copy link
Copy Markdown

Test Results

    23 files      23 suites   3d 7h 40m 47s ⏱️
 3 862 tests  3 860 ✅ 0 💤  2 ❌
72 743 runs  72 724 ✅ 0 💤 19 ❌

For more details on these failures, see this check.

Results for commit 101b2a8.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants