Skip to content

Commit 79495df

Browse files
committed
Add rpath_wrappers_dir build option
1 parent 6684446 commit 79495df

File tree

6 files changed

+39
-71
lines changed

6 files changed

+39
-71
lines changed

easybuild/framework/easyblock.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,7 +2102,9 @@ def install_extensions_sequential(self, install=True):
21022102
# don't reload modules for toolchain, there is no need since they will be loaded already;
21032103
# the (fake) module for the parent software gets loaded before installing extensions
21042104
ext.toolchain.prepare(onlymod=self.cfg['onlytcmod'], silent=True, loadmod=False,
2105-
rpath_filter_dirs=self.rpath_filter_dirs)
2105+
rpath_filter_dirs=self.rpath_filter_dirs,
2106+
rpath_include_dirs=self.rpath_include_dirs,
2107+
rpath_wrappers_dir=self.rpath_wrappers_dir)
21062108

21072109
# actual installation of the extension
21082110
if install:
@@ -2264,7 +2266,9 @@ def update_exts_progress_bar_helper(running_exts, progress_size):
22642266
# don't reload modules for toolchain, there is no need since they will be loaded already;
22652267
# the (fake) module for the parent software gets loaded before installing extensions
22662268
ext.toolchain.prepare(onlymod=self.cfg['onlytcmod'], silent=True, loadmod=False,
2267-
rpath_filter_dirs=self.rpath_filter_dirs)
2269+
rpath_filter_dirs=self.rpath_filter_dirs,
2270+
rpath_include_dirs=self.rpath_include_dirs,
2271+
rpath_wrappers_dir=self.rpath_wrappers_dir)
22682272
if install:
22692273
ext.install_extension_substep("pre_install_extension")
22702274
ext.async_cmd_task = ext.install_extension_substep("install_extension_async", thread_pool)
@@ -2878,6 +2882,15 @@ def prepare_step(self, start_dir=True, load_tc_deps_modules=True):
28782882
'$ORIGIN/../lib64',
28792883
])
28802884

2885+
# Location to store RPATH wrappers
2886+
self.rpath_wrappers_dir = build_option('rpath_wrappers_dir')
2887+
if self.rpath_wrappers_dir is not None:
2888+
# Verify the path given is absolute
2889+
if os.path.isabs(self.rpath_wrappers_dir):
2890+
_log.debug("Using %s to store/use RPATH wrappers" % self.rpath_wrappers_dir)
2891+
else:
2892+
raise EasyBuildError("Path used for rpath_wrappers_dir is not an absolute path: %s", path)
2893+
28812894
if self.iter_idx > 0:
28822895
# reset toolchain for iterative runs before preparing it again
28832896
self.toolchain.reset()
@@ -2895,7 +2908,7 @@ def prepare_step(self, start_dir=True, load_tc_deps_modules=True):
28952908
# prepare toolchain: load toolchain module and dependencies, set up build environment
28962909
self.toolchain.prepare(self.cfg['onlytcmod'], deps=self.cfg.dependencies(), silent=self.silent,
28972910
loadmod=load_tc_deps_modules, rpath_filter_dirs=self.rpath_filter_dirs,
2898-
rpath_include_dirs=self.rpath_include_dirs)
2911+
rpath_include_dirs=self.rpath_include_dirs, rpath_wrappers_dir=self.rpath_wrappers_dir)
28992912

29002913
# keep track of environment variables that were tweaked and need to be restored after environment got reset
29012914
# $TMPDIR may be tweaked for OpenMPI 2.x, which doesn't like long $TMPDIR paths...

easybuild/tools/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
276276
'regtest_output_dir',
277277
'rpath_filter',
278278
'rpath_override_dirs',
279+
'rpath_wrappers_dir',
279280
'required_linked_shared_libs',
280281
'search_path_cpp_headers',
281282
'search_path_linker',

easybuild/tools/options.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,8 @@ def override_options(self):
521521
'rpath-filter': ("List of regex patterns to use for filtering out RPATH paths", 'strlist', 'store', None),
522522
'rpath-override-dirs': ("Path(s) to be prepended when linking with RPATH (string, colon-separated)",
523523
None, 'store', None),
524+
'rpath-wrappers-dir': ("Absolute path to directory to use for RPATH wrappers creation/use",
525+
None, 'store', None),
524526
'sanity-check-only': ("Only run sanity check (module is expected to be installed already",
525527
None, 'store_true', False),
526528
'set-default-module': ("Set the generated module as default", None, 'store_true', False),

easybuild/tools/toolchain/toolchain.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ def reset(self):
839839
self.variables_init()
840840

841841
def prepare(self, onlymod=None, deps=None, silent=False, loadmod=True,
842-
rpath_filter_dirs=None, rpath_include_dirs=None):
842+
rpath_filter_dirs=None, rpath_include_dirs=None, rpath_wrappers_dir=None):
843843
"""
844844
Prepare a set of environment parameters based on name/version of toolchain
845845
- load modules for toolchain and dependencies
@@ -853,6 +853,7 @@ def prepare(self, onlymod=None, deps=None, silent=False, loadmod=True,
853853
:param loadmod: whether or not to (re)load the toolchain module, and the modules for the dependencies
854854
:param rpath_filter_dirs: extra directories to include in RPATH filter (e.g. build dir, tmpdir, ...)
855855
:param rpath_include_dirs: extra directories to include in RPATH
856+
:param rpath_wrappers_dir: directory in which to create RPATH wrappers
856857
"""
857858

858859
# take into account --sysroot configuration setting
@@ -906,7 +907,11 @@ def prepare(self, onlymod=None, deps=None, silent=False, loadmod=True,
906907

907908
if build_option('rpath'):
908909
if self.options.get('rpath', True):
909-
self.prepare_rpath_wrappers(rpath_filter_dirs, rpath_include_dirs)
910+
self.prepare_rpath_wrappers(
911+
rpath_filter_dirs=rpath_filter_dirs,
912+
rpath_include_dirs=rpath_include_dirs,
913+
rpath_wrappers_dir=rpath_wrappers_dir
914+
)
910915
self.use_rpath = True
911916
else:
912917
self.log.info("Not putting RPATH wrappers in place, disabled via 'rpath' toolchain option")
@@ -975,8 +980,7 @@ def is_rpath_wrapper(path):
975980
# need to use binary mode to read the file, since it may be an actual compiler command (which is a binary file)
976981
return b'rpath_args.py $CMD' in read_file(path, mode='rb')
977982

978-
def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None, wrappers_dir=None,
979-
add_to_path=True, disable_wrapper_log=False):
983+
def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None, rpath_wrappers_dir=None):
980984
"""
981985
Put RPATH wrapper script in place for compiler and linker commands
982986
@@ -999,10 +1003,13 @@ def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None
9991003
rpath_filter_dirs.append(lib_stubs_pattern)
10001004

10011005
# directory where all wrappers will be placed
1002-
if wrappers_dir is None:
1006+
disable_wrapper_log = False
1007+
if rpath_wrappers_dir is None:
10031008
wrappers_dir = os.path.join(tempfile.mkdtemp(), RPATH_WRAPPERS_SUBDIR)
10041009
else:
1005-
wrappers_dir = os.path.join(wrappers_dir, RPATH_WRAPPERS_SUBDIR)
1010+
wrappers_dir = os.path.join(rpath_wrappers_dir, RPATH_WRAPPERS_SUBDIR)
1011+
# No logging when we may be exporting wrappers
1012+
disable_wrapper_log = True
10061013

10071014
# must also wrap compilers commands, required e.g. for Clang ('gcc' on OS X)?
10081015
c_comps, fortran_comps = self.compilers()
@@ -1068,8 +1075,7 @@ def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None
10681075
adjust_permissions(cmd_wrapper, stat.S_IXUSR)
10691076

10701077
# prepend location to this wrapper to $PATH
1071-
if add_to_path:
1072-
setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))
1078+
setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))
10731079

10741080
self.log.info("RPATH wrapper script for %s: %s (log: %s)", orig_cmd, which(cmd), rpath_wrapper_log)
10751081
else:

easybuild/tools/toolchain/utilities.py

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,13 @@
3636
* Kenneth Hoste (Ghent University)
3737
"""
3838
import copy
39-
import os
4039
import re
4140
import sys
4241

4342
import easybuild.tools.toolchain
4443
from easybuild.base import fancylogger
4544
from easybuild.tools.build_log import EasyBuildError
46-
from easybuild.tools.environment import setvar
47-
from easybuild.tools.toolchain.toolchain import Toolchain, RPATH_WRAPPERS_SUBDIR
45+
from easybuild.tools.toolchain.toolchain import Toolchain
4846
from easybuild.tools.utilities import get_subclasses, import_available_modules, nub
4947

5048

@@ -153,25 +151,3 @@ def get_toolchain(tc, tcopts, mns=None, tcdeps=None, modtool=None):
153151
tc_inst.set_options(tcopts)
154152

