From 0c1d70693b5440c11f3b1d779b01b0d38266f1a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=96hman?= Date: Sun, 19 Oct 2025 23:32:18 +0000 Subject: [PATCH 1/6] adding easyblocks: systemd_wrapper.py --- easybuild/easyblocks/s/systemd_wrapper.py | 162 ++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 easybuild/easyblocks/s/systemd_wrapper.py diff --git a/easybuild/easyblocks/s/systemd_wrapper.py b/easybuild/easyblocks/s/systemd_wrapper.py new file mode 100644 index 0000000000..51ea48ac86 --- /dev/null +++ b/easybuild/easyblocks/s/systemd_wrapper.py @@ -0,0 +1,162 @@ +## +# Copyright 2021-2025 Vrije Universiteit Brussel +# +# This file is part of EasyBuild, +# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), +# with support of Ghent University (http://ugent.be/hpc), +# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be), +# Flemish Research Foundation (FWO) (http://www.fwo.be/en) +# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). +# +# https://github.com/easybuilders/easybuild +# +# EasyBuild is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation v2. +# +# EasyBuild is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with EasyBuild. If not, see . +## +""" +EasyBuild support for installing a wrapper module file for systemd + +@author: Mikael Öhman (Chalmers University of Technology) +""" +import os +import re +from urllib.parse import urlparse + +from easybuild.tools import LooseVersion + +from easybuild.easyblocks.generic.bundle import Bundle +from easybuild.framework.easyconfig import CUSTOM +from easybuild.tools.build_log import EasyBuildError, print_warning +from easybuild.tools.filetools import change_dir, expand_glob_paths, mkdir, read_file, symlink, which, write_file +from easybuild.tools.run import run_shell_cmd +from easybuild.tools.systemtools import DARWIN, LINUX, get_os_type, get_shared_lib_ext, find_library_path + + +def get_sys_lib_dirs(): + res = run_shell_cmd('gcc -print-search-dirs', hidden=True, in_dry_run=True) + match = re.search(r"^libraries:\s*=(.*)$", res.output, re.MULTILINE) + if match: + return [p for p in match.group(1).split(":") if p] + return [] + + +def get_sys_include_dirs(): + cmd = "LC_ALL=C gcc -E -Wp,-v -xc /dev/null" + res = run_shell_cmd(cmd, hidden=True, in_dry_run=True) + sys_include_dirs = [] + for match in re.finditer(r'^\s(/[^\0\n]*)+', res.output, re.MULTILINE): + sys_include_dirs.extend(match.groups()) + return sys_include_dirs + + +def get_sys_pc_dirs(): + res = run_shell_cmd('pkg-config --variable pc_path pkgconf', hidden=True, in_dry_run=True) + return res.output.strip().split(':') + + +class EB_systemd_wrapper(Bundle): + """ + Find path to installation files of systemd and udev in the host system. + """ + + def __init__(self, *args, **kwargs): + """Locate the installation files of systemd in the host system""" + super().__init__(*args, **kwargs) + + shlib_ext = get_shared_lib_ext() + systemd_lib_patterns = [f'libsystemd.{shlib_ext}', f'libudev.{shlib_ext}'] + systemd_include_patterns = ['systemd', 'libudev.h'] + systemd_pc_patterns = ['systemd.pc', 'udev.pc'] + + self.systemd = dict() + + # Check lib paths for systemd libraries + sys_lib_dirs = get_sys_lib_dirs() + self.log.debug('Found the following lib directories in host system: %s', ', '.join(sys_lib_dirs)) + + self.systemd['libs'] = [] + for systemd_lib_pattern in systemd_lib_patterns: + for sys_lib_dir in sys_lib_dirs: + file = os.path.join(sys_lib_dir, systemd_lib_pattern) + if os.path.exists(file): + self.systemd['libs'].append(file) + break + else: + raise EasyBuildError('Could not find library: %s.', systemd_lib_pattern) + + # Check system include paths for systemd headers + sys_include_dirs = get_sys_include_dirs() + self.log.debug('Found the following include directories in host system: %s', ', '.join(sys_include_dirs)) + + self.systemd['includes'] = [] + for systemd_include_pattern in systemd_include_patterns: + for sys_include_dir in sys_include_dirs: + file = os.path.join(sys_include_dir, systemd_include_pattern) + if os.path.exists(file): + self.systemd['includes'].append(file) + break + else: + raise EasyBuildError('Could not find includes: %s.', systemd_include_pattern) + + # Check pkgconfig paths + sys_pc_dirs = get_sys_pc_dirs() + print(sys_pc_dirs) + self.log.debug("Found the following pkgconfig directories in host system: %s", ', '.join(sys_pc_dirs)) + + self.systemd['pcs'] = [] + for systemd_pc_pattern in systemd_pc_patterns: + for sys_pc_dir in sys_pc_dirs: + file = os.path.join(sys_pc_dir, systemd_pc_pattern) + if os.path.exists(file): + self.systemd['pcs'].append(file) + break + else: + raise EasyBuildError('Could not find pkgconfig file: %s.', systemd_pc_pattern) + + def fetch_step(self, *args, **kwargs): + """Nothing to fetch""" + + def extract_step(self): + """No sources to extract""" + + def configure_step(self): + """No configure step""" + + def build_step(self): + """No configure step""" + + def install_step(self): + """Symlink OS systemd installation""" + include_dir = os.path.join(self.installdir, 'include') + mkdir(include_dir, parents=True) + for file in self.systemd['includes']: + symlink(file, os.path.join(include_dir, os.path.basename(file))) + + lib_dir = os.path.join(self.installdir, 'lib') + mkdir(lib_dir, parents=True) + for file in self.systemd['libs']: + symlink(file, os.path.join(lib_dir, os.path.basename(file))) + + pc_dir = os.path.join(self.installdir, 'share', 'pkgconfig') + mkdir(pc_dir, parents=True) + for file in self.systemd['pcs']: + symlink(file, os.path.join(pc_dir, os.path.basename(file))) + + def sanity_check_step(self): + """Custom sanity check for systemd wrapper.""" + shlib_ext = get_shared_lib_ext() + custom_paths = { + 'files': [f'lib/libsystemd.{shlib_ext}', f'libudev.{shlib_ext}', 'include/udev.h', + 'share/pkgconfig/libsystemd.pc', 'share/pkgconfig/libudev.pc'], + 'dirs': ['include/systemd'], + } + return super().sanity_check_step(custom_paths=custom_paths, custom_commands=[]) From e95a54f819c5d413d4c2759b7f925f45d86b18f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=96hman?= Date: Sun, 19 Oct 2025 23:36:20 +0000 Subject: [PATCH 2/6] remove unused imports --- easybuild/easyblocks/s/systemd_wrapper.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/s/systemd_wrapper.py b/easybuild/easyblocks/s/systemd_wrapper.py index 51ea48ac86..0875163bd7 100644 --- a/easybuild/easyblocks/s/systemd_wrapper.py +++ b/easybuild/easyblocks/s/systemd_wrapper.py @@ -34,11 +34,10 @@ from easybuild.tools import LooseVersion from easybuild.easyblocks.generic.bundle import Bundle -from easybuild.framework.easyconfig import CUSTOM -from easybuild.tools.build_log import EasyBuildError, print_warning -from easybuild.tools.filetools import change_dir, expand_glob_paths, mkdir, read_file, symlink, which, write_file +from easybuild.tools.build_log import EasyBuildError +from easybuild.tools.filetools import mkdir, symlink from easybuild.tools.run import run_shell_cmd -from easybuild.tools.systemtools import DARWIN, LINUX, get_os_type, get_shared_lib_ext, find_library_path +from easybuild.tools.systemtools import get_shared_lib_ext def get_sys_lib_dirs(): From 94066d86b1149fd12f3630e0f271c7a621cd932f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=96hman?= Date: Sun, 19 Oct 2025 23:37:00 +0000 Subject: [PATCH 3/6] remove unused imports --- easybuild/easyblocks/s/systemd_wrapper.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/easybuild/easyblocks/s/systemd_wrapper.py b/easybuild/easyblocks/s/systemd_wrapper.py index 0875163bd7..f567f20abf 100644 --- a/easybuild/easyblocks/s/systemd_wrapper.py +++ b/easybuild/easyblocks/s/systemd_wrapper.py @@ -29,9 +29,6 @@ """ import os import re -from urllib.parse import urlparse - -from easybuild.tools import LooseVersion from easybuild.easyblocks.generic.bundle import Bundle from easybuild.tools.build_log import EasyBuildError From 69702676ab7a78db8801cd57bae38b4e379329c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=96hman?= Date: Mon, 20 Oct 2025 13:34:33 +0000 Subject: [PATCH 4/6] Handle errors during install --- easybuild/easyblocks/s/systemd_wrapper.py | 45 ++++++++++++++--------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/easybuild/easyblocks/s/systemd_wrapper.py b/easybuild/easyblocks/s/systemd_wrapper.py index f567f20abf..e9234da059 100644 --- a/easybuild/easyblocks/s/systemd_wrapper.py +++ b/easybuild/easyblocks/s/systemd_wrapper.py @@ -79,44 +79,44 @@ def __init__(self, *args, **kwargs): sys_lib_dirs = get_sys_lib_dirs() self.log.debug('Found the following lib directories in host system: %s', ', '.join(sys_lib_dirs)) - self.systemd['libs'] = [] + self.systemd['libs'] = dict() for systemd_lib_pattern in systemd_lib_patterns: for sys_lib_dir in sys_lib_dirs: file = os.path.join(sys_lib_dir, systemd_lib_pattern) if os.path.exists(file): - self.systemd['libs'].append(file) + self.systemd['libs'][systemd_lib_pattern] = file break else: - raise EasyBuildError('Could not find library: %s.', systemd_lib_pattern) + self.systemd['pcs'][systemd_pc_pattern] = None # Check system include paths for systemd headers sys_include_dirs = get_sys_include_dirs() self.log.debug('Found the following include directories in host system: %s', ', '.join(sys_include_dirs)) - self.systemd['includes'] = [] + self.systemd['includes'] = dict() for systemd_include_pattern in systemd_include_patterns: for sys_include_dir in sys_include_dirs: file = os.path.join(sys_include_dir, systemd_include_pattern) if os.path.exists(file): - self.systemd['includes'].append(file) + self.systemd['includes'][systemd_include_pattern] = file break else: - raise EasyBuildError('Could not find includes: %s.', systemd_include_pattern) + self.systemd['pcs'][systemd_pc_pattern] = None # Check pkgconfig paths sys_pc_dirs = get_sys_pc_dirs() print(sys_pc_dirs) self.log.debug("Found the following pkgconfig directories in host system: %s", ', '.join(sys_pc_dirs)) - self.systemd['pcs'] = [] + self.systemd['pcs'] = dict() for systemd_pc_pattern in systemd_pc_patterns: for sys_pc_dir in sys_pc_dirs: file = os.path.join(sys_pc_dir, systemd_pc_pattern) if os.path.exists(file): - self.systemd['pcs'].append(file) + self.systemd['pcs'][systemd_pc_pattern] = file break else: - raise EasyBuildError('Could not find pkgconfig file: %s.', systemd_pc_pattern) + self.systemd['pcs'][systemd_pc_pattern] = None def fetch_step(self, *args, **kwargs): """Nothing to fetch""" @@ -132,20 +132,29 @@ def build_step(self): def install_step(self): """Symlink OS systemd installation""" - include_dir = os.path.join(self.installdir, 'include') - mkdir(include_dir, parents=True) - for file in self.systemd['includes']: - symlink(file, os.path.join(include_dir, os.path.basename(file))) - lib_dir = os.path.join(self.installdir, 'lib') mkdir(lib_dir, parents=True) - for file in self.systemd['libs']: - symlink(file, os.path.join(lib_dir, os.path.basename(file))) + for pattern, file in self.systemd['libs'].items(): + if file is not None: + symlink(file, os.path.join(lib_dir, os.path.basename(file))) + else: + raise EasyBuildError('Could not find library: %s', pattern) + + include_dir = os.path.join(self.installdir, 'include') + mkdir(include_dir, parents=True) + for pattern, file in self.systemd['includes'].items(): + if file is not None: + symlink(file, os.path.join(include_dir, os.path.basename(file))) + else: + raise EasyBuildError('Could not find include: %s', pattern) pc_dir = os.path.join(self.installdir, 'share', 'pkgconfig') mkdir(pc_dir, parents=True) - for file in self.systemd['pcs']: - symlink(file, os.path.join(pc_dir, os.path.basename(file))) + for pattern, file in self.systemd['pcs'].items(): + if file is not None: + symlink(file, os.path.join(pc_dir, os.path.basename(file))) + else: + raise EasyBuildError('Could not find pkgconfig files: %s', pattern) def sanity_check_step(self): """Custom sanity check for systemd wrapper.""" From da89cf6d4d9810b81f367d87f731e62cdfa641d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=96hman?= Date: Mon, 20 Oct 2025 13:36:49 +0000 Subject: [PATCH 5/6] Fix copy paste mistakes --- easybuild/easyblocks/s/systemd_wrapper.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/easybuild/easyblocks/s/systemd_wrapper.py b/easybuild/easyblocks/s/systemd_wrapper.py index e9234da059..866893fb5e 100644 --- a/easybuild/easyblocks/s/systemd_wrapper.py +++ b/easybuild/easyblocks/s/systemd_wrapper.py @@ -77,7 +77,7 @@ def __init__(self, *args, **kwargs): # Check lib paths for systemd libraries sys_lib_dirs = get_sys_lib_dirs() - self.log.debug('Found the following lib directories in host system: %s', ', '.join(sys_lib_dirs)) + self.log.info('Found the following lib directories in host system: %s', ', '.join(sys_lib_dirs)) self.systemd['libs'] = dict() for systemd_lib_pattern in systemd_lib_patterns: @@ -87,11 +87,11 @@ def __init__(self, *args, **kwargs): self.systemd['libs'][systemd_lib_pattern] = file break else: - self.systemd['pcs'][systemd_pc_pattern] = None + self.systemd['libs'][systemd_lib_pattern] = None # Check system include paths for systemd headers sys_include_dirs = get_sys_include_dirs() - self.log.debug('Found the following include directories in host system: %s', ', '.join(sys_include_dirs)) + self.log.info('Found the following include directories in host system: %s', ', '.join(sys_include_dirs)) self.systemd['includes'] = dict() for systemd_include_pattern in systemd_include_patterns: @@ -101,12 +101,11 @@ def __init__(self, *args, **kwargs): self.systemd['includes'][systemd_include_pattern] = file break else: - self.systemd['pcs'][systemd_pc_pattern] = None + self.systemd['includes'][systemd_include_pattern] = None # Check pkgconfig paths sys_pc_dirs = get_sys_pc_dirs() - print(sys_pc_dirs) - self.log.debug("Found the following pkgconfig directories in host system: %s", ', '.join(sys_pc_dirs)) + self.log.info("Found the following pkgconfig directories in host system: %s", ', '.join(sys_pc_dirs)) self.systemd['pcs'] = dict() for systemd_pc_pattern in systemd_pc_patterns: From e0c9f481d3a8a05c71b1bd6b2a7fdcf71652bcd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=96hman?= Date: Mon, 20 Oct 2025 14:37:09 +0000 Subject: [PATCH 6/6] fix sanity check step --- easybuild/easyblocks/s/systemd_wrapper.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/s/systemd_wrapper.py b/easybuild/easyblocks/s/systemd_wrapper.py index 866893fb5e..3e1859c18e 100644 --- a/easybuild/easyblocks/s/systemd_wrapper.py +++ b/easybuild/easyblocks/s/systemd_wrapper.py @@ -30,7 +30,7 @@ import os import re -from easybuild.easyblocks.generic.bundle import Bundle +from easybuild.framework.easyblock import EasyBlock from easybuild.tools.build_log import EasyBuildError from easybuild.tools.filetools import mkdir, symlink from easybuild.tools.run import run_shell_cmd @@ -59,7 +59,7 @@ def get_sys_pc_dirs(): return res.output.strip().split(':') -class EB_systemd_wrapper(Bundle): +class EB_systemd_wrapper(EasyBlock): """ Find path to installation files of systemd and udev in the host system. """ @@ -159,8 +159,8 @@ def sanity_check_step(self): """Custom sanity check for systemd wrapper.""" shlib_ext = get_shared_lib_ext() custom_paths = { - 'files': [f'lib/libsystemd.{shlib_ext}', f'libudev.{shlib_ext}', 'include/udev.h', - 'share/pkgconfig/libsystemd.pc', 'share/pkgconfig/libudev.pc'], + 'files': [f'lib/libsystemd.{shlib_ext}', f'lib/libudev.{shlib_ext}', 'include/libudev.h', + 'share/pkgconfig/systemd.pc', 'share/pkgconfig/udev.pc'], 'dirs': ['include/systemd'], } return super().sanity_check_step(custom_paths=custom_paths, custom_commands=[])