Skip to content

Commit 5487954

Browse files
committed
Squash and rebase for 5.0.x branch
1 parent 6ce366d commit 5487954

File tree

3 files changed

+92
-7
lines changed

3 files changed

+92
-7
lines changed

easybuild/tools/toolchain/toolchain.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -975,7 +975,8 @@ def is_rpath_wrapper(path):
975975
# need to use binary mode to read the file, since it may be an actual compiler command (which is a binary file)
976976
return b'rpath_args.py $CMD' in read_file(path, mode='rb')
977977

978-
def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None):
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):
979980
"""
980981
Put RPATH wrapper script in place for compiler and linker commands
981982
@@ -998,7 +999,11 @@ def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None
998999
rpath_filter_dirs.append(lib_stubs_pattern)
9991000

10001001
# directory where all wrappers will be placed
1001-
wrappers_dir = os.path.join(tempfile.mkdtemp(), RPATH_WRAPPERS_SUBDIR)
1002+
if wrappers_dir is None:
1003+
wrappers_dir = os.path.join(tempfile.mkdtemp(), RPATH_WRAPPERS_SUBDIR)
1004+
else:
1005+
if not os.path.exists(wrappers_dir):
1006+
os.mkdir(wrappers_dir)
10021007

10031008
# must also wrap compilers commands, required e.g. for Clang ('gcc' on OS X)?
10041009
c_comps, fortran_comps = self.compilers()
@@ -1045,7 +1050,7 @@ def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None
10451050
raise EasyBuildError("Refusing the create a fork bomb, which(%s) == %s", cmd, orig_cmd)
10461051

10471052
# enable debug mode in wrapper script by specifying location for log file
1048-
if build_option('debug'):
1053+
if build_option('debug') and not disable_wrapper_log:
10491054
rpath_wrapper_log = os.path.join(tempfile.gettempdir(), 'rpath_wrapper_%s.log' % cmd)
10501055
else:
10511056
rpath_wrapper_log = '/dev/null'
@@ -1061,10 +1066,11 @@ def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None
10611066
'wrapper_dir': wrapper_dir,
10621067
}
10631068
write_file(cmd_wrapper, cmd_wrapper_txt)
1064-
adjust_permissions(cmd_wrapper, stat.S_IXUSR)
1069+
adjust_permissions(cmd_wrapper, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
10651070

10661071
# prepend location to this wrapper to $PATH
1067-
setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))
1072+
if add_to_path:
1073+
setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))
10681074

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

easybuild/tools/toolchain/utilities.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
import easybuild.tools.toolchain
4343
from easybuild.base import fancylogger
4444
from easybuild.tools.build_log import EasyBuildError
45-
from easybuild.tools.toolchain.toolchain import Toolchain
45+
from easybuild.tools.environment import setvar
46+
from easybuild.tools.toolchain.toolchain import Toolchain, RPATH_WRAPPERS_SUBDIR
4647
from easybuild.tools.utilities import get_subclasses, import_available_modules, nub
4748

4849

@@ -151,3 +152,25 @@ def get_toolchain(tc, tcopts, mns=None, tcdeps=None, modtool=None):
151152
tc_inst.set_options(tcopts)
152153

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

test/framework/toolchain.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
from easybuild.tools.systemtools import get_shared_lib_ext
5656
from easybuild.tools.toolchain.mpi import get_mpi_cmd_template
5757
from easybuild.tools.toolchain.toolchain import env_vars_external_module
58-
from easybuild.tools.toolchain.utilities import get_toolchain, search_toolchain
58+
from easybuild.tools.toolchain.utilities import get_toolchain, search_toolchain, export_rpath_wrappers
5959
from easybuild.toolchains.compiler.clang import Clang
6060

6161
easybuild.tools.toolchain.compiler.systemtools.get_compiler_family = lambda: st.POWER
@@ -3137,6 +3137,62 @@ 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()"""
3142+
3143+
# put fake 'g++' command in place that just echos its arguments
3144+
fake_gxx = os.path.join(self.test_prefix, 'fake', 'g++')
3145+
write_file(fake_gxx, '#!/bin/bash\necho "$@"')
3146+
adjust_permissions(fake_gxx, stat.S_IXUSR)
3147+
os.environ['PATH'] = '%s:%s' % (os.path.join(self.test_prefix, 'fake'), os.getenv('PATH', ''))
3148+
3149+
# enable --rpath for a toolchain so we test against it
3150+
init_config(build_options={'rpath': True, 'silent': True})
3151+
tc = self.get_toolchain('gompi', version='2018a')
3152+
tc.set_options({'rpath': True})
3153+
# allow the underlying toolchain to be in a prepared state (which may include rpath wrapping)
3154+
tc.prepare()
3155+
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+
3161+
# check that wrapper was created
3162+
target_wrapper = os.path.join(target_wrapper_dir, 'gxx_wrapper', 'g++')
3163+
self.assertTrue(os.path.exists(target_wrapper))
3164+
# Make sure it is a wrapper
3165+
self.assertTrue(b'rpath_args.py $CMD' in read_file(target_wrapper, mode='rb'))
3166+
# Make sure it wraps our fake 'g++'
3167+
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')})
3195+
31403196
def test_prepare_openmpi_tmpdir(self):
31413197
"""Test handling of long $TMPDIR path for OpenMPI 2.x"""
31423198

0 commit comments

Comments
 (0)