155153
return tc_inst
156-
157-
158-
def export_rpath_wrappers(targetdir, toolchain_name, toolchain_version, rpath_filter_dirs=None,
159-
rpath_include_dirs=None):
160-
tc = get_toolchain({'name': toolchain_name, 'version': toolchain_version}, {})
161-
162-
# Temporarily filter any existing RPATH wrappers from the PATH
163-
orig_paths = os.getenv('PATH')
164-
filtered_paths = [path for path in orig_paths.split(':') if RPATH_WRAPPERS_SUBDIR not in path]
165-
setvar('PATH', ':'.join(filtered_paths))
166-
167-
tc.prepare_rpath_wrappers(
168-
rpath_filter_dirs=rpath_filter_dirs,
169-
rpath_include_dirs=rpath_include_dirs,
170-
wrappers_dir=targetdir,
171-
add_to_path=False,
172-
disable_wrapper_log=True,
173-
)
174-
_log.debug("Installed RPATH wrappers in command specific subdirectories of %s" % (str(targetdir)))
175-
176-
# Restore the PATH
177-
setvar('PATH', orig_paths)

test/framework/toolchain.py

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3137,61 +3137,31 @@ def test_toolchain_prepare_rpath(self):
31373137
self.assertTrue(os.path.samefile(res[1], fake_gxx))
31383138
self.assertFalse(any(os.path.samefile(x, fake_gxx) for x in res[2:]))
31393139

3140-
def test_export_rpath(self):
3141-
"""Test tools.toolchain.export_rpath_wrappers()"""
3140+
def test_toolchain_prepare_rpath_external(self):
3141+
"""Test toolchain.prepare under --rpath with --rpath-wrappers-dir"""
31423142

31433143
# put fake 'g++' command in place that just echos its arguments
31443144
fake_gxx = os.path.join(self.test_prefix, 'fake', 'g++')
31453145
write_file(fake_gxx, '#!/bin/bash\necho "$@"')
31463146
adjust_permissions(fake_gxx, stat.S_IXUSR)
31473147
os.environ['PATH'] = '%s:%s' % (os.path.join(self.test_prefix, 'fake'), os.getenv('PATH', ''))
31483148

3149+
# export the wrappers to a target location
3150+
target_wrapper_dir = os.path.join(self.test_prefix, 'target')
31493151
# enable --rpath for a toolchain so we test against it
3150-
init_config(build_options={'rpath': True, 'silent': True})
3152+
init_config(build_options={'rpath': True, 'silent': True, 'rpath_wrappers_dir': target_wrapper_dir})
31513153
tc = self.get_toolchain('gompi', version='2018a')
31523154
tc.set_options({'rpath': True})
31533155
# allow the underlying toolchain to be in a prepared state (which may include rpath wrapping)
31543156
tc.prepare()
31553157

3156-
# export the wrappers to a target location
3157-
target_wrapper_dir = os.path.join(self.test_prefix, 'target')
3158-
export_rpath_wrappers(targetdir=target_wrapper_dir, toolchain_name='gompi', toolchain_version='2018a',
3159-
rpath_filter_dirs=['/filter_path'], rpath_include_dirs=['/include_path'])
3160-
31613158
# check that wrapper was created
31623159
target_wrapper = os.path.join(target_wrapper_dir, RPATH_WRAPPERS_SUBDIR, 'gxx_wrapper', 'g++')
31633160
self.assertTrue(os.path.exists(target_wrapper))
31643161
# Make sure it is a wrapper
31653162
self.assertTrue(b'rpath_args.py $CMD' in read_file(target_wrapper, mode='rb'))
31663163
# Make sure it wraps our fake 'g++'
31673164
self.assertTrue(fake_gxx.encode(encoding="utf-8") in read_file(target_wrapper, mode='rb'))
3168-
# Make sure the wrapper is not in PATH (we export only)
3169-
self.assertFalse(any(os.path.samefile(x, target_wrapper) for x in which('g++', retain_all=True)))
3170-
3171-
# check whether fake g++ was wrapped and that arguments are what they should be
3172-
# no -rpath for /path because of rpath filter
3173-
mkdir(os.path.join(self.test_prefix, 'foo'), parents=True)
3174-
cmd = ' '.join([
3175-
target_wrapper,
3176-
'${USER}.c',
3177-
'-L%s/foo' % self.test_prefix,
3178-
'-L/filter_path',
3179-
"'$FOO'",
3180-
'-DX="\\"\\""',
3181-
])
3182-
res = run_shell_cmd(cmd, hidden=True)
3183-
self.assertEqual(res.exit_code, 0)
3184-
expected = ' '.join([
3185-
'-Wl,-rpath=/include_path',
3186-
'-Wl,--disable-new-dtags',
3187-
'-Wl,-rpath=%s/foo' % self.test_prefix,
3188-
'%(user)s.c',
3189-
'-L%s/foo' % self.test_prefix,
3190-
'-L/filter_path',
3191-
'$FOO',
3192-
'-DX=""',
3193-
])
3194-
self.assertEqual(res.output.strip(), expected % {'user': os.getenv('USER')})
31953165

31963166
def test_prepare_openmpi_tmpdir(self):
31973167
"""Test handling of long $TMPDIR path for OpenMPI 2.x"""

0 commit comments

Comments
 (0)