Skip to content

Commit 75a5532

Browse files
committed
add support for easystack file that contains easyconfig filenames
1 parent 8f014a8 commit 75a5532

File tree

3 files changed

+99
-16
lines changed

3 files changed

+99
-16
lines changed

easybuild/framework/easystack.py

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,19 @@ def check_value(value, context):
6464
class EasyStack(object):
6565
"""One class instance per easystack. General options + list of all SoftwareSpecs instances"""
6666

67-
def __init__(self):
67+
def __init__(self, easyconfigs=None):
6868
self.easybuild_version = None
6969
self.robot = False
7070
self.software_list = []
71+
self.easyconfigs = []
72+
if easyconfigs is not None:
73+
self.easyconfigs.extend(easyconfigs)
7174

7275
def compose_ec_filenames(self):
7376
"""Returns a list of all easyconfig names"""
7477
ec_filenames = []
78+
79+
# entries specified via 'software' top-level key
7580
for sw in self.software_list:
7681
full_ec_version = det_full_ec_version({
7782
'toolchain': {'name': sw.toolchain_name, 'version': sw.toolchain_version},
@@ -80,6 +85,11 @@ def compose_ec_filenames(self):
8085
})
8186
ec_filename = '%s-%s.eb' % (sw.name, full_ec_version)
8287
ec_filenames.append(ec_filename)
88+
89+
# entries specified via 'easyconfigs' top-level key
90+
for ec in self.easyconfigs:
91+
ec_filenames.append(ec + '.eb')
92+
8393
return ec_filenames
8494

8595
# flags applicable to all sw (i.e. robot)
@@ -108,21 +118,53 @@ class EasyStackParser(object):
108118

109119
@staticmethod
110120
def parse(filepath):
111-
"""Parses YAML file and assigns obtained values to SW config instances as well as general config instance"""
121+
"""
122+
Parses YAML file and assigns obtained values to SW config instances as well as general config instance"""
112123
yaml_txt = read_file(filepath)
113124

114125
try:
115126
easystack_raw = yaml.safe_load(yaml_txt)
116127
except yaml.YAMLError as err:
117128
raise EasyBuildError("Failed to parse %s: %s" % (filepath, err))
118129

119-
easystack = EasyStack()
130+
easystack_data = None
131+
top_keys = ('easyconfigs', 'software')
132+
for key in top_keys:
133+
if key in easystack_raw:
134+
easystack_data = easystack_raw[key]
135+
break
120136

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)
137+
if easystack_data is None:
138+
msg = "Not a valid EasyStack YAML file: no 'easyconfigs' or 'software' top-level key found"
139+
raise EasyBuildError(msg)
140+
else:
141+
parse_method_name = 'parse_by_' + key
142+
parse_method = getattr(EasyStackParser, 'parse_by_%s' % key, None)
143+
if parse_method is None:
144+
raise EasyBuildError("Easystack parse method '%s' not found!", parse_method_name)
145+
146+
# assign general easystack attributes
147+
easybuild_version = easystack_raw.get('easybuild_version', None)
148+
robot = easystack_raw.get('robot', False)
149+
150+
return parse_method(filepath, easystack_data, easybuild_version=easybuild_version, robot=robot)
151+
152+
@staticmethod
153+
def parse_by_easyconfigs(filepath, easyconfigs, easybuild_version=None, robot=False):
154+
"""
155+
Parse easystack file with 'easyconfigs' as top-level key.
156+
"""
157+
easystack = EasyStack(easyconfigs=easyconfigs)
158+
159+
return easystack
160+
161+
@staticmethod
162+
def parse_by_software(filepath, software, easybuild_version=None, robot=False):
163+
"""
164+
Parse easystack file with 'software' as top-level key.
165+
"""
166+
167+
easystack = EasyStack()
126168

127169
# assign software-specific easystack attributes
128170
for name in software:
@@ -224,8 +266,8 @@ def parse(filepath):
224266
easystack.software_list.append(sw)
225267

226268
# assign general easystack attributes
227-
easystack.easybuild_version = easystack_raw.get('easybuild_version', None)
228-
easystack.robot = easystack_raw.get('robot', False)
269+
easystack.easybuild_version = easybuild_version
270+
easystack.robot = robot
229271

230272
return easystack
231273

test/framework/easystack.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,36 @@ def tearDown(self):
5656
easybuild.tools.build_log.EXPERIMENTAL = self.orig_experimental
5757
super(EasyStackTest, self).tearDown()
5858

59+
def test_easystack_basic(self):
60+
"""Test for basic easystack file."""
61+
topdir = os.path.dirname(os.path.abspath(__file__))
62+
test_easystack = os.path.join(topdir, 'easystacks', 'test_easystack_basic.yaml')
63+
64+
ec_fns, opts = parse_easystack(test_easystack)
65+
expected = [
66+
'binutils-2.25-GCCcore-4.9.3.eb',
67+
'binutils-2.26-GCCcore-4.9.3.eb',
68+
'foss-2018a.eb',
69+
'toy-0.0-gompi-2018a-test.eb',
70+
]
71+
self.assertEqual(sorted(ec_fns), sorted(expected))
72+
self.assertEqual(opts, {})
73+
74+
def test_easystack_easyconfigs(self):
75+
"""Test for easystack file using 'easyconfigs' key."""
76+
topdir = os.path.dirname(os.path.abspath(__file__))
77+
test_easystack = os.path.join(topdir, 'easystacks', 'test_easystack_easyconfigs.yaml')
78+
79+
ec_fns, opts = parse_easystack(test_easystack)
80+
expected = [
81+
'binutils-2.25-GCCcore-4.9.3.eb',
82+
'binutils-2.26-GCCcore-4.9.3.eb',
83+
'foss-2018a.eb',
84+
'toy-0.0-gompi-2018a-test.eb',
85+
]
86+
self.assertEqual(sorted(ec_fns), sorted(expected))
87+
self.assertEqual(opts, {})
88+
5989
def test_parse_fail(self):
6090
"""Test for clean error when easystack file fails to parse."""
6191
test_yml = os.path.join(self.test_prefix, 'test.yml')
@@ -120,15 +150,17 @@ def test_easystack_versions(self):
120150
versions = ('1.2.3', '1.2.30', '2021a', '1.2.3')
121151
for version in versions:
122152
write_file(test_easystack, tmpl_easystack_txt + ' ' + version)
123-
ec_fns, _ = parse_easystack(test_easystack)
153+
ec_fns, opts = parse_easystack(test_easystack)
124154
self.assertEqual(ec_fns, ['foo-%s.eb' % version])
155+
self.assertEqual(opts, {})
125156

126157
# multiple versions as a list
127158
test_easystack_txt = tmpl_easystack_txt + " [1.2.3, 3.2.1]"
128159
write_file(test_easystack, test_easystack_txt)
129-
ec_fns, _ = parse_easystack(test_easystack)
160+
ec_fns, opts = parse_easystack(test_easystack)
130161
expected = ['foo-1.2.3.eb', 'foo-3.2.1.eb']
131162
self.assertEqual(sorted(ec_fns), sorted(expected))
163+
self.assertEqual(opts, {})
132164

133165
# multiple versions listed with more info
134166
test_easystack_txt = '\n'.join([
@@ -139,9 +171,10 @@ def test_easystack_versions(self):
139171
" versionsuffix: -foo",
140172
])
141173
write_file(test_easystack, test_easystack_txt)
142-
ec_fns, _ = parse_easystack(test_easystack)
174+
ec_fns, opts = parse_easystack(test_easystack)
143175
expected = ['foo-1.2.3.eb', 'foo-2021a.eb', 'foo-3.2.1-foo.eb']
144176
self.assertEqual(sorted(ec_fns), sorted(expected))
177+
self.assertEqual(opts, {})
145178

146179
# versions that get interpreted by YAML as float or int, single quotes required
147180
for version in ('1.2', '123', '3.50', '100', '2.44_01'):
@@ -152,8 +185,9 @@ def test_easystack_versions(self):
152185

153186
# all is fine when wrapping the value in single quotes
154187
write_file(test_easystack, tmpl_easystack_txt + " '" + version + "'")
155-
ec_fns, _ = parse_easystack(test_easystack)
188+
ec_fns, opts = parse_easystack(test_easystack)
156189
self.assertEqual(ec_fns, ['foo-%s.eb' % version])
190+
self.assertEqual(opts, {})
157191

158192
# one rotten apple in the basket is enough
159193
test_easystack_txt = tmpl_easystack_txt + " [1.2.3, %s, 3.2.1]" % version
@@ -179,9 +213,10 @@ def test_easystack_versions(self):
179213
" versionsuffix: -foo",
180214
])
181215
write_file(test_easystack, test_easystack_txt)
182-
ec_fns, _ = parse_easystack(test_easystack)
216+
ec_fns, opts = parse_easystack(test_easystack)
183217
expected = ['foo-1.2.3.eb', 'foo-%s.eb' % version, 'foo-3.2.1-foo.eb']
184218
self.assertEqual(sorted(ec_fns), sorted(expected))
219+
self.assertEqual(opts, {})
185220

186221
# also check toolchain version that could be interpreted as a non-string value...
187222
test_easystack_txt = '\n'.join([
@@ -192,9 +227,10 @@ def test_easystack_versions(self):
192227
" versions: [1.2.3, '2.3']",
193228
])
194229
write_file(test_easystack, test_easystack_txt)
195-
ec_fns, _ = parse_easystack(test_easystack)
230+
ec_fns, opts = parse_easystack(test_easystack)
196231
expected = ['test-1.2.3-intel-2021.03.eb', 'test-2.3-intel-2021.03.eb']
197232
self.assertEqual(sorted(ec_fns), sorted(expected))
233+
self.assertEqual(opts, {})
198234

199235

200236
def suite():
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
easyconfigs:
2+
- binutils-2.25-GCCcore-4.9.3
3+
- binutils-2.26-GCCcore-4.9.3
4+
- foss-2018a
5+
- toy-0.0-gompi-2018a-test

0 commit comments

Comments
 (0)