Skip to content

Make non-system include paths accessible at runtime #19179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

LukasBreitwieser
Copy link
Contributor

The current implementation faces issues with accessing header files from external libraries stored in non-system paths when the C++ interpreter tries to resolve them at runtime. This access is necessary under certain conditions even if a corresponding module file exists. Related issues: SPI-2794, #18949

To address this issue, we need to ensure that non-system path includes, which are known at build time, remain accessible at runtime. The proposed solution leverages the existing ROOT_INCLUDE_PATH environment variable.

This commit introduces a new CMake variable DEFAULT_ROOT_INCLUDE_PATH and a helper function IF_NOT_SYSTEM_PATH_APPEND. This function allows us to conditionally append include paths of external libraries to DEFAULT_ROOT_INCLUDE_PATH, for example IF_NOT_SYSTEM_PATH_APPEND("${Vc_INCLUDE_DIR}" DEFAULT_ROOT_INCLUDE_PATH)

The DEFAULT_ROOT_INCLUDE_PATH variable is then utilized to populate the ROOT_INCLUDE_PATH environment variable in the configuration files (thisroot.{sh,csh,fish,bat}).

If the library's include path is dynamically modified between build and run time, the configuration can be updated by patching the thisroot.{sh,csh,fish,bat} files.

The current implementation faces issues with accessing header files
from external libraries stored in non-system paths when the C++
interpreter tries to resolve them at runtime. This access is necessary
under certain conditions even if a corresponding module file exists.

To address this issue, we need to ensure that non-system path includes,
which are known at build time, remain accessible at runtime. The
proposed solution leverages the existing `ROOT_INCLUDE_PATH` environment
variable.

This commit introduces a new CMake variable `DEFAULT_ROOT_INCLUDE_PATH`
and a helper function `IF_NOT_SYSTEM_PATH_APPEND`. This function allows
us to conditionally append include paths of external libraries to
`DEFAULT_ROOT_INCLUDE_PATH`, for example
`IF_NOT_SYSTEM_PATH_APPEND("${Vc_INCLUDE_DIR}" DEFAULT_ROOT_INCLUDE_PATH)`

The `DEFAULT_ROOT_INCLUDE_PATH` variable is then utilized to populate the
`ROOT_INCLUDE_PATH` environment variable in the configuration files
(`thisroot.{sh,csh,fish,bat}`).

If the library's include path is dynamically modified between build and
run time, the configuration can be updated by patching the
`thisroot.{sh,csh,fish,bat}` files.
Copy link

github-actions bot commented Jun 25, 2025

Test Results

    20 files      20 suites   3d 9h 11m 55s ⏱️
 3 066 tests  3 066 ✅ 0 💤 0 ❌
59 718 runs  59 718 ✅ 0 💤 0 ❌

Results for commit 107b911.

♻️ This comment has been updated with latest results.

Copy link
Member

@hageboeck hageboeck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I added a few comments below

Comment on lines +2147 to +2150
set(${is_system_path} true PARENT_SCOPE)
endif()
endforeach()
set(${is_system_path} false PARENT_SCOPE)
Copy link
Member

@hageboeck hageboeck Jun 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The canonical bools are all caps, and we can break out early:

Suggested change
set(${is_system_path} true PARENT_SCOPE)
endif()
endforeach()
set(${is_system_path} false PARENT_SCOPE)
set(${is_system_path} TRUE PARENT_SCOPE)
return()
endif()
endforeach()
set(${is_system_path} FALSE PARENT_SCOPE)

@@ -89,6 +89,10 @@ clean_environment()
default_manpath=""
fi
fi
if [ -n "${ROOT_INCLUDE_PATH-}" ]; then
drop_from_path "$ROOT_INCLUDE_PATH" "@DEFAULT_ROOT_INCLUDE_PATH@"
ROOT_INCLUDE_PATH=$newpath
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newpath seems to be undefined here, or did I miss something?

# The 2nd argument is the variable that the path gets appended to if it is
# not a system path.
#----------------------------------------------------------------------------
function (IF_NOT_SYSTEM_PATH_APPEND path variable)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function ever going to be called with another variable than the default root include path? If yes, fair enough, otherwise I would call it something like BUILD_ROOT_INCLUDE_PATH("${Vc_INCLUDE_DIR}"), and remove the second argument, since any negations in names are hard to parse.

Comment on lines +171 to +175
ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@
export ROOT_INCLUDE_PATH
else
ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@:$ROOT_INCLUDE_PATH
export ROOT_INCLUDE_PATH
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@
export ROOT_INCLUDE_PATH
else
ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@:$ROOT_INCLUDE_PATH
export ROOT_INCLUDE_PATH
export ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@
else
export ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@:$ROOT_INCLUDE_PATH

@hageboeck
Copy link
Member

@LukasBreitwieser, what happens when you install (and therefore relocate) ROOT? Given that these paths are system paths, things should continue to work, won't they?

foreach (dir ${CMAKE_SYSTEM_PATH})
if ("${path}" STREQUALS "$dir")
set(${is_system_path} true PARENT_SCOPE)
endif()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
endif()
return()
endif()

Maybe my cmake is too rusty, but shouldn't we not always set is_system_path to false at the end of the function?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I think the return is needed

ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@
export ROOT_INCLUDE_PATH
else
ROOT_INCLUDE_PATH=@DEFAULT_ROOT_INCLUDE_PATH@:$ROOT_INCLUDE_PATH
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you consider this an issue if DEFAULT_ROOT_INCLUDE_PATH is empty? Not just here but in the other shell setups?

@@ -162,6 +166,14 @@ set_environment()
else
JUPYTER_CONFIG_DIR=$ROOTSYS/etc/notebook:$JUPYTER_CONFIG_DIR; export JUPYTER_CONFIG_DIR
fi

if [ -z "${ROOT_INCLUDE_PATH-}" ]; then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name contains an extra - if I'm not mistaken, so this would always be zero.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is bash syntax for using a default value (of nothing) when the variable is not set.

Copy link
Member

@hageboeck hageboeck Jun 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I had to read up on the subtle difference between ${VAR:-} vs ${VAR-} ...

@andresailer
Copy link
Contributor

I am getting an error in the lcg build with this

[100%] Generating tutorials/hsimple.root
root.exe: /build/jenkins/workspace/lcg_nightly_pipeline/build/projects/ROOT-default-root-include-path/src/ROOT/default-root-include-path/interpreter/llvm-project/clang/lib/Serialization/ASTReader.cpp:4332: clang::ASTReader::ASTReadResult clang::ASTReader::ReadModuleMapFileBlock(RecordData&, ModuleFile&, const ModuleFile*, unsigned int): Assertion `M && M->Name == F.ModuleName && "found module with different name"' failed.

https://lcgapp-services.cern.ch/cdash/build/98193/files

@pcanal
Copy link
Member

pcanal commented Jun 27, 2025

What do you practically mean by:

If the library's include path is dynamically modified between build and run time, the configuration can be updated by patching the thisroot.{sh,csh,fish,bat} files.

?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants