Skip to content

enhance custom easyblock for LLVM: ensure sysroot dynamic linker is used + add ignore patterns for failing tests that can be ignored #3741

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

Merged
merged 28 commits into from
Jun 7, 2025
Merged
Changes from 9 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
11cccdc
Ensure sysroot dynamic linker is used
Crivella May 28, 2025
10c0565
Improved sanity_check
Crivella May 28, 2025
d755cb7
IMproved logic and logging
Crivella May 28, 2025
a7e531f
Fix
Crivella May 28, 2025
1f6b5e7
Fix not escaping loop once linker is found
Crivella May 28, 2025
10c5d8c
Do not use symlinks as the dynamic linker
Crivella May 28, 2025
dfa308f
Fix wrong parameter name
Crivella May 28, 2025
f6d7df2
Test adding the GCCcore libs to the cfg files
Crivella May 28, 2025
e156ff2
Fix name
Crivella May 28, 2025
61bce01
Update easybuild/easyblocks/l/llvm.py
Crivella May 28, 2025
ff5f64c
WIP: enforce dynamic linker also for runtimes
Crivella Jun 2, 2025
253bc99
-L only GCCcore
Crivella Jun 2, 2025
0b8e4ae
Added creation of CFG file also for stage 1 in case of non-bootstrap …
Crivella Jun 2, 2025
804b22b
WIP: Make tests pick up correct `libstdc++.so`
Crivella Jun 3, 2025
6a2ec62
Some ompd tests will end up using the system GDB that can pick the wr…
Crivella Jun 3, 2025
73f6227
Add fix to always use default configuration files in `compiler-rt` tests
Crivella Jun 4, 2025
41596a1
Added more descriptions
Crivella Jun 4, 2025
735af23
Ignore tests failing due to hardcoded paths that do no account for `s…
Crivella Jun 4, 2025
6a44669
More verbose
Crivella Jun 4, 2025
917cacb
Disallow sysroot builds for LLVM older than 19
Crivella Jun 5, 2025
d53f1d1
Added new ignore patterns
Crivella Jun 5, 2025
af94102
Moved the update of the ignore patterns in a dedicated method
Crivella Jun 5, 2025
28ef722
minor tweaks to LLVM easyblock
boegel Jun 6, 2025
86c0d80
Merge pull request #1 from boegel/fix-LLVM-dynlink
Crivella Jun 6, 2025
079e0e1
Missing f-string classifier
Crivella Jun 6, 2025
c825c94
Update easybuild/easyblocks/l/llvm.py
Crivella Jun 6, 2025
ced857f
Revert "Missing f-string classifier"
Crivella Jun 6, 2025
edd5e7a
Missing f-string classifier without isort
Crivella Jun 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 81 additions & 12 deletions easybuild/easyblocks/l/llvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ def __init__(self, *args, **kwargs):
self.offload_targets = ['host']
# self._added_librt = None
self.host_triple = None
self.dynamic_linker = None

# Shared
off_opts, on_opts = [], []
Expand Down Expand Up @@ -354,10 +355,12 @@ def __init__(self, *args, **kwargs):
general_opts['LLVM_INCLUDE_GO_TESTS'] = 'OFF'

# Sysroot
sysroot = build_option('sysroot')
if sysroot:
general_opts['DEFAULT_SYSROOT'] = sysroot
general_opts['CMAKE_SYSROOT'] = sysroot
self.sysroot = build_option('sysroot')
if self.sysroot:
general_opts['DEFAULT_SYSROOT'] = self.sysroot
general_opts['CMAKE_SYSROOT'] = self.sysroot
self._set_dynamic_linker()
trace_msg(f"Using '{self.dynamic_linker}' as dynamic linker from sysroot {self.sysroot}")

# list of CUDA compute capabilities to use can be specifed in two ways (where (2) overrules (1)):
# (1) in the easyconfig file, via the custom cuda_compute_capabilities;
Expand Down Expand Up @@ -566,6 +569,20 @@ def _set_gcc_prefix(self):
self.gcc_prefix = gcc_prefix
self.log.debug("Using %s as the gcc install location", self.gcc_prefix)

def _set_dynamic_linker(self):
"""Set the dynamic linker for the build if not the default one."""
if self.sysroot:
linkers = glob.glob(os.path.join(self.sysroot, '**', 'ld-*.so*'))
for linker in linkers:
if os.path.isfile(linker) and not os.path.islink(linker):
self.log.info("Using linker %s from sysroot", linker)
self.dynamic_linker = linker
break
else:
msg = f"No linker found in sysroot {self.sysroot}, using default linker"
trace_msg(msg)
self.log.warning(msg)

def configure_step(self):
"""
Install extra tools in bin/; enable zlib if it is a dep; optionally enable rtti; and set the build target
Expand Down Expand Up @@ -745,13 +762,33 @@ def configure_step3(self):
if self.full_llvm:
self._cmakeopts.update(remove_gcc_dependency_opts)

@staticmethod
def _create_compiler_config_file(compilers, gcc_prefix, installdir):
def _create_compiler_config_file(self, installdir):
"""Create a config file for the compiler to point to the correct GCC installation."""
self._set_gcc_prefix()
bin_dir = os.path.join(installdir, 'bin')
prefix_str = '--gcc-install-dir=%s' % gcc_prefix
for comp in compilers:
write_file(os.path.join(bin_dir, f'{comp}.cfg'), prefix_str)
opts = [f'--gcc-install-dir={self.gcc_prefix}']

if self.dynamic_linker:
opts.append(f'-Wl,-dynamic-linker,{self.dynamic_linker}')
# The --dyld-prefix flag exists, but beside being poorly documented it is also not supported by flang
# https://reviews.llvm.org/D851
# prefix = self.sysroot.rstrip('/')
# opts.append(f'--dyld-prefix={prefix}')

# Check, for a non `full_llvm` build, if GCCcore is in the LIBRARY_PATH, and if not add it
# This is needed as the runtimes tests will not add the -L option to the linker command line for GCCcore
# otherwise
if not self.full_llvm:
gcc_root = get_software_root('GCCcore')
gcc_lib = os.path.join(gcc_root, 'lib64')
lib_path = os.getenv('LLIBRARY_PATH', '')
if gcc_lib not in lib_path:
self.log.info("Adding GCCcore libraries location `%s` the config files", gcc_lib)
lib_path = f"{gcc_lib}:{lib_path}" if lib_path else gcc_lib
opts.append(f'-L{lib_path}')

for comp in self.cfg_compilers:
write_file(os.path.join(bin_dir, f'{comp}.cfg'), ' '.join(opts))

def build_with_prev_stage(self, prev_dir, stage_dir):
"""Build LLVM using the previous stage."""
Expand Down Expand Up @@ -826,7 +863,7 @@ def build_with_prev_stage(self, prev_dir, stage_dir):
# Also runs of the intermediate step compilers should be made aware of the GCC installation
if LooseVersion(self.version) >= LooseVersion('19'):
self._set_gcc_prefix()
self._create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, prev_dir)
self._create_compiler_config_file(prev_dir)

self.add_cmake_opts()

Expand Down Expand Up @@ -1011,7 +1048,7 @@ def test_step(self):
# Also runs of test suite compilers should be made aware of the GCC installation
if LooseVersion(self.version) >= LooseVersion('19'):
self._set_gcc_prefix()
self._create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.final_dir)
self._create_compiler_config_file(self.final_dir)

self.ignore_patterns = self.cfg['test_suite_ignore_patterns'] or []

Expand Down Expand Up @@ -1072,7 +1109,7 @@ def install_step(self):
# For GCC aware installation create config files in order to point to the correct GCC installation
# Required as GCC_INSTALL_PREFIX was removed (see https://github.com/llvm/llvm-project/pull/87360)
self._set_gcc_prefix()
self._create_compiler_config_file(self.cfg_compilers, self.gcc_prefix, self.installdir)
self._create_compiler_config_file(self.installdir)

# This is needed as some older build system will select a different naming scheme for the library leading to
# The correct target <__config_site> and libclang_rt.builtins.a not being found
Expand Down Expand Up @@ -1138,6 +1175,36 @@ def _sanity_check_gcc_prefix(compilers, gcc_prefix, installdir):
error_msg = f"GCC installation path '{check_prefix}' does not match expected path '{gcc_prefix}'"
raise EasyBuildError(error_msg)

def _sanity_check_dynamic_linker(self):
"""Check if the dynamic linker is correct."""
if self.sysroot:
c_name = 'abcdefg.c'
o_name = 'abcdefg.o'
x_name = 'abcdefg.x'
with open(c_name, 'w', encoding='utf-8') as f:
f.write('#include <stdio.h>\n')
f.write('int main() { printf("Hello World\\n"); return 0; }\n')
cmd = f"{os.path.join(self.installdir, 'bin', 'clang')} -o {o_name} -c {c_name}"
run_shell_cmd(cmd, fail_on_error=True)
cmd = f"{os.path.join(self.installdir, 'bin', 'clang')} -v -o {x_name} {o_name}"
res = run_shell_cmd(cmd, fail_on_error=True)
out = res.output

# Check if the dynamic linker is set to the sysroot
if self.sysroot not in out:
error_msg = f"Dynamic linker is not set to the sysroot '{self.sysroot}'"
raise EasyBuildError(error_msg)

cmd = f'./{x_name}'
res = run_shell_cmd(cmd, fail_on_error=False)
if res.exit_code != EasyBuildExit.SUCCESS:
error_msg = f"Failed to run the compiled executable '{x_name}' for testing the dynamic linker"
raise EasyBuildError(error_msg)

remove_file(c_name)
remove_file(o_name)
remove_file(x_name)

def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False, extra_modules=None):
"""Perform sanity checks on the installed LLVM."""
lib_dir_runtime = None
Expand Down Expand Up @@ -1331,8 +1398,10 @@ def sanity_check_step(self, custom_paths=None, custom_commands=None, extension=F
# Required for 'clang -v' to work if linked to LLVM runtimes
with _wrap_env(ld_path=os.path.join(self.installdir, lib_dir_runtime)):
self._sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir)
self._sanity_check_dynamic_linker()
else:
self._sanity_check_gcc_prefix(gcc_prefix_compilers, self.gcc_prefix, self.installdir)
self._sanity_check_dynamic_linker()

return super().sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands)

Expand Down