Skip to content

Commit a106f61

Browse files
committed
Make specifying exts_defaultclass optional
It is NOT required when all extensions have either custom easyblocks or an `easyblock` key in their option dict. In those cases `exts_defaultclass` is redundant making it harder to write the EasyConfig. Check for a missing easyblock when actually using it so the error will be just a bit later but still (almost) the same.
1 parent 944e3ed commit a106f61

File tree

2 files changed

+56
-16
lines changed

2 files changed

+56
-16
lines changed

easybuild/framework/easyblock.py

+16-15
Original file line numberDiff line numberDiff line change
@@ -2792,8 +2792,10 @@ def init_ext_instances(self):
27922792
# proper way: derive module path from specified class name
27932793
default_class = exts_defaultclass
27942794
default_class_modpath = get_module_path(default_class, generic=True)
2795+
elif exts_defaultclass is None:
2796+
default_class = default_class_modpath = None
27952797
else:
2796-
error_msg = "Improper default extension class specification, should be string: %s (%s)"
2798+
error_msg = "Improper default extension class specification, should be string or None: %s (%s)"
27972799
raise EasyBuildError(error_msg, exts_defaultclass, type(exts_defaultclass))
27982800

27992801
exts_cnt = len(self.exts)
@@ -2846,8 +2848,11 @@ def init_ext_instances(self):
28462848
"for extension %s: %s",
28472849
class_name, mod_path, ext_name, err)
28482850

2849-
# fallback attempt: use default class
2851+
# fallback attempt: use default class if any
28502852
if inst is None:
2853+
if not default_class:
2854+
raise EasyBuildError("ERROR: No default extension class set for %s and no explicit or custom "
2855+
"easyblock found for extension %s", self.name, ext_name)
28512856
try:
28522857
cls = get_class_for(default_class_modpath, default_class)
28532858
self.log.debug("Obtained class %s for installing extension %s", cls, ext_name)
@@ -2906,21 +2911,17 @@ def extensions_step(self, fetch=False, install=True):
29062911
if install:
29072912
self.log.info("Installing extensions")
29082913

2909-
# we really need a default class
2910-
if not self.cfg['exts_defaultclass'] and fake_mod_data:
2911-
self.clean_up_fake_module(fake_mod_data)
2912-
raise EasyBuildError("ERROR: No default extension class set for %s", self.name)
2913-
2914-
self.init_ext_instances()
2915-
2916-
if self.skip:
2917-
self.skip_extensions()
2914+
try:
2915+
self.init_ext_instances()
29182916

2919-
self.install_extensions(install=install)
2917+
if self.skip:
2918+
self.skip_extensions()
29202919

2921-
# cleanup (unload fake module, remove fake module dir)
2922-
if fake_mod_data:
2923-
self.clean_up_fake_module(fake_mod_data)
2920+
self.install_extensions(install=install)
2921+
finally:
2922+
# cleanup (unload fake module, remove fake module dir)
2923+
if fake_mod_data:
2924+
self.clean_up_fake_module(fake_mod_data)
29242925

29252926
stop_progress_bar(PROGRESS_BAR_EXTENSIONS, visible=False)
29262927

test/framework/easyconfig.py

+40-1
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,46 @@ def test_exts_list(self):
527527
regex = re.compile('EBEXTSLISTPI.*ext1-1.0,ext2-2.0')
528528
self.assertTrue(regex.search(modtxt), "Pattern '%s' found in: %s" % (regex.pattern, modtxt))
529529

530+
def test_extensions_default_class(self):
531+
"""Test that exts_defaultclass doesn't need to be specified if explicit one is present."""
532+
533+
init_config(build_options={'silent': True})
534+
535+
self.contents = textwrap.dedent("""
536+
easyblock = "ConfigureMake"
537+
name = "PI"
538+
version = "3.14"
539+
homepage = "http://example.com"
540+
description = "test easyconfig"
541+
toolchain = SYSTEM
542+
exts_list = [
543+
("toy", "0.0"), # Custom block by name
544+
("bar", "0.0", { # Explicit
545+
'easyblock': 'Toy_Extension',
546+
'sources': ['toy-%(version)s.tar.gz'],
547+
}),
548+
]
549+
""")
550+
self.prep()
551+
# Ensure source is found
552+
toy_tar_gz = os.path.join(self.test_sourcepath, 'toy', 'toy-0.0.tar.gz')
553+
copy_file(toy_tar_gz, self.test_prefix)
554+
os.environ['EASYBUILD_SOURCEPATH'] = self.test_prefix
555+
init_config(build_options={'silent': True})
556+
557+
ec = EasyConfig(self.eb_file)
558+
eb = EasyBlock(ec)
559+
eb.fetch_step()
560+
with self.mocked_stdout_stderr():
561+
eb.extensions_step()
562+
563+
pi_installdir = os.path.join(self.test_installpath, 'software', 'PI', '3.14')
564+
565+
# check whether files expected to be installed for both extensions are in place
566+
self.assertExists(os.path.join(pi_installdir, 'bin', 'toy'))
567+
self.assertExists(os.path.join(pi_installdir, 'lib', 'libtoy.a'))
568+
self.assertExists(os.path.join(pi_installdir, 'lib', 'libbar.a'))
569+
530570
def test_extensions_templates(self):
531571
"""Test whether templates used in exts_list are resolved properly."""
532572

@@ -549,7 +589,6 @@ def test_extensions_templates(self):
549589
'description = "test easyconfig"',
550590
'toolchain = SYSTEM',
551591
'dependencies = [("Python", "3.6.6")]',
552-
'exts_defaultclass = "EB_Toy"',
553592
# bogus, but useful to check whether this get resolved
554593
'exts_default_options = {"source_urls": [PYPI_SOURCE]}',
555594
'exts_list = [',

0 commit comments

Comments
 (0)