Skip to content

[rootcling] Remove llvm::cl dependency by using ROOT's optparse#20903

Open
devajithvs wants to merge 2 commits into
root-project:masterfrom
devajithvs:rootcling.optparse
Open

[rootcling] Remove llvm::cl dependency by using ROOT's optparse#20903
devajithvs wants to merge 2 commits into
root-project:masterfrom
devajithvs:rootcling.optparse

Conversation

@devajithvs

@devajithvs devajithvs commented Jan 15, 2026

Copy link
Copy Markdown
Contributor

This Pull request:

This is a draft PR to test if it is possible to use optparse in rootcling:

  • barecling subcommand makes this slightly complicated, but not sure if it is worth adding subcommand feature to optparse.

This patch potentially removes issues like

CommandLine Error: Option 'W' registered more than once!

caused by llvm::cl registering the same options multiple times when ROOT is linked against a shared libLLVM.

(See: #12156)

Changes or fixes:

Checklist:

  • tested changes locally
  • updated the docs (if necessary)

This PR fixes #

@devajithvs devajithvs self-assigned this Jan 15, 2026
@github-actions

github-actions Bot commented Jan 15, 2026

Copy link
Copy Markdown

Test Results

    20 files      20 suites   3d 4h 57m 19s ⏱️
 3 868 tests  3 868 ✅ 0 💤 0 ❌
70 077 runs  70 077 ✅ 0 💤 0 ❌

Results for commit 8f54bc5.

♻️ This comment has been updated with latest results.

@silverweed

Copy link
Copy Markdown
Contributor

Note: clearly if we want to use optparse.hxx from modules other than main I'll move it to a better location (should it be in core since we use it from core/dictgen?)

@devajithvs

Copy link
Copy Markdown
Contributor Author

Note: clearly if we want to use optparse.hxx from modules other than main I'll move it to a better location (should it be in core since we use it from core/dictgen?)

Yes, it would be nice to have the header somewhere in core.

@hahnjo

hahnjo commented Jan 20, 2026

Copy link
Copy Markdown
Member

For future reference, this is the reverse direction of #4171

@hahnjo hahnjo requested a review from vgvassilev January 23, 2026 10:04
@hahnjo

hahnjo commented Jan 23, 2026

Copy link
Copy Markdown
Member

I just tested removing all LLVM_DYLIB workarounds, and including this PR solves all build errors due to duplicate options registered with llvm::cl::opt. There are a few test failures (~10) that need to be tracked down (in future PRs), but I think this is promising 👍

@devajithvs devajithvs force-pushed the rootcling.optparse branch 2 times, most recently from ccb43c6 to 195fcf1 Compare January 23, 2026 10:34
@vgvassilev

Copy link
Copy Markdown
Member

I am not sure what is the merit of going this direction. If we want to avoid the assert, it is rather a symptom rather than an issue. That is, when we see the assert we almost certainly have screwed up the setup of the llvm linking.

@hahnjo

hahnjo commented Jan 23, 2026

Copy link
Copy Markdown
Member

That is, when we see the assert we almost certainly have screwed up the setup of the llvm linking.

The goal here is to make this a non-screwup and allow linking against the libLLVM dylib, if the user tells us to

@vgvassilev

Copy link
Copy Markdown
Member

That is, when we see the assert we almost certainly have screwed up the setup of the llvm linking.

The goal here is to make this a non-screwup and allow linking against the libLLVM dylib, if the user tells us to

Yes, and that would cause symbols to clash when dlopening libraries in libCling that bring their own copy of llvm. Of course that assertion won’t fire however later when you deploy root it would cause issues. We’ve been discussing this over many years and there doesn’t seem to be a reasonable way forward. That’s why I wonder if it’s worth revisiting this topic rather than actually fixing llvm to not link libLLVM.so when we request static linking.

@hahnjo

hahnjo commented Jan 23, 2026

Copy link
Copy Markdown
Member

that would cause symbols to clash when dlopening libraries in libCling that bring their own copy of llvm

When building against an external LLVM, the user tells us (more or less) that they don't care about symbol clashes. Otherwise they would use builtin_llvm=ON in which case we can take all precautions necessary to avoid issues. Nothing changes there, the plan is definitely not to start building libLLVM.so inside ROOT. The goal here is rather to allow linking against libLLVM.so which is an extremely common way distributions build LLVM (all of Arch Linux, Enterprise Linux, Fedora, Ubuntu; didn't check Debian).

@vgvassilev

Copy link
Copy Markdown
Member

that would cause symbols to clash when dlopening libraries in libCling that bring their own copy of llvm

When building against an external LLVM, the user tells us (more or less) that they don't care about symbol clashes. Otherwise they would use builtin_llvm=ON in which case we can take all precautions necessary to avoid issues. Nothing changes there, the plan is definitely not to start building libLLVM.so inside ROOT. The goal here is rather to allow linking against libLLVM.so which is an extremely common way distributions build LLVM (all of Arch Linux, Enterprise Linux, Fedora, Ubuntu; didn't check Debian).

Yes, but what is the end goal? You can't do what you are doing for rootcling to libCling, right? If you do the same strategy you can't load things like libmessa which ships its own llvm.

@hahnjo

hahnjo commented Jan 23, 2026

Copy link
Copy Markdown
Member

You can't do what you are doing for rootcling to libCling, right?

I don't understand this question, what does "rootcling to libCling" mean?

If you do the same strategy you can't load things like libmessa which ships its own llvm.

I don't see this being the case: libEGL_mesa.so and libGLX_mesa.so link against the system-provided libLLVM.so. If they are the same, they can happily co-exist in the same process.

@vgvassilev

Copy link
Copy Markdown
Member

You can't do what you are doing for rootcling to libCling, right?

I don't understand this question, what does "rootcling to libCling" mean?

If you do the same strategy you can't load things like libmessa which ships its own llvm.

I don't see this being the case: libEGL_mesa.so and libGLX_mesa.so link against the system-provided libLLVM.so. If they are the same, they can happily co-exist in the same process.

What if they are different which is usually the case? Also, some distributions statically linked llvm in messa and who knows what else. I think going in this direction will cause a lot of pain for us because we don't know what the users are going to ask ROOT to dlopen... I think that reintroduces a problem that we worked very hard to avoid. We can probably work it around in a more reliable way if we used a linker script to rename libCling's llvm symbols to avoid clashes. However, I do not see really a benefit rootcling to be using a different version of llvm...

@hahnjo

hahnjo commented Jan 26, 2026

Copy link
Copy Markdown
Member

I don't see this being the case: libEGL_mesa.so and libGLX_mesa.so link against the system-provided libLLVM.so. If they are the same, they can happily co-exist in the same process.

What if they are different which is usually the case?

Then you face the same problem as with any process that has two version of LLVM in it. Note that this is already the case today because an external LLVM is not built in a way that we can hide all symbols, even if linked in statically. The only way we can guarantee this is with builtin_llvm=ON (where nothing changes).

Also, some distributions statically linked llvm in messa and who knows what else.

As mentioned above, I think this can already cause problems today because statically linking in external LLVM is not guaranteed to hide all symbols.

I think going in this direction will cause a lot of pain for us because we don't know what the users are going to ask ROOT to dlopen... I think that reintroduces a problem that we worked very hard to avoid. We can probably work it around in a more reliable way if we used a linker script to rename libCling's llvm symbols to avoid clashes. However, I do not see really a benefit rootcling to be using a different version of llvm...

I would like to ask that you view the problem from a different angle: Today it is not possible to build ROOT against a shared libLLVM.so. At the end of this route, we will finally be able to allow it. In my opinion this is a big win for scenarios where the user can make sure it doesn't cause issues, and want to use it. You can still screw up, but that's not different than today. And once again, the plan is not to make this the default build mode: With builtin_llvm=ON, nothing changes.

@vgvassilev

Copy link
Copy Markdown
Member

I don't see this being the case: libEGL_mesa.so and libGLX_mesa.so link against the system-provided libLLVM.so. If they are the same, they can happily co-exist in the same process.

What if they are different which is usually the case?

Then you face the same problem as with any process that has two version of LLVM in it. Note that this is already the case today because an external LLVM is not built in a way that we can hide all symbols, even if linked in statically. The only way we can guarantee this is with builtin_llvm=ON (where nothing changes).

For rootcling, yes. However, that's my point the current setup (before this PR) prevents us to screw up. If we let that work, the obvious next question is what happens to libCling.so? Does it depend (eg dlopen at runtime) on libLLVM.so?

Also, some distributions statically linked llvm in messa and who knows what else.

As mentioned above, I think this can already cause problems today because statically linking in external LLVM is not guaranteed to hide all symbols.

I think going in this direction will cause a lot of pain for us because we don't know what the users are going to ask ROOT to dlopen... I think that reintroduces a problem that we worked very hard to avoid. We can probably work it around in a more reliable way if we used a linker script to rename libCling's llvm symbols to avoid clashes. However, I do not see really a benefit rootcling to be using a different version of llvm...

I would like to ask that you view the problem from a different angle: Today it is not possible to build ROOT against a shared libLLVM.so. At the end of this route, we will finally be able to allow it. In my opinion this is a big win for scenarios where the user can make sure it doesn't cause issues, and want to use it. You can still screw up, but that's not different than today. And once again, the plan is not to make this the default build mode: With builtin_llvm=ON, nothing changes.

I can see that in the case of rootcling building against libLLVM.so somewhat safer because it is a binary that's supposed to be used as a binary but my libCling question remains: what tells the users of -Dbuiltin_llvm=OFF that they screwed up the external llvm build?

If we want to link against libLLVM.so because that's what we get with the package managers and LLVM_LINK_LLVM_DYLIB does not help, I can propose a separate solution that we use in CppInterOp which gives priority to the static archives:

# Get rid of libLLVM-X.so which is appended to the list of static libraries.
if (LLVM_LINK_LLVM_DYLIB)
set(new_libs ${link_libs})
set(libs ${new_libs})
while(NOT "${new_libs}" STREQUAL "")
foreach(lib ${new_libs})
if(TARGET ${lib})
get_target_property(transitive_libs ${lib} INTERFACE_LINK_LIBRARIES)
if (NOT transitive_libs)
continue()
endif()
foreach(transitive_lib ${transitive_libs})
if(NOT TARGET ${transitive_lib})
continue()
endif()
get_target_property(lib_type ${transitive_lib} TYPE)
if("${lib_type}" STREQUAL "STATIC_LIBRARY")
list(APPEND static_transitive_libs ${transitive_lib})
else()
# Filter our libLLVM.so and friends.
continue()
endif()
if(NOT ${transitive_lib} IN_LIST libs)
list(APPEND newer_libs ${transitive_lib})
list(APPEND libs ${transitive_lib})
endif()
endforeach(transitive_lib)
# Update the target properties with the list of only static libraries.
set_target_properties(${lib} PROPERTIES INTERFACE_LINK_LIBRARIES "${static_transitive_libs}")
set(static_transitive_libs "")
endif()
endforeach(lib)
set(new_libs ${newer_libs})
set(newer_libs "")
endwhile()
If that's the problem we are trying to work around I'd propose to fix it as also @vepadulano suffers a version of it.

@hahnjo

hahnjo commented Jan 26, 2026

Copy link
Copy Markdown
Member

For rootcling, yes. However, that's my point the current setup (before this PR) prevents us to screw up.

First, this PR itself only modifies rootcling option parsing. This in itself doesn't change anything with respect to linking, it's only a first step in that direction. Maybe we can separate these two discussions.
Second, no, the current setup doesn't prevent us from screwing up. As I explained, statically linking in an external LLVM (even built without LLVM_LINK_LLVM_DYLIB) does not properly hide symbols in all cases. For example, we have this code in interpreter/CMakeLists.txt to make sure builtin_llvm is compiled in a way to allow for this:

if (NOT MSVC AND NOT APPLE)
# Allow the compiler to optimize knowing that symbols defined in libCling.so
# are not interposed.
ROOT_ADD_CXX_FLAG(CMAKE_CXX_FLAGS "-fno-semantic-interposition")
endif()
set(CMAKE_VISIBILITY_INLINES_HIDDEN "ON")

If we let that work, the obvious next question is what happens to libCling.so? Does it depend (eg dlopen at runtime) on libLLVM.so?

Yes, eventually libCling.so could be linked against libLLVM.so if the user supplies a build of LLVM that prefers to do so.

I can see that in the case of rootcling building against libLLVM.so somewhat safer because it is a binary that's supposed to be used as a binary but my libCling question remains: what tells the users of -Dbuiltin_llvm=OFF that they screwed up the external llvm build?

If we want to link against libLLVM.so because that's what we get with the package managers and LLVM_LINK_LLVM_DYLIB does not help, I can propose a separate solution that we use in CppInterOp which gives priority to the static archives:

