diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index f886e91551..d745a0e5da 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -1654,7 +1654,8 @@ def prepare_step(self, start_dir=True): self.rpath_filter_dirs.append(self.builddir) # prepare toolchain: load toolchain module and dependencies, set up build environment - self.toolchain.prepare(self.cfg['onlytcmod'], silent=self.silent, rpath_filter_dirs=self.rpath_filter_dirs) + self.toolchain.prepare(self.cfg['onlytcmod'], silent=self.silent, rpath=self.cfg['rpath'], + rpath_filter_dirs=self.rpath_filter_dirs) # handle allowed system dependencies for (name, version) in self.cfg['allow_system_deps']: @@ -1818,7 +1819,7 @@ def extensions_step(self, fetch=False): # don't reload modules for toolchain, there is no need since they will be loaded already; # the (fake) module for the parent software gets loaded before installing extensions inst.toolchain.prepare(onlymod=self.cfg['onlytcmod'], silent=True, loadmod=False, - rpath_filter_dirs=self.rpath_filter_dirs) + rpath=self.cfg['rpath'], rpath_filter_dirs=self.rpath_filter_dirs) # real work inst.prerun() diff --git a/easybuild/framework/easyconfig/default.py b/easybuild/framework/easyconfig/default.py index b9a945a9af..45525d567b 100644 --- a/easybuild/framework/easyconfig/default.py +++ b/easybuild/framework/easyconfig/default.py @@ -99,6 +99,7 @@ 'preconfigopts': ['', 'Extra options pre-passed to configure.', BUILD], 'preinstallopts': ['', 'Extra prefix options for installation.', BUILD], 'postinstallcmds': [[], 'Commands to run after the install step.', BUILD], + 'rpath': [False, "Use RPATH linking (regardless of --rpath configuration option)", BUILD], 'runtest': [None, ('Indicates if a test should be run after make; should specify argument ' 'after make (for e.g.,"test" for make test)'), BUILD], 'sanity_check_commands': [[], ("format: [(name, options)] e.g. [('gzip','-h')]. " diff --git a/easybuild/tools/toolchain/toolchain.py b/easybuild/tools/toolchain/toolchain.py index 0adef1f3c1..d5d820da50 100644 --- a/easybuild/tools/toolchain/toolchain.py +++ b/easybuild/tools/toolchain/toolchain.py @@ -637,7 +637,7 @@ def compilers(self): return (c_comps, fortran_comps) - def prepare(self, onlymod=None, silent=False, loadmod=True, rpath_filter_dirs=None): + def prepare(self, onlymod=None, silent=False, loadmod=True, rpath=None, rpath_filter_dirs=None): """ Prepare a set of environment parameters based on name/version of toolchain - load modules for toolchain and dependencies @@ -648,6 +648,7 @@ def prepare(self, onlymod=None, silent=False, loadmod=True, rpath_filter_dirs=No (If string: comma separated list of variables that will be ignored). :param silent: keep quiet, or not (mostly relates to extended dry run output) :param loadmod: whether or not to (re)load the toolchain module, and the modules for the dependencies + :param rpath: enable RPATH linking (regardless of 'rpath' configuration option) :param rpath_filter_dirs: extra directories to include in RPATH filter (e.g. build dir, tmpdir, ...) """ if loadmod: @@ -679,12 +680,20 @@ def prepare(self, onlymod=None, silent=False, loadmod=True, rpath_filter_dirs=No if build_option('use_%s' % cache_tool): self.prepare_compiler_cache(cache_tool) - if build_option('rpath'): + if rpath: + self.use_rpath = True + self.log.info("Enabling RPATH linking, as explicitely specified for this installation") + elif build_option('rpath'): if self.options.get('rpath', True): - self.prepare_rpath_wrappers() self.use_rpath = True + self.log.info("Enabling RPATH linking, as configured") else: - self.log.info("Not putting RPATH wrappers in place, disabled via 'rpath' toolchain option") + self.log.info("Not enabling use of RPATH linking, disabled via 'rpath' toolchain option") + else: + self.log.info("Not enabling use of RPATH linking, not requested") + + if self.use_rpath: + self.prepare_rpath_wrappers() def comp_cache_compilers(self, cache_tool): """ diff --git a/test/framework/toolchain.py b/test/framework/toolchain.py index c75718f8fc..cba5266808 100644 --- a/test/framework/toolchain.py +++ b/test/framework/toolchain.py @@ -1276,21 +1276,43 @@ def test_toolchain_prepare_rpath(self): fake_gcc = os.path.join(self.test_prefix, 'fake', 'gcc') write_file(fake_gcc, '#!/bin/bash\necho "$@"') adjust_permissions(fake_gcc, stat.S_IXUSR) - os.environ['PATH'] = '%s:%s' % (os.path.join(self.test_prefix, 'fake'), os.getenv('PATH', '')) + path = '%s:%s' % (os.path.join(self.test_prefix, 'fake'), os.getenv('PATH', '')) + os.environ['PATH'] = path - # enable --rpath and prepare toolchain - init_config(build_options={'rpath': True, 'rpath_filter': ['/ba.*']}) tc = self.get_toolchain('gompi', version='1.3.12') # preparing RPATH wrappers requires --experimental, need to bypass that here tc.log.experimental = lambda x: x + # by default, RPATH linking is not enabled + tc.prepare() + res = which('gcc', retain_all=True) + self.assertTrue(len(res) >= 1) + self.assertFalse(tc.is_rpath_wrapper(res[0])) + self.assertFalse(any(tc.is_rpath_wrapper(x) for x in res[1:])) + self.assertTrue(os.path.samefile(res[0], fake_gcc)) + + # use of RPATH wrapper can be enabled regardless of configuration (via 'rpath' easyconfig parameter) + os.environ['PATH'] = path + tc.prepare(rpath=True) + res = which('gcc', retain_all=True) + self.assertTrue(len(res) >= 2) + self.assertTrue(tc.is_rpath_wrapper(res[0])) + self.assertFalse(any(tc.is_rpath_wrapper(x) for x in res[1:])) + self.assertTrue(os.path.samefile(res[1], fake_gcc)) + + # enable --rpath and prepare toolchain + init_config(build_options={'rpath': True, 'rpath_filter': ['/ba.*']}) + tc = self.get_toolchain('gompi', version='1.3.12') + tc.log.experimental = lambda x: x + # 'rpath' toolchain option gives control to disable use of RPATH wrappers tc.set_options({}) self.assertTrue(tc.options['rpath']) # enabled by default # setting 'rpath' toolchain option to false implies no RPATH wrappers being used tc.set_options({'rpath': False}) + os.environ['PATH'] = path tc.prepare() res = which('gcc', retain_all=True) self.assertTrue(len(res) >= 1) @@ -1300,6 +1322,7 @@ def test_toolchain_prepare_rpath(self): # enable 'rpath' toolchain option again (equivalent to the default setting) tc.set_options({'rpath': True}) + os.environ['PATH'] = path tc.prepare() # check that wrapper is indeed in place diff --git a/test/framework/toy_build.py b/test/framework/toy_build.py index 500df62299..fc3d8c7e6d 100644 --- a/test/framework/toy_build.py +++ b/test/framework/toy_build.py @@ -1436,14 +1436,22 @@ def test_toy_rpath(self): # also test use of --rpath-filter self.test_toy_build(extra_args=['--rpath', '--rpath-filter=/test.*,/foo.*', '--experimental'], raise_error=True) - # test use of rpath toolchain option + # test use of rpath toolchain option to selectively disable use of RPATH test_ecs = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs') - toy_ec_txt = read_file(os.path.join(test_ecs, 't', 'toy', 'toy-0.0.eb')) + toy_ec = os.path.join(test_ecs, 't', 'toy', 'toy-0.0.eb') + toy_ec_txt = read_file(toy_ec) toy_ec_txt += "\ntoolchainopts = {'rpath': False}\n" toy_ec = os.path.join(self.test_prefix, 'toy.eb') write_file(toy_ec, toy_ec_txt) self.test_toy_build(ec_file=toy_ec, extra_args=['--rpath', '--experimental'], raise_error=True) + # test use of rpath easyconfig parameter to selectively enable use of RPATH regardless of configuration + toy_ec_txt = read_file(toy_ec) + toy_ec_txt += "\nrpath = True\n" + toy_ec = os.path.join(self.test_prefix, 'toy.eb') + write_file(toy_ec, toy_ec_txt) + self.test_toy_build(ec_file=toy_ec, extra_args=['--experimental'], raise_error=True) + def test_toy_modaltsoftname(self): """Build two dependent toys as in test_toy_toy but using modaltsoftname""" topdir = os.path.dirname(os.path.abspath(__file__))