Skip to content

Commit c4f66c0

Browse files
authored
Merge pull request #4639 from boegel/5.0.x
sync with develop (20240915)
2 parents 56db19c + 405a3ec commit c4f66c0

File tree

14 files changed

+162
-44
lines changed

14 files changed

+162
-44
lines changed

RELEASE_NOTES

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,43 @@ For more detailed information, please see the git log.
44
These release notes can also be consulted at https://easybuild.readthedocs.io/en/latest/Release_notes.html.
55

66

7+
v4.9.3 (14 September 2024)
8+
--------------------------
9+
10+
update/bugfix release
11+
12+
- various enhancements, including:
13+
- add support for `--extra-source-urls` to fetch sources from additional URLs (#4079)
14+
- add definition for gmpflf toolchain (#4566, #4571)
15+
- reuse pre-computed checksums (#4569)
16+
- add `cuda_cc_space_sep` variant that does not have periods (#4583)
17+
- add `--skip-sanity-check` option (#4590)
18+
- add `GNU_FTP_SOURCE` template constant (#4597)
19+
- improve error messages for empty easyconfigs (#4603)
20+
- improve help string for `--dep-graph` (#4610)
21+
- only call `_sanity_check_step_extensions` if `--skip-extensions` is not set (#4620)
22+
- add support for `--software-commit` and an associated template `%(software_commit)s` (#4628)
23+
- various bug fixes, including:
24+
- correctly evaluate result for `--dep-graph` (#4554)
25+
- fix fetch progress bar showing to many files (#4568)
26+
- resolve internal for imkl>=2021 version subdir via "latest" symlink (#4570)
27+
- fix typo in message about including an easyblock from a commit (#4575)
28+
- don't use special flags for `strict`, `precise`, `loose`, `veryloose` toolchain options on RISC-V (#4576)
29+
- fix help text for `cuda_compute_capabilities` template (#4589)
30+
- fix help message for `--http-headers-fields-urlpat` configuration option (#4594)
31+
- fix `test_compiler_cache` in case `gcc` is available multiple times (#4599)
32+
- handle post-install patches in check_checksums_for (#4605)
33+
- fix `copy_file` with a folder as the target (#4609)
34+
- allow for case where `homepage = None` when generating the docs (#4626)
35+
- fix test_github_det_commit_status by using more recent commits (#4636)
36+
- other changes:
37+
- clean up code that was only there to support Python 2.6 + avoid syntax warnings when parsing py2vs3/py.p2 with Python 3.x (#3788)
38+
- use Intel's oneAPI Fortran compiler by default for version 2024.0.0 and newer (`oneapi_fortran` toolchain option set to `True`) (#4567)
39+
- allow using Node 16 actions in CI (#4574)
40+
- remove a superflous check in `EasyBlock.run_all_steps` (#4623)
41+
- remove trailing dots from backup message produced by --inject-checksums (#4632)
42+
43+
744
v4.9.2 (12 June 2024)
845
---------------------
946

easybuild/base/generaloption.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,16 @@ def what_str_list_tuple(name):
100100
"""Given name, return separator, class and helptext wrt separator.
101101
(Currently supports strlist, strtuple, pathlist, pathtuple)
102102
"""
103-
sep = ','
104-
helpsep = 'comma'
105103
if name.startswith('path'):
106104
sep = os.pathsep
107105
helpsep = 'pathsep'
106+
elif name.startswith('url'):
107+
# | is one of the only characters not in the grammar for URIs (RFC3986)
108+
sep = '|'
109+
helpsep = '|'
110+
else:
111+
sep = ','
112+
helpsep = 'comma'
108113

109114
klass = None
110115
if name.endswith('list'):
@@ -185,6 +190,7 @@ class ExtOption(CompleterOption):
185190
- strlist, strtuple : convert comma-separated string in a list resp. tuple of strings
186191
- pathlist, pathtuple : using os.pathsep, convert pathsep-separated string in a list resp. tuple of strings
187192
- the path separator is OS-dependent
193+
- urllist, urltuple: convert string seperated by '|' to a list resp. tuple of strings
188194
"""
189195
EXTEND_SEPARATOR = ','
190196

@@ -201,7 +207,7 @@ class ExtOption(CompleterOption):
201207
TYPED_ACTIONS = Option.TYPED_ACTIONS + EXTOPTION_EXTRA_OPTIONS + EXTOPTION_STORE_OR
202208
ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + EXTOPTION_EXTRA_OPTIONS
203209

204-
TYPE_STRLIST = ['%s%s' % (name, klass) for klass in ['list', 'tuple'] for name in ['str', 'path']]
210+
TYPE_STRLIST = ['%s%s' % (name, klass) for klass in ['list', 'tuple'] for name in ['str', 'path', 'url']]
205211
TYPE_CHECKER = {x: check_str_list_tuple for x in TYPE_STRLIST}
206212
TYPE_CHECKER.update(Option.TYPE_CHECKER)
207213
TYPES = tuple(TYPE_STRLIST + list(Option.TYPES))

easybuild/framework/easyblock.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
from easybuild.tools.build_log import print_error, print_msg, print_warning
7575
from easybuild.tools.config import CHECKSUM_PRIORITY_JSON, DEFAULT_ENVVAR_USERS_MODULES
7676
from easybuild.tools.config import FORCE_DOWNLOAD_ALL, FORCE_DOWNLOAD_PATCHES, FORCE_DOWNLOAD_SOURCES
77+
from easybuild.tools.config import EASYBUILD_SOURCES_URL # noqa
7778
from easybuild.tools.config import build_option, build_path, get_log_filename, get_repository, get_repositorypath
7879
from easybuild.tools.config import install_path, log_path, package_path, source_paths
7980
from easybuild.tools.environment import restore_env, sanitize_env
@@ -106,9 +107,6 @@
106107
from easybuild.tools.utilities import remove_unwanted_chars, time2str, trace_msg
107108
from easybuild.tools.version import this_is_easybuild, VERBOSE_VERSION, VERSION
108109

109-
110-
EASYBUILD_SOURCES_URL = 'https://sources.easybuild.io'
111-
112110
DEFAULT_BIN_LIB_SUBDIRS = ('bin', 'lib', 'lib64')
113111

114112
MODULE_ONLY_STEPS = [MODULE_STEP, PREPARE_STEP, READY_STEP, POSTITER_STEP, SANITYCHECK_STEP]
@@ -887,8 +885,10 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No
887885
source_urls = []
888886
source_urls.extend(self.cfg['source_urls'])
889887

890-
# add https://sources.easybuild.io as fallback source URL
891-
source_urls.append(EASYBUILD_SOURCES_URL + '/' + os.path.join(name_letter, location))
888+
# Add additional URLs as configured.
889+
for url in build_option("extra_source_urls"):
890+
url += "/" + name_letter + "/" + location
891+
source_urls.append(url)
892892

893893
mkdir(targetdir, parents=True)
894894

@@ -2482,7 +2482,7 @@ def check_checksums_for(self, ent, sub='', source_cnt=None):
24822482
checksum_issues = []
24832483

24842484
sources = ent.get('sources', [])
2485-
patches = ent.get('patches', [])
2485+
patches = ent.get('patches', []) + ent.get('postinstallpatches', [])
24862486
checksums = ent.get('checksums', [])
24872487
# Single source should be re-wrapped as a list, and checksums with it
24882488
if isinstance(sources, dict):
@@ -3503,10 +3503,7 @@ def _sanity_check_step_extensions(self):
35033503
"""Sanity check on extensions (if any)."""
35043504
failed_exts = []
35053505

3506-
if build_option('skip_extensions'):
3507-
self.log.info("Skipping sanity check for extensions since skip-extensions is enabled...")
3508-
return
3509-
elif not self.ext_instances:
3506+
if not self.ext_instances:
35103507
# class instances for extensions may not be initialized yet here,
35113508
# for example when using --module-only or --sanity-check-only
35123509
self.prepare_for_extensions()
@@ -3659,7 +3656,10 @@ def xs2str(xs):
36593656

36603657
# also run sanity check for extensions (unless we are an extension ourselves)
36613658
if not extension:
3662-
self._sanity_check_step_extensions()
3659+
if build_option('skip_extensions'):
3660+
self.log.info("Skipping sanity check for extensions since skip-extensions is enabled...")
3661+
else:
3662+
self._sanity_check_step_extensions()
36633663

36643664
linked_shared_lib_fails = self.sanity_check_linked_shared_libs()
36653665
if linked_shared_lib_fails:
@@ -3926,12 +3926,13 @@ def update_config_template_run_step(self):
39263926
self.cfg.generate_template_values()
39273927

39283928
def skip_step(self, step, skippable):
3929-
"""Dedice whether or not to skip the specified step."""
3929+
"""Decide whether or not to skip the specified step."""
39303930
skip = False
39313931
force = build_option('force')
39323932
module_only = build_option('module_only') or self.cfg['module_only']
39333933
sanity_check_only = build_option('sanity_check_only')
39343934
skip_extensions = build_option('skip_extensions')
3935+
skip_sanity_check = build_option('skip_sanity_check')
39353936
skip_test_step = build_option('skip_test_step')
39363937
skipsteps = self.cfg['skipsteps']
39373938

@@ -3959,6 +3960,10 @@ def skip_step(self, step, skippable):
39593960
self.log.info("Skipping %s step because of sanity-check-only mode", step)
39603961
skip = True
39613962

3963+
elif skip_sanity_check and step == SANITYCHECK_STEP:
3964+
self.log.info("Skipping %s step as request via skip-sanity-check", step)
3965+
skip = True
3966+
39623967
elif skip_extensions and step == EXTENSIONS_STEP:
39633968
self.log.info("Skipping %s step as requested via skip-extensions", step)
39643969
skip = True
@@ -3969,9 +3974,9 @@ def skip_step(self, step, skippable):
39693974

39703975
else:
39713976
msg = "Not skipping %s step (skippable: %s, skip: %s, skipsteps: %s, module_only: %s, force: %s, "
3972-
msg += "sanity_check_only: %s, skip_extensions: %s, skip_test_step: %s)"
3977+
msg += "sanity_check_only: %s, skip_extensions: %s, skip_test_step: %s, skip_sanity_check: %s)"
39733978
self.log.debug(msg, step, skippable, self.skip, skipsteps, module_only, force,
3974-
sanity_check_only, skip_extensions, skip_test_step)
3979+
sanity_check_only, skip_extensions, skip_test_step, skip_sanity_check)
39753980

39763981
return skip
39773982

@@ -4113,7 +4118,7 @@ def run_all_steps(self, run_test_cases):
41134118
Build and install this software.
41144119
run_test_cases (bool): run tests after building (e.g.: make test)
41154120
"""
4116-
if self.cfg['stop'] and self.cfg['stop'] == 'cfg':
4121+
if self.cfg['stop'] == 'cfg':
41174122
return True
41184123

41194124
steps = self.get_steps(run_test_cases=run_test_cases, iteration_count=self.det_iter_cnt())
@@ -4725,7 +4730,7 @@ def make_checksum_lines(checksums, indent_level):
47254730

47264731
# back up easyconfig file before injecting checksums
47274732
ec_backup = back_up_file(ec['spec'])
4728-
print_msg("backup of easyconfig file saved to %s..." % ec_backup, log=_log)
4733+
print_msg("backup of easyconfig file saved to %s" % ec_backup, log=_log)
47294734

47304735
# compute & inject checksums for sources/patches
47314736
print_msg("injecting %s checksums for sources & patches in %s..." % (checksum_type, ec_fn), log=_log)

easybuild/framework/easyconfig/easyconfig.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,8 @@ def __init__(self, path, extra_options=None, build_specs=None, validate=True, hi
441441
self.path = path
442442
self.rawtxt = read_file(path)
443443
self.log.debug("Raw contents from supplied easyconfig file %s: %s", path, self.rawtxt)
444+
if not self.rawtxt.strip():
445+
raise EasyBuildError('Easyconfig file is empty')
444446
else:
445447
self.rawtxt = rawtxt
446448
self.log.debug("Supplied raw easyconfig contents: %s" % self.rawtxt)
@@ -1884,7 +1886,9 @@ def get_easyblock_class(easyblock, name=None, error_on_failed_import=True, error
18841886
else:
18851887
# if no easyblock specified, try to find if one exists
18861888
if name is None:
1887-
name = "UNKNOWN"
1889+
if error_on_missing_easyblock:
1890+
raise EasyBuildError("No easyblock found as neither name nor easyblock were specified")
1891+
return None
18881892
# The following is a generic way to calculate unique class names for any funny software title
18891893
class_name = encode_class_name(name)
18901894
# modulepath will be the namespace + encoded modulename (from the classname)

easybuild/main.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -723,9 +723,11 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None, pr
723723
if options.ignore_test_failure:
724724
raise EasyBuildError("Found both ignore-test-failure and skip-test-step enabled. "
725725
"Please use only one of them.")
726-
else:
727-
print_warning("Will not run the test step as requested via skip-test-step. "
728-
"Consider using ignore-test-failure instead and verify the results afterwards")
726+
print_warning("Will not run the test step as requested via skip-test-step. "
727+
"Consider using ignore-test-failure instead and verify the results afterwards")
728+
if options.skip_sanity_check and options.sanity_check_only:
729+
raise EasyBuildError("Found both skip-sanity-check and sanity-check-only enabled. "
730+
"Please use only one of them.")
729731

730732
# if EasyStack file is provided, parse it, and loop over the items in the EasyStack file
731733
if options.easystack:

easybuild/tools/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@
121121
DEFAULT_PR_TARGET_ACCOUNT = 'easybuilders'
122122
DEFAULT_PREFIX = os.path.join(os.path.expanduser('~'), ".local", "easybuild")
123123
DEFAULT_REPOSITORY = 'FileRepository'
124+
EASYBUILD_SOURCES_URL = 'https://sources.easybuild.io'
125+
DEFAULT_EXTRA_SOURCE_URLS = (EASYBUILD_SOURCES_URL,)
124126
# Filter these CUDA libraries by default from the RPATH sanity check.
125127
# These are the only four libraries for which the CUDA toolkit ships stubs. By design, one is supposed to build
126128
# against the stub versions, but use the libraries that come with the CUDA driver at runtime. That means they should
@@ -307,6 +309,7 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
307309
'set_gid_bit',
308310
'silence_hook_trigger',
309311
'skip_extensions',
312+
'skip_sanity_check',
310313
'skip_test_cases',
311314
'skip_test_step',
312315
'sticky_bit',
@@ -392,6 +395,9 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
392395
'defaultopt': [
393396
'default_opt_level',
394397
],
398+
DEFAULT_EXTRA_SOURCE_URLS: [
399+
'extra_source_urls',
400+
],
395401
DEFAULT_ALLOW_LOADED_MODULES: [
396402
'allow_loaded_modules',
397403
],

easybuild/tools/docs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ def list_software(output_format=FORMAT_TXT, detailed=False, only_installed=False
805805
if isinstance(ec, dict):
806806
template_values = template_constant_dict(ec)
807807
for key in keys:
808-
if '%(' in info[key]:
808+
if info[key] and '%(' in info[key]:
809809
try:
810810
info[key] = info[key] % template_values
811811
except (KeyError, TypeError, ValueError) as err:

easybuild/tools/filetools.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,12 +2394,16 @@ def copy_file(path, target_path, force_in_dry_run=False):
23942394
try:
23952395
# check whether path to copy exists (we could be copying a broken symlink, which is supported)
23962396
path_exists = os.path.exists(path)
2397+
# If target is a folder, the target_path will be a file with the same name inside the folder
2398+
if os.path.isdir(target_path):
2399+
target_path = os.path.join(target_path, os.path.basename(path))
23972400
target_exists = os.path.exists(target_path)
2401+
23982402
if target_exists and path_exists and os.path.samefile(path, target_path):
23992403
_log.debug("Not copying %s to %s since files are identical", path, target_path)
24002404
# if target file exists and is owned by someone else than the current user,
2401-
# try using shutil.copyfile to just copy the file contents
2402-
# since shutil.copy2 will fail when trying to copy over file metadata (since chown requires file ownership)
2405+
# copy just the file contents (shutil.copyfile instead of shutil.copy2)
2406+
# since copying the file metadata/permissions will fail since chown requires file ownership
24032407
elif target_exists and os.stat(target_path).st_uid != os.getuid():
24042408
shutil.copyfile(path, target_path)
24052409
_log.info("Copied contents of file %s to %s", path, target_path)

0 commit comments

Comments
 (0)