# Get rid of libLLVM-X.so which is appended to the list of static libraries.
if (LLVM_LINK_LLVM_DYLIB)
set(new_libs ${link_libs})
set(libs ${new_libs})
while(NOT "${new_libs}" STREQUAL "")
foreach(lib ${new_libs})
if(TARGET ${lib})
get_target_property(transitive_libs ${lib} INTERFACE_LINK_LIBRARIES)
if (NOT transitive_libs)
continue()
endif()
foreach(transitive_lib ${transitive_libs})
if(NOT TARGET ${transitive_lib})
continue()
endif()
get_target_property(lib_type ${transitive_lib} TYPE)
if("${lib_type}" STREQUAL "STATIC_LIBRARY")
list(APPEND static_transitive_libs ${transitive_lib})
else()
# Filter our libLLVM.so and friends.
continue()
endif()
if(NOT ${transitive_lib} IN_LIST libs)
list(APPEND newer_libs ${transitive_lib})
list(APPEND libs ${transitive_lib})
endif()
endforeach(transitive_lib)
# Update the target properties with the list of only static libraries.
set_target_properties(${lib} PROPERTIES INTERFACE_LINK_LIBRARIES "${static_transitive_libs}")
set(static_transitive_libs "")
endif()
endforeach(lib)
set(new_libs ${newer_libs})
set(newer_libs "")
endwhile()

If that's the problem we are trying to work around I'd propose to fix it as also @vepadulano suffers a version of it.

I know this code, it's extremely hacky, and it doesn't work in all cases: If Clang is built against an LLVM with LLVM_LINK_LLVM_DYLIB, the CMake targets only have libLLVM.so as dependency. You are trying to account for this with

# We just got rid of the libLLVM.so and other components shipped as shared
# libraries, we need to make up for the missing dependency.
list(APPEND LLVM_LINK_COMPONENTS
Coverage
FrontendHLSL
LTO
)
# We will need to append the missing dependencies to pull in the right
# LLVM library dependencies.
list(APPEND link_libs
clangCodeGen
clangStaticAnalyzerCore
)

but it doesn't fully work, there are linking problems in current master when I tried two weeks ago.

@vgvassilev

Copy link
Copy Markdown
Member

For rootcling, yes. However, that's my point the current setup (before this PR) prevents us to screw up.

First, this PR itself only modifies rootcling option parsing. This in itself doesn't change anything with respect to linking, it's only a first step in that direction.

I agree with that, however, rootcling heavily depends on llvm and there is no good reason to move away from llvm-based option parsing unless we want to achieve something broader. That means that those two things cannot really be decoupled because the first thing seen by itself is a waste of time/effort.

Maybe we can separate these two discussions. Second, no, the current setup doesn't prevent us from screwing up. As I explained, statically linking in an external LLVM (even built without LLVM_LINK_LLVM_DYLIB) does not properly hide symbols in all cases.

What I actually meant is that we have some way to signal that we are doing something wrong. The approach that we statically link rootcling will be gone iirc.

For example, we have this code in interpreter/CMakeLists.txt to make sure builtin_llvm is compiled in a way to allow for this:

if (NOT MSVC AND NOT APPLE)
# Allow the compiler to optimize knowing that symbols defined in libCling.so
# are not interposed.
ROOT_ADD_CXX_FLAG(CMAKE_CXX_FLAGS "-fno-semantic-interposition")
endif()
set(CMAKE_VISIBILITY_INLINES_HIDDEN "ON")

If we let that work, the obvious next question is what happens to libCling.so? Does it depend (eg dlopen at runtime) on libLLVM.so?

Yes, eventually libCling.so could be linked against libLLVM.so if the user supplies a build of LLVM that prefers to do so.

That is the reason I expressed concerns about this PR to begin with. I do not think we should go that route. We lower the route towards using system llvm but at the same time we lower the bar to introduce setups that are prone to subtle abi mismatches.

I can see that in the case of rootcling building against libLLVM.so somewhat safer because it is a binary that's supposed to be used as a binary but my libCling question remains: what tells the users of -Dbuiltin_llvm=OFF that they screwed up the external llvm build?
If we want to link against libLLVM.so because that's what we get with the package managers and LLVM_LINK_LLVM_DYLIB does not help, I can propose a separate solution that we use in CppInterOp which gives priority to the static archives:

# Get rid of libLLVM-X.so which is appended to the list of static libraries.
if (LLVM_LINK_LLVM_DYLIB)
set(new_libs ${link_libs})
set(libs ${new_libs})
while(NOT "${new_libs}" STREQUAL "")
foreach(lib ${new_libs})
if(TARGET ${lib})
get_target_property(transitive_libs ${lib} INTERFACE_LINK_LIBRARIES)
if (NOT transitive_libs)
continue()
endif()
foreach(transitive_lib ${transitive_libs})
if(NOT TARGET ${transitive_lib})
continue()
endif()
get_target_property(lib_type ${transitive_lib} TYPE)
if("${lib_type}" STREQUAL "STATIC_LIBRARY")
list(APPEND static_transitive_libs ${transitive_lib})
else()
# Filter our libLLVM.so and friends.
continue()
endif()
if(NOT ${transitive_lib} IN_LIST libs)
list(APPEND newer_libs ${transitive_lib})
list(APPEND libs ${transitive_lib})
endif()
endforeach(transitive_lib)
# Update the target properties with the list of only static libraries.
set_target_properties(${lib} PROPERTIES INTERFACE_LINK_LIBRARIES "${static_transitive_libs}")
set(static_transitive_libs "")
endif()
endforeach(lib)
set(new_libs ${newer_libs})
set(newer_libs "")
endwhile()

If that's the problem we are trying to work around I'd propose to fix it as also @vepadulano suffers a version of it.

I know this code, it's extremely hacky, and it doesn't work in all cases: If Clang is built against an LLVM with LLVM_LINK_LLVM_DYLIB, the CMake targets only have libLLVM.so as dependency. You are trying to account for this with

# We just got rid of the libLLVM.so and other components shipped as shared
# libraries, we need to make up for the missing dependency.
list(APPEND LLVM_LINK_COMPONENTS
Coverage
FrontendHLSL
LTO
)
# We will need to append the missing dependencies to pull in the right
# LLVM library dependencies.
list(APPEND link_libs
clangCodeGen
clangStaticAnalyzerCore
)

but it doesn't fully work, there are linking problems in current master when I tried two weeks ago.

The approach is hacky but can achieve what you need with far less friction. If you share what did not work I'd be happy to help fixing it rather to go the current direction. Yes, the cmake approach is more of a workaround and where the real fix should be in clang. That is, when we install clang in the clang's cmake targets file we need to add logic that respects the fact if we selected static linking. If we did, it should not attach libLLVM.so automatically -- I'd claim that this fixes a bug in llvm and at the same time follows the spirit of the ROOT infrastructure that has been working for years successfully.

@hahnjo

hahnjo commented Jan 27, 2026

Copy link
Copy Markdown
Member

Maybe we can separate these two discussions. Second, no, the current setup doesn't prevent us from screwing up. As I explained, statically linking in an external LLVM (even built without LLVM_LINK_LLVM_DYLIB) does not properly hide symbols in all cases.

What I actually meant is that we have some way to signal that we are doing something wrong. The approach that we statically link rootcling will be gone iirc.

If I understand correctly what you're saying, #12156 for you is a "feature" to signal a potential, but not guaranteed problem to the user? For me, it is a bug in our build system that should eventually be fixed because that is where upstream LLVM is going.

That is the reason I expressed concerns about this PR to begin with. I do not think we should go that route. We lower the route towards using system llvm but at the same time we lower the bar to introduce setups that are prone to subtle abi mismatches.

I cannot argue against the point that there are (other) ways to screw up. But on the other hand, I firmly believe that it would be better to have the possibility than outright disallowing it. I would be ok with making this build mode conditional behind a (hidden) configuration flag, and otherwise clearly error during build rather than at run-time of rootcling.

I know this code, it's extremely hacky, and it doesn't work in all cases: If Clang is built against an LLVM with LLVM_LINK_LLVM_DYLIB, the CMake targets only have libLLVM.so as dependency. You are trying to account for this with

# We just got rid of the libLLVM.so and other components shipped as shared
# libraries, we need to make up for the missing dependency.
list(APPEND LLVM_LINK_COMPONENTS
Coverage
FrontendHLSL
LTO
)
# We will need to append the missing dependencies to pull in the right
# LLVM library dependencies.
list(APPEND link_libs
clangCodeGen
clangStaticAnalyzerCore
)

but it doesn't fully work, there are linking problems in current master when I tried two weeks ago.

The approach is hacky but can achieve what you need with far less friction. If you share what did not work I'd be happy to help fixing it rather to go the current direction.

