Skip to content

Commit 0bb5724

Browse files
authored
Merge pull request #4100 from easybuilders/4.6.x
release EasyBuild 4.6.2
2 parents 0a506aa + a981b30 commit 0bb5724

33 files changed

+439
-134
lines changed

.github/workflows/bootstrap_script.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,10 @@ jobs:
5454
lc_all: C
5555
fail-fast: false
5656
steps:
57-
- uses: actions/checkout@v2
57+
- uses: actions/checkout@v3
5858

5959
- name: set up Python
60-
uses: actions/setup-python@v2
60+
uses: actions/setup-python@v3
6161
with:
6262
python-version: ${{matrix.python}}
6363
architecture: x64

.github/workflows/container_tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ jobs:
1010
python: [2.7, 3.6]
1111
fail-fast: false
1212
steps:
13-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v3
1414

1515
- name: set up Python
16-
uses: actions/setup-python@v2
16+
uses: actions/setup-python@v3
1717
with:
1818
python-version: ${{matrix.python}}
1919
architecture: x64

.github/workflows/eb_command.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ jobs:
99
python: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, '3.10']
1010
fail-fast: false
1111
steps:
12-
- uses: actions/checkout@v2
12+
- uses: actions/checkout@v3
1313

1414
- name: set up Python
15-
uses: actions/setup-python@v2
15+
uses: actions/setup-python@v3
1616
with:
1717
python-version: ${{matrix.python}}
1818
architecture: x64
@@ -77,6 +77,10 @@ jobs:
7777
echo "Looking for pattern \"${pattern}\" in eb_version.out..."
7878
grep "$pattern" eb_version.out
7979
done
80+
if grep -q "Considering ''" eb_version.out; then
81+
echo '`eb` did wrongly consider an empty command'
82+
false
83+
fi
8084
# also check when specifying Python command via $EB_PYTHON
8185
for eb_python in "python${pymajver}" "python${pymajminver}"; do
8286
export EB_PYTHON="${eb_python}"

.github/workflows/linting.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ jobs:
88
python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, '3.10']
99

1010
steps:
11-
- uses: actions/checkout@v2
11+
- uses: actions/checkout@v3
1212

1313
- name: set up Python
14-
uses: actions/setup-python@v2
14+
uses: actions/setup-python@v3
1515
with:
1616
python-version: ${{ matrix.python-version }}
1717

.github/workflows/unit_tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ jobs:
7676
lc_all: C
7777
fail-fast: false
7878
steps:
79-
- uses: actions/checkout@v2
79+
- uses: actions/checkout@v3
8080

8181
- name: set up Python
82-
uses: actions/setup-python@v2
82+
uses: actions/setup-python@v3
8383
with:
8484
python-version: ${{matrix.python}}
8585
architecture: x64

RELEASE_NOTES

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,29 @@ 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.6.2 (October 21st 2022)
8+
--------------------------
9+
10+
update/bugfix release
11+
12+
- various enhancements, including:
13+
- add support for easystack file that contains easyconfig filenames + implement parsing of configuration options (#4021)
14+
- skip over unset $EB_PYTHON/$EB_INSTALLPYTHON in eb wrappeer script (#4080)
15+
- add GITHUB_RELEASE and GITHUB_LOWER_RELEASE templates (#4084)
16+
- add `%(cuda_cc_cmake)s` template (#4087)
17+
- various bug fixes, including:
18+
- make check_sha256_checksums verify all checksums if they're specified as a dict value (#4076)
19+
- replace use of symlink with copied files in `alt_location` tests to fix failing EasyBuild installation on BeeGFS (#4083)
20+
- fix trying to generate RPATH wrappers for Clang (#4088)
21+
- make sure that GitPython version is a proper version before checking minimal required version (#4090, #4091)
22+
- first look for patch in alt_location when it is specified (#4093)
23+
- other changes:
24+
- make scripts executable (#4081)
25+
- make --inject-checksums inject dictionary value for checksums which maps filename to SHA256 checksum (#4085)
26+
- update to v3 of actions/checkout and actions/setup-python in CI workflows (#4089)
27+
- use SYSTEM template constant in dependencies instead of True in framework tests (#4094)
28+
29+
730
v4.6.1 (September 12th 2022)
831
----------------------------
932

easybuild/framework/easyblock.py

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -753,8 +753,15 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No
753753
foundfile = None
754754
failedpaths = []
755755

756-
# always look first in the dir of the current eb file
757-
ebpath = [os.path.dirname(self.cfg.path)]
756+
# always look first in the dir of the current eb file unless alt_location is set
757+
if alt_location is None:
758+
ebpath = [os.path.dirname(self.cfg.path)]
759+
self.log.info("Considering directory in which easyconfig file is located when searching for %s: %s",
760+
filename, ebpath[0])
761+
else:
762+
ebpath = []
763+
self.log.info("Not considering directory in which easyconfig file is located when searching for %s "
764+
"because alt_location is set to %s", filename, alt_location)
758765

759766
# always consider robot + easyconfigs install paths as a fall back (e.g. for patch files, test cases, ...)
760767
common_filepaths = []
@@ -2375,33 +2382,34 @@ def check_checksums_for(self, ent, sub='', source_cnt=None):
23752382
checksum_issues.append(msg)
23762383

23772384
for fn, checksum in zip(sources + patches, checksums):
2385+
2386+
# a checksum may be specified as a dictionary which maps filename to actual checksum
2387+
# for example when different source files are used for different CPU architectures
23782388
if isinstance(checksum, dict):
2379-
# sources entry may be a dictionary rather than just a string value with filename
2380-
if isinstance(fn, dict):
2381-
filename = fn['filename']
2382-
else:
2383-
filename = fn
2384-
checksum = checksum.get(filename)
2385-
2386-
# take into account that we may encounter a tuple of valid SHA256 checksums
2387-
# (see https://github.com/easybuilders/easybuild-framework/pull/2958)
2388-
if isinstance(checksum, tuple):
2389-
# 1st tuple item may indicate checksum type, must be SHA256 or else it's blatently ignored here
2390-
if len(checksum) == 2 and checksum[0] == CHECKSUM_TYPE_SHA256:
2391-
valid_checksums = (checksum[1],)
2392-
else:
2393-
valid_checksums = checksum
2389+
checksums_to_check = checksum.values()
23942390
else:
2395-
valid_checksums = (checksum,)
2396-
2397-
non_sha256_checksums = [c for c in valid_checksums if not is_sha256_checksum(c)]
2398-
if non_sha256_checksums:
2399-
if all(c is None for c in non_sha256_checksums):
2400-
print_warning("Found %d None checksum value(s), please make sure this is intended!" %
2401-
len(non_sha256_checksums))
2391+
checksums_to_check = [checksum]
2392+
2393+
for checksum in checksums_to_check:
2394+
# take into account that we may encounter a tuple of valid SHA256 checksums
2395+
# (see https://github.com/easybuilders/easybuild-framework/pull/2958)
2396+
if isinstance(checksum, tuple):
2397+
# 1st tuple item may indicate checksum type, must be SHA256 or else it's blatently ignored here
2398+
if len(checksum) == 2 and checksum[0] == CHECKSUM_TYPE_SHA256:
2399+
valid_checksums = (checksum[1],)
2400+
else:
2401+
valid_checksums = checksum
24022402
else:
2403-
msg = "Non-SHA256 checksum(s) found for %s: %s" % (fn, valid_checksums)
2404-
checksum_issues.append(msg)
2403+
valid_checksums = (checksum,)
2404+
2405+
non_sha256_checksums = [c for c in valid_checksums if not is_sha256_checksum(c)]
2406+
if non_sha256_checksums:
2407+
if all(c is None for c in non_sha256_checksums):
2408+
print_warning("Found %d None checksum value(s), please make sure this is intended!" %
2409+
len(non_sha256_checksums))
2410+
else:
2411+
msg = "Non-SHA256 checksum(s) found for %s: %s" % (fn, valid_checksums)
2412+
checksum_issues.append(msg)
24052413

24062414
return checksum_issues
24072415

@@ -4415,11 +4423,11 @@ def make_checksum_lines(checksums, indent_level):
44154423
line_indent = INDENT_4SPACES * indent_level
44164424
checksum_lines = []
44174425
for fn, checksum in checksums:
4418-
checksum_line = "%s'%s', # %s" % (line_indent, checksum, fn)
4426+
checksum_line = "%s{'%s': '%s'}," % (line_indent, fn, checksum)
44194427
if len(checksum_line) > MAX_LINE_LENGTH:
44204428
checksum_lines.extend([
4421-
"%s# %s" % (line_indent, fn),
4422-
"%s'%s'," % (line_indent, checksum),
4429+
"%s{'%s':" % (line_indent, fn),
4430+
"%s '%s'}," % (line_indent, checksum),
44234431
])
44244432
else:
44254433
checksum_lines.append(checksum_line)

easybuild/framework/easyconfig/templates.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
('mpi_cmd_prefix', "Prefix command for running MPI programs (with default number of ranks)"),
9191
('cuda_compute_capabilities', "Comma-separated list of CUDA compute capabilities, as specified via "
9292
"--cuda-compute-capabilities configuration option or via cuda_compute_capabilities easyconfig parameter"),
93+
('cuda_cc_cmake', "List of CUDA compute capabilities suitable for use with $CUDAARCHS in CMake 3.18+"),
9394
('cuda_cc_space_sep', "Space-separated list of CUDA compute capabilities"),
9495
('cuda_cc_semicolon_sep', "Semicolon-separated list of CUDA compute capabilities"),
9596
('cuda_sm_comma_sep', "Comma-separated list of sm_* values that correspond with CUDA compute capabilities"),
@@ -113,6 +114,10 @@
113114
'GitHub source URL (namelower is used if github_account easyconfig parameter is not specified)'),
114115
('GITHUB_LOWER_SOURCE', 'https://github.com/%(github_account)s/%(namelower)s/archive',
115116
'GitHub source URL (lowercase name, namelower is used if github_account easyconfig parameter is not specified)'),
117+
('GITHUB_RELEASE', 'https://github.com/%(github_account)s/%(name)s/releases/download/v%(version)s',
118+
'GitHub release URL (namelower is used if github_account easyconfig parameter is not specified)'),
119+
('GITHUB_LOWER_RELEASE', 'https://github.com/%(github_account)s/%(namelower)s/releases/download/v%(version)s',
120+
'GitHub release URL (namelower is used if github_account easyconfig parameter is not specified)'),
116121
('GNU_SAVANNAH_SOURCE', 'https://download-mirror.savannah.gnu.org/releases/%(namelower)s',
117122
'download.savannah.gnu.org source url'),
118123
('GNU_SOURCE', 'https://ftpmirror.gnu.org/gnu/%(namelower)s',
@@ -353,6 +358,7 @@ def template_constant_dict(config, ignore=None, skip_lower=None, toolchain=None)
353358
template_values['cuda_compute_capabilities'] = ','.join(cuda_compute_capabilities)
354359
template_values['cuda_cc_space_sep'] = ' '.join(cuda_compute_capabilities)
355360
template_values['cuda_cc_semicolon_sep'] = ';'.join(cuda_compute_capabilities)
361+
template_values['cuda_cc_cmake'] = ';'.join(cc.replace('.', '') for cc in cuda_compute_capabilities)
356362
sm_values = ['sm_' + cc.replace('.', '') for cc in cuda_compute_capabilities]
357363
template_values['cuda_sm_comma_sep'] = ','.join(sm_values)
358364
template_values['cuda_sm_space_sep'] = ' '.join(sm_values)

easybuild/framework/easystack.py

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
pass
4242
_log = fancylogger.getLogger('easystack', fname=False)
4343

44+
EASYSTACK_DOC_URL = 'https://docs.easybuild.io/en/latest/Easystack-files.html'
45+
4446

4547
def check_value(value, context):
4648
"""
@@ -68,10 +70,16 @@ def __init__(self):
6870
self.easybuild_version = None
6971
self.robot = False
7072
self.software_list = []
73+
self.easyconfigs = [] # A list of easyconfig names. May or may not include .eb extension
74+
# A dict where keys are easyconfig names, values are dictionary of options that should be
75+
# applied for that easyconfig
76+
self.ec_opts = {}
7177

7278
def compose_ec_filenames(self):
7379
"""Returns a list of all easyconfig names"""
7480
ec_filenames = []
81+
82+
# entries specified via 'software' top-level key
7583
for sw in self.software_list:
7684
full_ec_version = det_full_ec_version({
7785
'toolchain': {'name': sw.toolchain_name, 'version': sw.toolchain_version},
@@ -80,6 +88,10 @@ def compose_ec_filenames(self):
8088
})
8189
ec_filename = '%s-%s.eb' % (sw.name, full_ec_version)
8290
ec_filenames.append(ec_filename)
91+
92+
# entries specified via 'easyconfigs' top-level key
93+
for ec in self.easyconfigs:
94+
ec_filenames.append(ec)
8395
return ec_filenames
8496

8597
# flags applicable to all sw (i.e. robot)
@@ -108,21 +120,89 @@ class EasyStackParser(object):
108120

109121
@staticmethod
110122
def parse(filepath):
111-
"""Parses YAML file and assigns obtained values to SW config instances as well as general config instance"""
123+
"""
124+
Parses YAML file and assigns obtained values to SW config instances as well as general config instance"""
112125
yaml_txt = read_file(filepath)
113126

114127
try:
115128
easystack_raw = yaml.safe_load(yaml_txt)
116129
except yaml.YAMLError as err:
117130
raise EasyBuildError("Failed to parse %s: %s" % (filepath, err))
118131

132+
easystack_data = None
133+
top_keys = ('easyconfigs', 'software')
134+
keys_found = []
135+
for key in top_keys:
136+
if key in easystack_raw:
137+
keys_found.append(key)
138+
# For now, we don't support mixing multiple top_keys, so check that only one was defined
139+
if len(keys_found) > 1:
140+
keys_string = ', '.join(keys_found)
141+
msg = "Specifying multiple top level keys (%s) " % keys_string
142+
msg += "in one EasyStack file is currently not supported"
143+
msg += ", see %s for documentation." % EASYSTACK_DOC_URL
144+
raise EasyBuildError(msg)
145+
elif len(keys_found) == 0:
146+
msg = "Not a valid EasyStack YAML file: no 'easyconfigs' or 'software' top-level key found"
147+
msg += ", see %s for documentation." % EASYSTACK_DOC_URL
148+
raise EasyBuildError(msg)
149+
else:
150+
key = keys_found[0]
151+
easystack_data = easystack_raw[key]
152+
153+
parse_method_name = 'parse_by_' + key
154+
parse_method = getattr(EasyStackParser, 'parse_by_%s' % key, None)
155+
if parse_method is None:
156+
raise EasyBuildError("Easystack parse method '%s' not found!", parse_method_name)
157+
158+
# assign general easystack attributes
159+
easybuild_version = easystack_raw.get('easybuild_version', None)
160+
robot = easystack_raw.get('robot', False)
161+
162+
return parse_method(filepath, easystack_data, easybuild_version=easybuild_version, robot=robot)
163+
164+
@staticmethod
165+
def parse_by_easyconfigs(filepath, easyconfigs, easybuild_version=None, robot=False):
166+
"""
167+
Parse easystack file with 'easyconfigs' as top-level key.
168+
"""
169+
119170
easystack = EasyStack()
120171

121-
try:
122-
software = easystack_raw["software"]
123-
except KeyError:
124-
wrong_structure_file = "Not a valid EasyStack YAML file: no 'software' key found"
125-
raise EasyBuildError(wrong_structure_file)
172+
for easyconfig in easyconfigs:
173+
if isinstance(easyconfig, str):
174+
if not easyconfig.endswith('.eb'):
175+
easyconfig = easyconfig + '.eb'
176+
easystack.easyconfigs.append(easyconfig)
177+
elif isinstance(easyconfig, dict):
178+
if len(easyconfig) == 1:
179+
# Get single key from dictionary 'easyconfig'
180+
easyconf_name = list(easyconfig.keys())[0]
181+
# Add easyconfig name to the list
182+
if not easyconf_name.endswith('.eb'):
183+
easyconf_name_with_eb = easyconf_name + '.eb'
184+
else:
185+
easyconf_name_with_eb = easyconf_name
186+
easystack.easyconfigs.append(easyconf_name_with_eb)
187+
# Add options to the ec_opts dict
188+
if 'options' in easyconfig[easyconf_name].keys():
189+
easystack.ec_opts[easyconf_name_with_eb] = easyconfig[easyconf_name]['options']
190+
else:
191+
dict_keys = ', '.join(easyconfig.keys())
192+
msg = "Failed to parse easystack file: expected a dictionary with one key (the EasyConfig name), "
193+
msg += "instead found keys: %s" % dict_keys
194+
msg += ", see %s for documentation." % EASYSTACK_DOC_URL
195+
raise EasyBuildError(msg)
196+
197+
return easystack
198+
199+
@staticmethod
200+
def parse_by_software(filepath, software, easybuild_version=None, robot=False):
201+
"""
202+
Parse easystack file with 'software' as top-level key.
203+
"""
204+
205+
easystack = EasyStack()
126206

127207
# assign software-specific easystack attributes
128208
for name in software:
@@ -224,8 +304,8 @@ def parse(filepath):
224304
easystack.software_list.append(sw)
225305

226306
# assign general easystack attributes
227-
easystack.easybuild_version = easystack_raw.get('easybuild_version', None)
228-
easystack.robot = easystack_raw.get('robot', False)
307+
easystack.easybuild_version = easybuild_version
308+
easystack.robot = robot
229309

230310
return easystack
231311

@@ -243,12 +323,16 @@ def parse_easystack(filepath):
243323

244324
easyconfig_names = easystack.compose_ec_filenames()
245325

246-
general_options = easystack.get_general_options()
326+
# Disabled general options for now. We weren't using them, and first want support for EasyConfig-specific options.
327+
# Then, we need a method to resolve conflicts (specific options should win)
328+
# general_options = easystack.get_general_options()
247329

248330
_log.debug("EasyStack parsed. Proceeding to install these Easyconfigs: %s" % ', '.join(sorted(easyconfig_names)))
249-
if len(general_options) != 0:
250-
_log.debug("General options for installation are: \n%s" % str(general_options))
251-
else:
252-
_log.debug("No general options were specified in easystack")
253-
254-
return easyconfig_names, general_options
331+
_log.debug("Using EasyConfig specific options based on the following dict:")
332+
_log.debug(easystack.ec_opts)
333+
# if len(general_options) != 0:
334+
# _log.debug("General options for installation are: \n%s" % str(general_options))
335+
# else:
336+
# _log.debug("No general options were specified in easystack")
337+
338+
return easyconfig_names, easystack.ec_opts

0 commit comments

Comments
 (0)