Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -3690,15 +3690,21 @@ def build_and_install_one(ecdict, init_env):

if os.path.exists(app.installdir) and build_option('read_only_installdir') and (
build_option('rebuild') or build_option('force')):
enabled_write_permissions = True
# re-enable write permissions so we can install additional modules
adjust_permissions(app.installdir, stat.S_IWUSR, add=True, recursive=True)
else:
enabled_write_permissions = False

result = app.run_all_steps(run_test_cases=run_test_cases)

if not dry_run:
# also add any extension easyblocks used during the build for reproducibility
if app.ext_instances:
copy_easyblocks_for_reprod(app.ext_instances, reprod_dir)
# If not already done remove the granted write permissions if we did so
if enabled_write_permissions and os.lstat(app.installdir)[stat.ST_MODE] & stat.S_IWUSR:
adjust_permissions(app.installdir, stat.S_IWUSR, add=False, recursive=True)

except EasyBuildError as err:
first_n = 300
Expand All @@ -3715,28 +3721,37 @@ def build_and_install_one(ecdict, init_env):

# successful (non-dry-run) build
if result and not dry_run:
def ensure_writable_log_dir(log_dir):
"""Make sure we can write into the log dir"""
if build_option('read_only_installdir'):
# temporarily re-enable write permissions for copying log/easyconfig to install dir
if os.path.exists(log_dir):
adjust_permissions(log_dir, stat.S_IWUSR, add=True, recursive=True)
else:
parent_dir = os.path.dirname(log_dir)
if os.path.exists(parent_dir):
adjust_permissions(parent_dir, stat.S_IWUSR, add=True, recursive=False)
mkdir(log_dir, parents=True)
adjust_permissions(parent_dir, stat.S_IWUSR, add=False, recursive=False)
else:
mkdir(log_dir, parents=True)
adjust_permissions(log_dir, stat.S_IWUSR, add=True, recursive=True)

if app.cfg['stop']:
ended = 'STOPPED'
if app.builddir is not None:
new_log_dir = os.path.join(app.builddir, config.log_path(ec=app.cfg))
else:
new_log_dir = os.path.dirname(app.logfile)
ensure_writable_log_dir(new_log_dir)

# if we're only running the sanity check, we should not copy anything new to the installation directory
elif build_option('sanity_check_only'):
_log.info("Only running sanity check, so skipping build stats, easyconfigs archive, reprod files...")

else:
new_log_dir = os.path.join(app.installdir, config.log_path(ec=app.cfg))
if build_option('read_only_installdir'):
# temporarily re-enable write permissions for copying log/easyconfig to install dir
if os.path.exists(new_log_dir):
adjust_permissions(new_log_dir, stat.S_IWUSR, add=True, recursive=True)
else:
adjust_permissions(app.installdir, stat.S_IWUSR, add=True, recursive=False)
mkdir(new_log_dir, parents=True)
adjust_permissions(app.installdir, stat.S_IWUSR, add=False, recursive=False)
ensure_writable_log_dir(new_log_dir)

# collect build stats
_log.info("Collecting build stats...")
Expand Down
7 changes: 7 additions & 0 deletions easybuild/framework/easyconfig/easyconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,13 @@ def disable_templating(self):
finally:
self.enable_templating = old_enable_templating

def __str__(self):
"""Return a string representation of this EasyConfig instance"""
if self.path:
return '%s EasyConfig @ %s' % (self.name, self.path)
else:
return 'Raw %s EasyConfig' % self.name

def filename(self):
"""Determine correct filename for this easyconfig file."""

Expand Down
2 changes: 1 addition & 1 deletion easybuild/tools/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,7 @@ def run_module(self, *args, **kwargs):
else:
args = list(args)

self.log.debug('Current MODULEPATH: %s' % os.environ.get('MODULEPATH', ''))
self.log.debug('Current MODULEPATH: %s' % os.environ.get('MODULEPATH', '<unset>'))

# restore selected original environment variables before running module command
environ = os.environ.copy()
Expand Down
26 changes: 20 additions & 6 deletions test/framework/toy_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,21 +106,26 @@ def tearDown(self):
if os.path.exists(self.dummylogfn):
os.remove(self.dummylogfn)

def check_toy(self, installpath, outtxt, version='0.0', versionprefix='', versionsuffix=''):
def check_toy(self, installpath, outtxt, version='0.0', versionprefix='', versionsuffix='', error=None):
"""Check whether toy build succeeded."""

full_version = ''.join([versionprefix, version, versionsuffix])

if error is not None:
error_msg = '\nNote: Caught error: %s' % error
else:
error_msg = ''

# check for success
success = re.compile(r"COMPLETED: Installation ended successfully \(took .* secs?\)")
self.assertTrue(success.search(outtxt), "COMPLETED message found in '%s" % outtxt)
success = re.compile(r"COMPLETED: Installation (ended|STOPPED) successfully \(took .* secs?\)")
self.assertTrue(success.search(outtxt), "COMPLETED message found in '%s'%s" % (outtxt, error_msg))

# if the module exists, it should be fine
toy_module = os.path.join(installpath, 'modules', 'all', 'toy', full_version)
msg = "module for toy build toy/%s found (path %s)" % (full_version, toy_module)
if get_module_syntax() == 'Lua':
toy_module += '.lua'
self.assertTrue(os.path.exists(toy_module), msg)
self.assertTrue(os.path.exists(toy_module), msg + error_msg)

# module file is symlinked according to moduleclass
toy_module_symlink = os.path.join(installpath, 'modules', 'tools', 'toy', full_version)
Expand Down Expand Up @@ -183,7 +188,7 @@ def test_toy_build(self, extra_args=None, ec_file=None, tmpdir=None, verify=True
raise myerr

if verify:
self.check_toy(self.test_installpath, outtxt, versionsuffix=versionsuffix)
self.check_toy(self.test_installpath, outtxt, versionsuffix=versionsuffix, error=myerr)

if test_readme:
# make sure postinstallcmds were used
Expand Down Expand Up @@ -615,7 +620,16 @@ def test_toy_permissions_installdir(self):
# 2. Existing build with --rebuild -> Reinstall and set read-only
# 3. Existing build with --force -> Reinstall and set read-only
# 4-5: Same as 2-3 but with --skip
for extra_args in ([], ['--rebuild'], ['--force'], ['--skip', '--rebuild'], ['--skip', '--force']):
# 6. Existing build with --fetch -> Test that logs can be written
test_cases = (
[],
['--rebuild'],
['--force'],
['--skip', '--rebuild'],
['--skip', '--force'],
['--rebuild', '--fetch'],
)
for extra_args in test_cases:
self.mock_stdout(True)
self.test_toy_build(ec_file=test_ec, extra_args=['--read-only-installdir'] + extra_args, force=False)
self.mock_stdout(False)
Expand Down
8 changes: 7 additions & 1 deletion test/framework/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,13 @@ def eb_main(self, args, do_build=False, return_error=False, logfile=None, verbos
env_before = copy.deepcopy(os.environ)

try:
main(args=args, logfile=logfile, do_build=do_build, testing=testing, modtool=self.modtool)
if '--fetch' in args:
# The config sets modules_tool to None if --fetch is specified,
# so do the same here to keep the behavior consistent
modtool = None
else:
modtool = self.modtool
main(args=args, logfile=logfile, do_build=do_build, testing=testing, modtool=modtool)
except SystemExit as err:
if raise_systemexit:
raise err
Expand Down