Skip to content

Fix LLVM easyblock for no-runtime builds (like Clang 20.1.8)#4083

Open
Micket wants to merge 2 commits intoeasybuilders:developfrom
Micket:20260227191350_new_pr_llvm
Open

Fix LLVM easyblock for no-runtime builds (like Clang 20.1.8)#4083
Micket wants to merge 2 commits intoeasybuilders:developfrom
Micket:20260227191350_new_pr_llvm

Conversation

@Micket
Copy link
Contributor

@Micket Micket commented Feb 27, 2026

(created using eb --new-pr)

lib_runtime_dir is assigned None value, but this sanity check doesn't control for that. This crashes easybuild hard as os.path.join(None, 'xxx') errors with TypeError.

I don't know the whole logic here, i just protect that use of this variable.

@Micket Micket requested a review from Thyre February 27, 2026 18:14
@Thyre
Copy link
Collaborator

Thyre commented Feb 27, 2026

Were you able to trigger this error somehow?
I'm guessing one could trigger this by disabling runtimes but keeping OpenMP with LLVM 19+? This looks to be an oversight.

Guarding the check for an empty lib_runtime_dir sounds good in any case.
I'll try to figure out if I can reproduce the crash by running a few --sanity-check-only commands.

@Thyre Thyre added the bug fix label Feb 27, 2026
@Thyre Thyre added this to the next release (5.2.2?) milestone Feb 27, 2026
@Micket
Copy link
Contributor Author

Micket commented Feb 27, 2026

Yes, it carshes hard at the end of my 3 hour clang build 😢

== sanity checking...
  >> loading modules: binutils/2.44-GCCcore-14.3.0, Clang/20.1.8-GCCcore-14.3.0...
== ... (took 2 secs)
== Summary:
   * [FAILED]  Clang/20.1.8-GCCcore-14.3.0

EasyBuild crashed! Please consider reporting a bug, this should not happen...