You can try it yourself: Build our fork of LLVM and Clang with LLVM_LINK_LLVM_DYLIB and use it with builtin_llvm=OFF builtin_clang=OFF. It will progress to some degree and then either screw up still linking libLLVM.so, have unresolved symbols due to missing LLVM library dependencies, or eventually miss linking against system libz.so and libxml2.so.

@vgvassilev

Copy link
Copy Markdown
Member

Maybe we can separate these two discussions. Second, no, the current setup doesn't prevent us from screwing up. As I explained, statically linking in an external LLVM (even built without LLVM_LINK_LLVM_DYLIB) does not properly hide symbols in all cases.

What I actually meant is that we have some way to signal that we are doing something wrong. The approach that we statically link rootcling will be gone iirc.

If I understand correctly what you're saying, #12156 for you is a "feature" to signal a potential, but not guaranteed problem to the user?

Yes, this is an odd feature as we do not control the linker output but I see this as a feature saying -- you are on a very think ice and very likely you are doing something wrong.

For me, it is a bug in our build system that should eventually be fixed because that is where upstream LLVM is going.

If we figure out a more friendly message I am all ears. If you mean the libLLVM.so direction -- that's what's the claim since N years now but I do not think the community agrees completely with that. Partially because of the problem of leaked symbols and abi mismatches

That is the reason I expressed concerns about this PR to begin with. I do not think we should go that route. We lower the route towards using system llvm but at the same time we lower the bar to introduce setups that are prone to subtle abi mismatches.

I cannot argue against the point that there are (other) ways to screw up. But on the other hand, I firmly believe that it would be better to have the possibility than outright disallowing it. I would be ok with making this build mode conditional behind a (hidden) configuration flag, and otherwise clearly error during build rather than at run-time of rootcling.

So you are proposing to link rootcling against the system libLLVM.so, complain and move on? rootcling depends on libCling, that'd mean that we also will have to allow linking libCling.so to libLLVM.so which directly raises my concern again.

I know this code, it's extremely hacky, and it doesn't work in all cases: If Clang is built against an LLVM with LLVM_LINK_LLVM_DYLIB, the CMake targets only have libLLVM.so as dependency. You are trying to account for this with

# We just got rid of the libLLVM.so and other components shipped as shared
# libraries, we need to make up for the missing dependency.
list(APPEND LLVM_LINK_COMPONENTS
Coverage
FrontendHLSL
LTO
)
# We will need to append the missing dependencies to pull in the right
# LLVM library dependencies.
list(APPEND link_libs
clangCodeGen
clangStaticAnalyzerCore
)

but it doesn't fully work, there are linking problems in current master when I tried two weeks ago.

The approach is hacky but can achieve what you need with far less friction. If you share what did not work I'd be happy to help fixing it rather to go the current direction.

You can try it yourself: Build our fork of LLVM and Clang with LLVM_LINK_LLVM_DYLIB and use it with builtin_llvm=OFF builtin_clang=OFF. It will progress to some degree and then either screw up still linking libLLVM.so, have unresolved symbols due to missing LLVM library dependencies, or eventually miss linking against system libz.so and libxml2.so.

Ok, if we fixed these, would that be a feasible way forward for now, until we fix the clang issue?

@hahnjo

hahnjo commented Jan 27, 2026

Copy link
Copy Markdown
Member

That is the reason I expressed concerns about this PR to begin with. I do not think we should go that route. We lower the route towards using system llvm but at the same time we lower the bar to introduce setups that are prone to subtle abi mismatches.

I cannot argue against the point that there are (other) ways to screw up. But on the other hand, I firmly believe that it would be better to have the possibility than outright disallowing it. I would be ok with making this build mode conditional behind a (hidden) configuration flag, and otherwise clearly error during build rather than at run-time of rootcling.

So you are proposing to link rootcling against the system libLLVM.so, complain and move on? rootcling depends on libCling, that'd mean that we also will have to allow linking libCling.so to libLLVM.so which directly raises my concern again.

I want to link both rootcling and libCling.so against libLLVM.so if the user asks to. In my opinion, they already do by specifying builtin_llvm=ON, but I would also be with an even more explicit option (allow_llvm_dylib=ON).

You can try it yourself: Build our fork of LLVM and Clang with LLVM_LINK_LLVM_DYLIB and use it with builtin_llvm=OFF builtin_clang=OFF. It will progress to some degree and then either screw up still linking libLLVM.so, have unresolved symbols due to missing LLVM library dependencies, or eventually miss linking against system libz.so and libxml2.so.

