Skip to content

Commit d66c0e5

Browse files
authored
Merge pull request #3375 from ocaisa/track_all_easyblocks_used
Also put easyblocks used by extensions in `reprod` directory
2 parents a4fcae2 + 5970655 commit d66c0e5

File tree

2 files changed

+66
-12
lines changed

2 files changed

+66
-12
lines changed

easybuild/framework/easyblock.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,11 +3278,19 @@ def build_and_install_one(ecdict, init_env):
32783278
start_time = time.time()
32793279
try:
32803280
run_test_cases = not build_option('skip_test_cases') and app.cfg['tests']
3281+
32813282
if not dry_run:
32823283
# create our reproducability files before carrying out the easyblock steps
32833284
reprod_dir_root = os.path.dirname(app.logfile)
32843285
reprod_dir = reproduce_build(app, reprod_dir_root)
3286+
32853287
result = app.run_all_steps(run_test_cases=run_test_cases)
3288+
3289+
if not dry_run:
3290+
# also add any extension easyblocks used during the build for reproducability
3291+
if app.ext_instances:
3292+
copy_easyblocks_for_reprod(app.ext_instances, reprod_dir)
3293+
32863294
except EasyBuildError as err:
32873295
first_n = 300
32883296
errormsg = "build failed (first %d chars): %s" % (first_n, err.msg[:first_n])
@@ -3416,6 +3424,24 @@ def build_and_install_one(ecdict, init_env):
34163424
return (success, application_log, errormsg)
34173425

34183426

3427+
def copy_easyblocks_for_reprod(easyblock_instances, reprod_dir):
3428+
reprod_easyblock_dir = os.path.join(reprod_dir, 'easyblocks')
3429+
easyblock_paths = set()
3430+
for easyblock_instance in easyblock_instances:
3431+
for easyblock_class in inspect.getmro(type(easyblock_instance)):
3432+
easyblock_path = inspect.getsourcefile(easyblock_class)
3433+
# if we reach EasyBlock or ExtensionEasyBlock class, we are done
3434+
# (ExtensionEasyblock is hardcoded to avoid a cyclical import)
3435+
if easyblock_class.__name__ in [EasyBlock.__name__, 'ExtensionEasyBlock']:
3436+
break
3437+
else:
3438+
easyblock_paths.add(easyblock_path)
3439+
for easyblock_path in easyblock_paths:
3440+
easyblock_basedir, easyblock_filename = os.path.split(easyblock_path)
3441+
copy_file(easyblock_path, os.path.join(reprod_easyblock_dir, easyblock_filename))
3442+
_log.info("Dumped easyblock %s required for reproduction to %s", easyblock_filename, reprod_easyblock_dir)
3443+
3444+
34193445
def reproduce_build(app, reprod_dir_root):
34203446
"""
34213447
Create reproducability files (processed easyconfig and easyblocks used) from class instance
@@ -3437,18 +3463,8 @@ def reproduce_build(app, reprod_dir_root):
34373463
except NotImplementedError as err:
34383464
_log.warning("Unable to dump easyconfig instance to %s: %s", reprod_spec, err)
34393465

3440-
# also archive the relevant easyblocks
3441-
reprod_easyblock_dir = os.path.join(reprod_dir, 'easyblocks')
3442-
for easyblock_class in inspect.getmro(type(app)):
3443-
easyblock_path = inspect.getsourcefile(easyblock_class)
3444-
easyblock_basedir, easyblock_filename = os.path.split(easyblock_path)
3445-
# if we reach EasyBlock or ExtensionEasyBlock class, we are done
3446-
# (ExtensionEasyblock is hardcoded to avoid a cyclical import)
3447-
if easyblock_class.__name__ in [EasyBlock.__name__, 'ExtensionEasyBlock']:
3448-
break
3449-
else:
3450-
copy_file(easyblock_path, os.path.join(reprod_easyblock_dir, easyblock_filename))
3451-
_log.info("Dumped easyblock %s required for reproduction to %s", easyblock_filename, reprod_easyblock_dir)
3466+
# also archive all the relevant easyblocks (including any used by extensions)
3467+
copy_easyblocks_for_reprod([app], reprod_dir)
34523468

34533469
# if there is a hook file we should also archive it
34543470
hooks_path = build_option('hooks')

test/framework/toy_build.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,6 +1839,44 @@ def test_reproducability(self):
18391839
reprod_hooks = os.path.join(reprod_dir, 'hooks', hooks_filename)
18401840
self.assertTrue(os.path.exists(reprod_hooks))
18411841

1842+
def test_reproducability_ext_easyblocks(self):
1843+
"""Test toy build produces expected reproducability files also when extensions are used"""
1844+
1845+
topdir = os.path.dirname(os.path.abspath(__file__))
1846+
toy_ec_file = os.path.join(topdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb')
1847+
toy_ec_txt = read_file(toy_ec_file)
1848+
1849+
ec1 = os.path.join(self.test_prefix, 'toy1.eb')
1850+
ec1_txt = '\n'.join([
1851+
toy_ec_txt,
1852+
"exts_list = [('barbar', '0.0')]",
1853+
"",
1854+
])
1855+
write_file(ec1, ec1_txt)
1856+
1857+
self.test_toy_build(ec_file=ec1, verify=False, extra_args=['--minimal-toolchains', '--easyblock=EB_toytoy'])
1858+
1859+
# Check whether easyconfig is dumped to reprod/ subdir
1860+
reprod_dir = os.path.join(self.test_installpath, 'software', 'toy', '0.0', 'easybuild', 'reprod')
1861+
reprod_ec = os.path.join(reprod_dir, 'toy-0.0.eb')
1862+
1863+
self.assertTrue(os.path.exists(reprod_ec))
1864+
1865+
# Check for child easyblock existence
1866+
child_easyblock = os.path.join(reprod_dir, 'easyblocks', 'toytoy.py')
1867+
self.assertTrue(os.path.exists(child_easyblock))
1868+
# Check for parent easyblock existence
1869+
parent_easyblock = os.path.join(reprod_dir, 'easyblocks', 'toy.py')
1870+
self.assertTrue(os.path.exists(parent_easyblock))
1871+
# Check for extension easyblock existence
1872+
ext_easyblock = os.path.join(reprod_dir, 'easyblocks', 'toy_extension.py')
1873+
self.assertTrue(os.path.exists(ext_easyblock))
1874+
1875+
# Make sure framework easyblock modules are not included
1876+
for framework_easyblock in ['easyblock.py', 'extensioneasyblock.py']:
1877+
path = os.path.join(reprod_dir, 'easyblocks', framework_easyblock)
1878+
self.assertFalse(os.path.exists(path))
1879+
18421880
def test_toy_toy(self):
18431881
"""Test building two easyconfigs in a single go, with one depending on the other."""
18441882
topdir = os.path.dirname(os.path.abspath(__file__))

0 commit comments

Comments
 (0)