Traceback (most recent call last):
  File "/usr/lib64/python3.9/runpy.py", line 197, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib64/python3.9/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/main.py", line 862, in <module>
    main_with_hooks()
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/main.py", line 847, in main_with_hooks
    exit_code: EasyBuildExit = main(args=args, prepared_cfg_data=(init_session_state, eb_go, cfg_settings))
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/main.py", line 798, in main
    is_successful = process_eb_args(orig_paths, eb_go, cfg_settings, modtool, testing, init_session_state,
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/main.py", line 614, in process_eb_args
    ecs_with_res = build_and_install_software(
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/main.py", line 226, in build_and_install_software
    raise error
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/main.py", line 180, in build_and_install_software
    (ec_res['success'], app_log, err_msg, err_code) = build_and_install_one(ec, init_env)
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/framework/easyblock.py", line 5120, in build_and_install_one
    result = app.run_all_steps(run_test_cases=run_test_cases)
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/framework/easyblock.py", line 4932, in run_all_steps
    self.run_step(step_name, step_methods)
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/framework/easyblock.py", line 4773, in run_step
    current_method()
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/easyblocks/l/llvm.py", line 1813, in sanity_check_step
    check_files.extend(os.path.join(lib_dir_runtime, x) for x in check_librt_files)
  File "/apps/Common/software/EasyBuild/5.2.1/lib/python3.9/site-packages/easybuild/easyblocks/l/llvm.py", line 1813, in <genexpr>
    check_files.extend(os.path.join(lib_dir_runtime, x) for x in check_librt_files)
  File "/usr/lib64/python3.9/posixpath.py", line 76, in join
    a = os.fspath(a)
TypeError: expected str, bytes or os.PathLike object, not NoneType

@Thyre
Copy link
Collaborator

Thyre commented Feb 27, 2026

Yeah, disabling build_runtimes in a recent LLVM with OpenMP enabled triggers this.
With --sanity-check-only:

EasyBuild crashed! Please consider reporting a bug, this should not happen...

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/main.py", line 862, in <module>
    main_with_hooks()
    ~~~~~~~~~~~~~~~^^
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/main.py", line 847, in main_with_hooks
    exit_code: EasyBuildExit = main(args=args, prepared_cfg_data=(init_session_state, eb_go, cfg_settings))
                               ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/main.py", line 798, in main
    is_successful = process_eb_args(orig_paths, eb_go, cfg_settings, modtool, testing, init_session_state,
                                    hooks, do_build)
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/main.py", line 614, in process_eb_args
    ecs_with_res = build_and_install_software(
        ordered_ecs, init_session_state, exit_on_failure=exit_on_failure, testing=testing)
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/main.py", line 226, in build_and_install_software
    raise error
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/main.py", line 180, in build_and_install_software
    (ec_res['success'], app_log, err_msg, err_code) = build_and_install_one(ec, init_env)
                                                      ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/framework/easyblock.py", line 5120, in build_and_install_one
    result = app.run_all_steps(run_test_cases=run_test_cases)
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/framework/easyblock.py", line 4932, in run_all_steps
    self.run_step(step_name, step_methods)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/framework/easyblock.py", line 4773, in run_step
    current_method()
    ~~~~~~~~~~~~~~^^
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/easyblocks/l/llvm.py", line 1813, in sanity_check_step
    check_files.extend(os.path.join(lib_dir_runtime, x) for x in check_librt_files)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/EasyBuild/lib/python3.14/site-packages/easybuild/easyblocks/l/llvm.py", line 1813, in <genexpr>
    check_files.extend(os.path.join(lib_dir_runtime, x) for x in check_librt_files)
                       ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "<frozen posixpath>", line 77, in join
TypeError: expected str, bytes or os.PathLike object, not NoneType

I'd keep your fix for sure, guarding these things is always a good idea I think.
To still check for OpenMP, we'd need to extend the if here:

https://github.com/easybuilders/easybuild-easyblocks/blob/c0b3a8983af664f7226eaa9e503840f6b8c3bbf2/easybuild/easyblocks/l/llvm.py#L1617C1-L1618C73

to also check if openmp is in final_runtimes (and the LLVM version is greater than 19...).

CC: @Crivella

@Thyre
Copy link
Collaborator

Thyre commented Feb 27, 2026

Test report by @Thyre

Overview of tested easyconfigs (in order)

  • SUCCESS LLVM-21.1.8-GCCcore-15.2.0.eb

Build succeeded for 1 out of 1 (total: 1 secs) (1 easyconfigs in total)
Linux - Linux Arch Linux UNKNOWN, x86_64, AMD Ryzen 7 7800X3D 8-Core Processor (zen4), 1 x AMD Navi 48 [Radeon RX 9070/9070 XT/9070 GRE] (device id: 0x7550, gfx: gfx1201, driver: Linuxversion6.18.13-arch1-1(linux@archlinux)(gcc(GCC)15.2.120260209,GNUld(GNUBinutils)2.46)#1SMPPREEMPT_DYNAMICWed,25Feb202623:12:35+0000), 1 x AMD Raphael (device id: 0x164e, gfx: gfx1036, driver: Linuxversion6.18.13-arch1-1(linux@archlinux)(gcc(GCC)15.2.120260209,GNUld(GNUBinutils)2.46)#1SMPPREEMPT_DYNAMICWed,25Feb202623:12:35+0000), Python 3.14.3
See https://gist.github.com/Thyre/66ab7f637fc624eda358a41c141f60ba for a full test report.

@Thyre
Copy link
Collaborator

Thyre commented Feb 27, 2026

Wait, we don't need a more complicated check. If there are any files in check_librt_files, we need lib_dir_runtime. So we could always initialize it after we determined which paths we want to check.

Let me try to come up with a draft.

@Thyre
Copy link
Collaborator

Thyre commented Feb 27, 2026

I think this might be sufficient enough:

diff --git a/easybuild/easyblocks/l/llvm.py b/easybuild/easyblocks/l/llvm.py
index d52cc8945..258b76e98 100644
--- a/easybuild/easyblocks/l/llvm.py
+++ b/easybuild/easyblocks/l/llvm.py
@@ -1613,9 +1613,6 @@ class EB_LLVM(CMakeMake):

     def sanity_check_step(self, custom_paths=None, custom_commands=None, *args, **kwargs):
         """Perform sanity checks on the installed LLVM."""
-        lib_dir_runtime = None
-        if self.cfg['build_runtimes']:
-            lib_dir_runtime = self.get_runtime_lib_path(self.installdir)
         shlib_ext = '.' + get_shared_lib_ext()

         if not hasattr(self, 'nvptx_target_cond'):
@@ -1808,6 +1805,10 @@ class EB_LLVM(CMakeMake):
             custom_commands += ["python -c 'import clang'"]
             custom_commands += ["python -c 'import mlir'"]

+        lib_dir_runtime = None
+        if self.cfg['build_runtimes'] or check_librt_files:
+            lib_dir_runtime = self.get_runtime_lib_path(self.installdir)
+
         check_files.extend(os.path.join('bin', x) for x in check_bin_files)
         check_files.extend(os.path.join('lib', x) for x in check_lib_files)
         check_files.extend(os.path.join(lib_dir_runtime, x) for x in check_librt_files)

@Micket
Copy link
Contributor Author

Micket commented Feb 27, 2026

But even then you need to protect

         check_files.extend(os.path.join(lib_dir_runtime, x) for x in check_librt_files)

if lib_dir_runtime could ever be None. If it really can't be, then we should always set it, not allowing it to be None?

@Thyre
Copy link
Collaborator

Thyre commented Feb 27, 2026

But even then you need to protect

         check_files.extend(os.path.join(lib_dir_runtime, x) for x in check_librt_files)

if lib_dir_runtime could ever be None. If it really can't be, then we should always set it, not allowing it to be None?

lib_dir_runtime should always be set if we have at least one check_librt_file (or have at least one runtime built, which makes use of the runtime library dir). Guarding this line still makes sense, just in case we're missing something.

It's certainly better to miss a check than to hard crash.
So yeah, your change together with the one I proposed above should be sufficient I think.
It would still check OpenMP for LLVM 19+ if all other runtimes are disabled.

@Micket
Copy link
Contributor Author

Micket commented Feb 27, 2026

Feel free to submit test reports. I'll upload for Clang.

@boegel boegel changed the title Fix llvm.py for no-runtime builds Fix LLVM easyblock for no-runtime builds (like Clang 20.1.8) Feb 27, 2026
@Micket
Copy link
Contributor Author

Micket commented Feb 28, 2026

Test report by @Micket

Overview of tested easyconfigs (in order)

Build succeeded for 0 out of 1 (total: 2 hours 48 mins 25 secs) (1 easyconfigs in total)
vera-r07-02 - Linux Rocky Linux 9.6, x86_64, Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz, 1 x NVIDIA NVIDIA A40, 580.105.08, Python 3.9.21
See https://gist.github.com/Micket/43f1945a72cf2ead19b1baddf6e878b1 for a full test report.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants