Skip to content

Commit fef920d

Browse files
authored
Merge pull request #3762 from boegel/ext_easyblock
honor specified easyblock for extensions
2 parents c32f8d9 + 6f3ff80 commit fef920d

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

easybuild/framework/easyblock.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
from easybuild.framework.easyconfig.style import MAX_LINE_LENGTH
6262
from easybuild.framework.easyconfig.tools import get_paths_for
6363
from easybuild.framework.easyconfig.templates import TEMPLATE_NAMES_EASYBLOCK_RUN_STEP, template_constant_dict
64-
from easybuild.framework.extension import resolve_exts_filter_template
64+
from easybuild.framework.extension import Extension, resolve_exts_filter_template
6565
from easybuild.tools import config, run
6666
from easybuild.tools.build_details import get_build_stats
6767
from easybuild.tools.build_log import EasyBuildError, dry_run_msg, dry_run_warning, dry_run_set_dirs
@@ -540,6 +540,10 @@ def fetch_extension_sources(self, skip_checksums=False):
540540
'options': ext_options,
541541
}
542542

543+
# if a particular easyblock is specified, make sure it's used
544+
# (this is picked up by init_ext_instances)
545+
ext_src['easyblock'] = ext_options.get('easyblock', None)
546+
543547
# construct dictionary with template values;
544548
# inherited from parent, except for name/version templates which are specific to this extension
545549
template_values = copy.deepcopy(self.cfg.template_values)
@@ -2295,18 +2299,31 @@ def init_ext_instances(self):
22952299
ext_name = ext['name']
22962300
self.log.debug("Creating class instance for extension %s...", ext_name)
22972301

2302+
# if a specific easyblock is specified for this extension, honor it;
2303+
# just passing this to get_easyblock_class is sufficient
2304+
easyblock = ext.get('easyblock', None)
2305+
if easyblock:
2306+
class_name = easyblock
2307+
mod_path = get_module_path(class_name)
2308+
else:
2309+
class_name = encode_class_name(ext_name)
2310+
mod_path = get_module_path(class_name, generic=False)
2311+
22982312
cls, inst = None, None
2299-
class_name = encode_class_name(ext_name)
2300-
mod_path = get_module_path(class_name, generic=False)
23012313

2302-
# try instantiating extension-specific class
2314+
# try instantiating extension-specific class, or honor specified easyblock
23032315
try:
23042316
# no error when importing class fails, in case we run into an existing easyblock
23052317
# with a similar name (e.g., Perl Extension 'GO' vs 'Go' for which 'EB_Go' is available)
2306-
cls = get_easyblock_class(None, name=ext_name, error_on_failed_import=False,
2318+
cls = get_easyblock_class(easyblock, name=ext_name, error_on_failed_import=False,
23072319
error_on_missing_easyblock=False)
2320+
23082321
self.log.debug("Obtained class %s for extension %s", cls, ext_name)
23092322
if cls is not None:
2323+
# make sure that this easyblock can be used to install extensions
2324+
if not issubclass(cls, Extension):
2325+
raise EasyBuildError("%s easyblock can not be used to install extensions!", cls.__name__)
2326+
23102327
inst = cls(self, ext)
23112328
except (ImportError, NameError) as err:
23122329
self.log.debug("Failed to use extension-specific class for extension %s: %s", ext_name, err)

test/framework/easyblock.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,42 @@ def test_extensions_step(self):
993993
eb.close_log()
994994
os.remove(eb.logfile)
995995

996+
def test_init_extensions(self):
997+
"""Test creating extension instances."""
998+
999+
testdir = os.path.abspath(os.path.dirname(__file__))
1000+
toy_ec_file = os.path.join(testdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0-gompi-2018a-test.eb')
1001+
toy_ec_txt = read_file(toy_ec_file)
1002+
1003+
test_ec = os.path.join(self.test_prefix, 'test.eb')
1004+
test_ec_txt = toy_ec_txt.replace("('barbar', '0.0', {", "('barbar', '0.0', {'easyblock': 'DummyExtension',")
1005+
write_file(test_ec, test_ec_txt)
1006+
ec = process_easyconfig(test_ec)[0]
1007+
eb = get_easyblock_instance(ec)
1008+
1009+
eb.prepare_for_extensions()
1010+
eb.init_ext_instances()
1011+
ext_inst_class_names = [x.__class__.__name__ for x in eb.ext_instances]
1012+
expected = [
1013+
'Toy_Extension', # 'ls' extension
1014+
'Toy_Extension', # 'bar' extension
1015+
'DummyExtension', # 'barbar' extension
1016+
'EB_toy', # 'toy' extension
1017+
]
1018+
self.assertEqual(ext_inst_class_names, expected)
1019+
1020+
# check what happen if we specify an easyblock that doesn't derive from Extension,
1021+
# and hence can't be used to install extensions...
1022+
test_ec = os.path.join(self.test_prefix, 'test_broken.eb')
1023+
test_ec_txt = test_ec_txt.replace('DummyExtension', 'ConfigureMake')
1024+
write_file(test_ec, test_ec_txt)
1025+
ec = process_easyconfig(test_ec)[0]
1026+
eb = get_easyblock_instance(ec)
1027+
1028+
eb.prepare_for_extensions()
1029+
error_pattern = "ConfigureMake easyblock can not be used to install extensions"
1030+
self.assertErrorRegex(EasyBuildError, error_pattern, eb.init_ext_instances)
1031+
9961032
def test_skip_extensions_step(self):
9971033
"""Test the skip_extensions_step"""
9981034

0 commit comments

Comments
 (0)