Ok, if we fixed these, would that be a feasible way forward for now, until we fix the clang issue?

I don't think these can be fixed because we simply don't have the information about dependent LLVM libraries anymore. And it still doesn't allow building ROOT against libLLVM.so if the user wants it, for example because they only have the shared library and not the static ones at all.

@vgvassilev

vgvassilev commented Jan 27, 2026

Copy link
Copy Markdown
Member

That is the reason I expressed concerns about this PR to begin with. I do not think we should go that route. We lower the route towards using system llvm but at the same time we lower the bar to introduce setups that are prone to subtle abi mismatches.

I cannot argue against the point that there are (other) ways to screw up. But on the other hand, I firmly believe that it would be better to have the possibility than outright disallowing it. I would be ok with making this build mode conditional behind a (hidden) configuration flag, and otherwise clearly error during build rather than at run-time of rootcling.

So you are proposing to link rootcling against the system libLLVM.so, complain and move on? rootcling depends on libCling, that'd mean that we also will have to allow linking libCling.so to libLLVM.so which directly raises my concern again.

I want to link both rootcling and libCling.so against libLLVM.so if the user asks to. In my opinion, they already do by specifying builtin_llvm=ON, but I would also be with an even more explicit option (allow_llvm_dylib=ON).

Ok. In that case it seems we are completing the circle. I think we should not do this because of the arguments I raised. The problem we are discussing with symbols and jits is very subtle and complicated. I do not think there are a lot of people in the world who practically understand it not to speak for the average ROOT user who nominally wants to spend less time in building. EDIT: That means we can hardly rely on user's choice here.

You can try it yourself: Build our fork of LLVM and Clang with LLVM_LINK_LLVM_DYLIB and use it with builtin_llvm=OFF builtin_clang=OFF. It will progress to some degree and then either screw up still linking libLLVM.so, have unresolved symbols due to missing LLVM library dependencies, or eventually miss linking against system libz.so and libxml2.so.

Ok, if we fixed these, would that be a feasible way forward for now, until we fix the clang issue?

I don't think these can be fixed because we simply don't have the information about dependent LLVM libraries anymore.

I do not think we will need all of that information to get ROOT to work.

And it still doesn't allow building ROOT against libLLVM.so if the user wants it, for example because they only have the shared library and not the static ones at all.

Yes, and my claim is that we should not help such setups to become footguns.

In similar cases I'd usually say if you want to maintain such a feature I wouldn't stand in the way but this is different because it can subtly break distributions depending on what's installed on the system. Having that behind an one more option does not make me happier since a) the direction, in my opinion, is concerning as it would make things more fragile in an already fragile system; b) we already have more options that we want.

In an incremental world we have a more conservative, less fragile and more sound to the current state of art way: strip away libLLVM.so as discussed and solve the 90% of the raised practical points.

@dpiparo

dpiparo commented Jun 11, 2026

Copy link
Copy Markdown
Member

Dear all,

I would like to revamp this PR.
For the ROOT project it's important to remove all the obstacles on the way to the usage of ROOT against an unpatched Clang, that more and more is shipped with LLVM as a shared object.
For this reason I suggest to:

  1. Fix the unfortunate conflicts that now affect these changes
  2. Merge the PR to the main branch

@devajithvs could you take care?

@devajithvs devajithvs force-pushed the rootcling.optparse branch from 195fcf1 to c038e90 Compare June 11, 2026 08:53
This would potentially remove issues like

```
CommandLine Error: Option 'W' registered more than once!
```

caused by `llvm::cl` registering the same options multiple times
when ROOT is linked against a shared `libLLVM`.
This replaces many usages of `shortflags` with multiple characters
whenever possible.

Some flags are omitted (e.g., `-writeEmptyRootPCM`) as they cause the diff
to be huge and can go in a separate PR.
@devajithvs devajithvs force-pushed the rootcling.optparse branch from 8c441c2 to 8f54bc5 Compare June 15, 2026 07:12
@devajithvs devajithvs marked this pull request as ready for review June 15, 2026 11:49
@devajithvs devajithvs force-pushed the rootcling.optparse branch from 8f54bc5 to b2af2a8 Compare June 15, 2026 15:41
@devajithvs devajithvs requested a review from hahnjo as a code owner June 15, 2026 15:41
@devajithvs devajithvs force-pushed the rootcling.optparse branch from b2af2a8 to 8f54bc5 Compare June 15, 2026 15:42
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.

5 participants