def test_find_eb_script(self):
     """Test find_eb_script function."""
     self.assertTrue(os.path.exists(ft.find_eb_script('rpath_args.py')))
     self.assertTrue(
         os.path.exists(ft.find_eb_script('rpath_wrapper_template.sh.in')))
     self.assertErrorRegex(EasyBuildError,
                           "Script 'no_such_script' not found",
                           ft.find_eb_script, 'no_such_script')
예제 #2
0
    def prepare_rpath_wrappers(self, rpath_filter_dirs=None):
        """
        Put RPATH wrapper script in place for compiler and linker commands

        :param rpath_filter_dirs: extra directories to include in RPATH filter (e.g. build dir, tmpdir, ...)
        """
        self.log.experimental(
            "Using wrapper scripts for compiler/linker commands that enforce RPATH linking"
        )

        if get_os_type() == LINUX:
            self.log.info("Putting RPATH wrappers in place...")
        else:
            raise EasyBuildError(
                "RPATH linking is currently only supported on Linux")

        wrapper_dir = os.path.join(tempfile.mkdtemp(), RPATH_WRAPPERS_SUBDIR)

        # must also wrap compilers commands, required e.g. for Clang ('gcc' on OS X)?
        c_comps, fortran_comps = self.compilers()

        rpath_args_py = find_eb_script('rpath_args.py')
        rpath_wrapper_template = find_eb_script('rpath_wrapper_template.sh.in')

        # prepend location to wrappers to $PATH
        setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))

        # figure out list of patterns to use in rpath filter
        rpath_filter = build_option('rpath_filter')
        if rpath_filter is None:
            rpath_filter = ['/lib.*', '/usr.*']
            self.log.debug(
                "No general RPATH filter specified, falling back to default: %s",
                rpath_filter)
        rpath_filter = ','.join(rpath_filter +
                                ['%s.*' % d for d in rpath_filter_dirs or []])
        self.log.debug("Combined RPATH filter: '%s'" % rpath_filter)

        # create wrappers
        for cmd in nub(c_comps + fortran_comps + ['ld', 'ld.gold']):
            orig_cmd = which(cmd)

            if orig_cmd:
                # bail out early if command already is a wrapped;
                # this may occur when building extensions
                if self.is_rpath_wrapper(orig_cmd):
                    self.log.info(
                        "%s already seems to be an RPATH wrapper script, not wrapping it again!",
                        orig_cmd)
                    continue

                cmd_wrapper = os.path.join(wrapper_dir, cmd)

                # make *very* sure we don't wrap around ourselves and create a fork bomb...
                if os.path.exists(cmd_wrapper) and os.path.exists(
                        orig_cmd) and os.path.samefile(orig_cmd, cmd_wrapper):
                    raise EasyBuildError(
                        "Refusing the create a fork bomb, which(%s) == %s",
                        cmd, orig_cmd)

                # enable debug mode in wrapper script by specifying location for log file
                if build_option('debug'):
                    rpath_wrapper_log = os.path.join(
                        tempfile.gettempdir(), 'rpath_wrapper_%s.log' % cmd)
                else:
                    rpath_wrapper_log = '/dev/null'

                # complete template script and put it in place
                cmd_wrapper_txt = read_file(rpath_wrapper_template) % {
                    'orig_cmd': orig_cmd,
                    'python': sys.executable,
                    'rpath_args_py': rpath_args_py,
                    'rpath_filter': rpath_filter,
                    'rpath_wrapper_log': rpath_wrapper_log,
                }
                write_file(cmd_wrapper, cmd_wrapper_txt)
                adjust_permissions(cmd_wrapper, stat.S_IXUSR)
                self.log.info("Wrapper script for %s: %s (log: %s)", orig_cmd,
                              which(cmd), rpath_wrapper_log)
            else:
                self.log.debug(
                    "Not installing RPATH wrapper for non-existing command '%s'",
                    cmd)
예제 #3
0
    def prepare_rpath_wrappers(self,
                               rpath_filter_dirs=None,
                               rpath_include_dirs=None):
        """
        Put RPATH wrapper script in place for compiler and linker commands

        :param rpath_filter_dirs: extra directories to include in RPATH filter (e.g. build dir, tmpdir, ...)
        """
        if get_os_type() == LINUX:
            self.log.info("Putting RPATH wrappers in place...")
        else:
            raise EasyBuildError(
                "RPATH linking is currently only supported on Linux")

        if rpath_filter_dirs is None:
            rpath_filter_dirs = []

        # always include filter for 'stubs' library directory,
        # cfr. https://github.com/easybuilders/easybuild-framework/issues/2683
        rpath_filter_dirs.append('.*/lib(64)?/stubs/?')

        # directory where all wrappers will be placed
        wrappers_dir = os.path.join(tempfile.mkdtemp(), RPATH_WRAPPERS_SUBDIR)

        # must also wrap compilers commands, required e.g. for Clang ('gcc' on OS X)?
        c_comps, fortran_comps = self.compilers()

        rpath_args_py = find_eb_script('rpath_args.py')
        rpath_wrapper_template = find_eb_script('rpath_wrapper_template.sh.in')

        # figure out list of patterns to use in rpath filter
        rpath_filter = build_option('rpath_filter')
        if rpath_filter is None:
            rpath_filter = ['/lib.*', '/usr.*']
            self.log.debug(
                "No general RPATH filter specified, falling back to default: %s",
                rpath_filter)
        rpath_filter = ','.join(rpath_filter +
                                ['%s.*' % d for d in rpath_filter_dirs])
        self.log.debug("Combined RPATH filter: '%s'", rpath_filter)

        rpath_include = ','.join(rpath_include_dirs or [])
        self.log.debug("Combined RPATH include paths: '%s'", rpath_include)

        # create wrappers
        for cmd in nub(c_comps + fortran_comps + ['ld', 'ld.gold', 'ld.bfd']):
            orig_cmd = which(cmd)

            if orig_cmd:
                # bail out early if command already is a wrapped;
                # this may occur when building extensions
                if self.is_rpath_wrapper(orig_cmd):
                    self.log.info(
                        "%s already seems to be an RPATH wrapper script, not wrapping it again!",
                        orig_cmd)
                    continue

                # determine location for this wrapper
                # each wrapper is placed in its own subdirectory to enable $PATH filtering per wrapper separately
                # avoid '+' character in directory name (for example with 'g++' command), which can cause trouble
                # (see https://github.com/easybuilders/easybuild-easyconfigs/issues/7339)
                wrapper_dir_name = '%s_wrapper' % cmd.replace('+', 'x')
                wrapper_dir = os.path.join(wrappers_dir, wrapper_dir_name)

                cmd_wrapper = os.path.join(wrapper_dir, cmd)

                # make *very* sure we don't wrap around ourselves and create a fork bomb...
                if os.path.exists(cmd_wrapper) and os.path.exists(
                        orig_cmd) and os.path.samefile(orig_cmd, cmd_wrapper):
                    raise EasyBuildError(
                        "Refusing the create a fork bomb, which(%s) == %s",
                        cmd, orig_cmd)

                # enable debug mode in wrapper script by specifying location for log file
                if build_option('debug'):
                    rpath_wrapper_log = os.path.join(
                        tempfile.gettempdir(), 'rpath_wrapper_%s.log' % cmd)
                else:
                    rpath_wrapper_log = '/dev/null'

                # complete template script and put it in place
                cmd_wrapper_txt = read_file(rpath_wrapper_template) % {
                    'orig_cmd': orig_cmd,
                    'python': sys.executable,
                    'rpath_args_py': rpath_args_py,
                    'rpath_filter': rpath_filter,
                    'rpath_include': rpath_include,
                    'rpath_wrapper_log': rpath_wrapper_log,
                    'wrapper_dir': wrapper_dir,
                }
                write_file(cmd_wrapper, cmd_wrapper_txt)
                adjust_permissions(cmd_wrapper, stat.S_IXUSR)
                self.log.info("Wrapper script for %s: %s (log: %s)", orig_cmd,
                              which(cmd), rpath_wrapper_log)

                # prepend location to this wrapper to $PATH
                setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))
            else:
                self.log.debug(
                    "Not installing RPATH wrapper for non-existing command '%s'",
                    cmd)
예제 #4
0
    def test_rpath_args_script(self):
        """Test rpath_args.py script"""
        script = find_eb_script('rpath_args.py')

        # simplest possible compiler command
        out, ec = run_cmd("%s gcc '' -c foo.c" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-Wl,-rpath=$ORIGIN/../lib'",
            "'-Wl,-rpath=$ORIGIN/../lib64'",
            "'-Wl,--disable-new-dtags'",
            "'-c'",
            "'foo.c'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # linker command, --enable-new-dtags should be filtered out
        out, ec = run_cmd("%s ld '' --enable-new-dtags foo.o" % script, simple=False)
        self.assertEqual(ec, 0)
        expected = '\n'.join([
            "CMD_ARGS=('foo.o')",
            "RPATH_ARGS='--disable-new-dtags -rpath=$ORIGIN/../lib -rpath=$ORIGIN/../lib64'",
            ''
        ])
        cmd_args = [
            "'-rpath=$ORIGIN/../lib'",
            "'-rpath=$ORIGIN/../lib64'",
            "'--disable-new-dtags'",
            "'foo.o'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # test passing no arguments
        out, ec = run_cmd("%s gcc ''" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-Wl,-rpath=$ORIGIN/../lib'",
            "'-Wl,-rpath=$ORIGIN/../lib64'",
            "'-Wl,--disable-new-dtags'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # test passing a single empty argument
        out, ec = run_cmd("%s ld.gold '' ''" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-rpath=$ORIGIN/../lib'",
            "'-rpath=$ORIGIN/../lib64'",
            "'--disable-new-dtags'",
            "''",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # single -L argument
        out, ec = run_cmd("%s gcc '' foo.c -L/foo -lfoo" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-Wl,-rpath=$ORIGIN/../lib'",
            "'-Wl,-rpath=$ORIGIN/../lib64'",
            "'-Wl,--disable-new-dtags'",
            "'-Wl,-rpath=/foo'",
            "'foo.c'",
            "'-L/foo'",
            "'-lfoo'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # relative paths passed to -L are *not* RPATH'ed in
        out, ec = run_cmd("%s gcc '' foo.c -L../lib -lfoo" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-Wl,-rpath=$ORIGIN/../lib'",
            "'-Wl,-rpath=$ORIGIN/../lib64'",
            "'-Wl,--disable-new-dtags'",
            "'foo.c'",
            "'-L../lib'",
            "'-lfoo'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # single -L argument, with value separated by a space
        out, ec = run_cmd("%s gcc '' foo.c -L   /foo -lfoo" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-Wl,-rpath=$ORIGIN/../lib'",
            "'-Wl,-rpath=$ORIGIN/../lib64'",
            "'-Wl,--disable-new-dtags'",
            "'-Wl,-rpath=/foo'",
            "'foo.c'",
            "'-L/foo'",
            "'-lfoo'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # multiple -L arguments, order should be preserved
        out, ec = run_cmd("%s ld '' -L/foo foo.o -L/lib64 -lfoo -lbar -L/usr/lib -L/bar" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-rpath=$ORIGIN/../lib'",
            "'-rpath=$ORIGIN/../lib64'",
            "'--disable-new-dtags'",
            "'-rpath=/foo'",
            "'-rpath=/lib64'",
            "'-rpath=/usr/lib'",
            "'-rpath=/bar'",
            "'-L/foo'",
            "'foo.o'",
            "'-L/lib64'",
            "'-lfoo'",
            "'-lbar'",
            "'-L/usr/lib'",
            "'-L/bar'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # test specifying of custom rpath filter
        out, ec = run_cmd("%s ld '/fo.*,/bar.*' -L/foo foo.o -L/lib64 -lfoo -L/bar -lbar" % script, simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-rpath=$ORIGIN/../lib'",
            "'-rpath=$ORIGIN/../lib64'",
            "'--disable-new-dtags'",
            "'-rpath=/lib64'",
            "'-L/foo'",
            "'foo.o'",
            "'-L/lib64'",
            "'-lfoo'",
            "'-L/bar'",
            "'-lbar'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # slightly trimmed down real-life example (compilation of XZ)
        args = ' '.join([
            '-fvisibility=hidden',
            '-Wall',
            '-O2',
            '-xHost',
            '-o .libs/lzmainfo',
            'lzmainfo-lzmainfo.o lzmainfo-tuklib_progname.o lzmainfo-tuklib_exit.o',
            '-L/icc/lib/intel64',
            '-L/imkl/lib',
            '-L/imkl/mkl/lib/intel64',
            '-L/gettext/lib',
            '../../src/liblzma/.libs/liblzma.so',
            '-lrt -liomp5 -lpthread',
            '-Wl,-rpath',
            '-Wl,/example/software/XZ/5.2.2-intel-2016b/lib',
        ])
        out, ec = run_cmd("%s icc '' %s" % (script, args), simple=False)
        self.assertEqual(ec, 0)
        cmd_args = [
            "'-Wl,-rpath=$ORIGIN/../lib'",
            "'-Wl,-rpath=$ORIGIN/../lib64'",
            "'-Wl,--disable-new-dtags'",
            "'-Wl,-rpath=/icc/lib/intel64'",
            "'-Wl,-rpath=/imkl/lib'",
            "'-Wl,-rpath=/imkl/mkl/lib/intel64'",
            "'-Wl,-rpath=/gettext/lib'",
            "'-fvisibility=hidden'",
            "'-Wall'",
            "'-O2'",
            "'-xHost'",
            "'-o' '.libs/lzmainfo'",
            "'lzmainfo-lzmainfo.o' 'lzmainfo-tuklib_progname.o' 'lzmainfo-tuklib_exit.o'",
            "'-L/icc/lib/intel64'",
            "'-L/imkl/lib'",
            "'-L/imkl/mkl/lib/intel64'",
            "'-L/gettext/lib'",
            "'../../src/liblzma/.libs/liblzma.so'",
            "'-lrt' '-liomp5' '-lpthread'",
            "'-Wl,-rpath'",
            "'-Wl,/example/software/XZ/5.2.2-intel-2016b/lib'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # trimmed down real-life example involving quotes and escaped quotes (compilation of GCC)
        args = [
            '-DHAVE_CONFIG_H',
            '-I.',
            '-Ibuild',
            '-I../../gcc',
            '-DBASEVER="\\"5.4.0\\""',
            '-DDATESTAMP="\\"\\""',
            '-DPKGVERSION="\\"(GCC) \\""',
            '-DBUGURL="\\"<http://gcc.gnu.org/bugs.html>\\""',
            '-o build/version.o',
            '../../gcc/version.c',
        ]
        cmd = "%s g++ '' %s" % (script, ' '.join(args))
        out, ec = run_cmd(cmd, simple=False)
        self.assertEqual(ec, 0)

        cmd_args = [
            "'-Wl,-rpath=$ORIGIN/../lib'",
            "'-Wl,-rpath=$ORIGIN/../lib64'",
            "'-Wl,--disable-new-dtags'",
            "'-DHAVE_CONFIG_H'",
            "'-I.'",
            "'-Ibuild'",
            "'-I../../gcc'",
            "'-DBASEVER=\"5.4.0\"'",
            "'-DDATESTAMP=\"\"'",
            "'-DPKGVERSION=\"(GCC) \"'",
            "'-DBUGURL=\"<http://gcc.gnu.org/bugs.html>\"'",
            "'-o' 'build/version.o'",
            "'../../gcc/version.c'",
        ]
        self.assertEqual(out.strip(), "CMD_ARGS=(%s)" % ' '.join(cmd_args))

        # verify that no -rpath arguments are injected when command is run in 'version check' mode
        cmd = "%s g++ '' -v" % script
        out, ec = run_cmd(cmd, simple=False)
        self.assertEqual(ec, 0)
        self.assertEqual(out.strip(), "CMD_ARGS=('-v')")
예제 #5
0
 def test_find_eb_script(self):
     """Test find_eb_script function."""
     self.assertTrue(os.path.exists(ft.find_eb_script('rpath_args.py')))
     self.assertTrue(os.path.exists(ft.find_eb_script('rpath_wrapper_template.sh.in')))
     self.assertErrorRegex(EasyBuildError, "Script 'no_such_script' not found", ft.find_eb_script, 'no_such_script')
예제 #6
0
    def prepare_rpath_wrappers(self, rpath_filter_dirs=None):
        """
        Put RPATH wrapper script in place for compiler and linker commands

        :param rpath_filter_dirs: extra directories to include in RPATH filter (e.g. build dir, tmpdir, ...)
        """
        self.log.experimental("Using wrapper scripts for compiler/linker commands that enforce RPATH linking")

        if get_os_type() == LINUX:
            self.log.info("Putting RPATH wrappers in place...")
        else:
            raise EasyBuildError("RPATH linking is currently only supported on Linux")

        wrapper_dir = os.path.join(tempfile.mkdtemp(), RPATH_WRAPPERS_SUBDIR)

        # must also wrap compilers commands, required e.g. for Clang ('gcc' on OS X)?
        c_comps, fortran_comps = self.compilers()

        rpath_args_py = find_eb_script('rpath_args.py')
        rpath_wrapper_template = find_eb_script('rpath_wrapper_template.sh.in')

        # prepend location to wrappers to $PATH
        setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))

        # figure out list of patterns to use in rpath filter
        rpath_filter = build_option('rpath_filter')
        if rpath_filter is None:
            rpath_filter = ['/lib.*', '/usr.*']
            self.log.debug("No general RPATH filter specified, falling back to default: %s", rpath_filter)
        rpath_filter = ','.join(rpath_filter + ['%s.*' % d for d in rpath_filter_dirs or []])
        self.log.debug("Combined RPATH filter: '%s'" % rpath_filter)

        # create wrappers
        for cmd in nub(c_comps + fortran_comps + ['ld', 'ld.gold']):
            orig_cmd = which(cmd)

            if orig_cmd:
                # bail out early if command already is a wrapped;
                # this may occur when building extensions
                if self.is_rpath_wrapper(orig_cmd):
                    self.log.info("%s already seems to be an RPATH wrapper script, not wrapping it again!", orig_cmd)
                    continue

                cmd_wrapper = os.path.join(wrapper_dir, cmd)

                # make *very* sure we don't wrap around ourselves and create a fork bomb...
                if os.path.exists(cmd_wrapper) and os.path.exists(orig_cmd) and os.path.samefile(orig_cmd, cmd_wrapper):
                    raise EasyBuildError("Refusing the create a fork bomb, which(%s) == %s", cmd, orig_cmd)

                # enable debug mode in wrapper script by specifying location for log file
                if build_option('debug'):
                    rpath_wrapper_log = os.path.join(tempfile.gettempdir(), 'rpath_wrapper_%s.log' % cmd)
                else:
                    rpath_wrapper_log = '/dev/null'

                # complete template script and put it in place
                cmd_wrapper_txt = read_file(rpath_wrapper_template) % {
                    'orig_cmd': orig_cmd,
                    'python': sys.executable,
                    'rpath_args_py': rpath_args_py,
                    'rpath_filter': rpath_filter,
                    'rpath_wrapper_log': rpath_wrapper_log,
                }
                write_file(cmd_wrapper, cmd_wrapper_txt)
                adjust_permissions(cmd_wrapper, stat.S_IXUSR)
                self.log.info("Wrapper script for %s: %s (log: %s)", orig_cmd, which(cmd), rpath_wrapper_log)
            else:
                self.log.debug("Not installing RPATH wrapper for non-existing command '%s'", cmd)
예제 #7
0
    def prepare_rpath_wrappers(self, rpath_filter_dirs=None, rpath_include_dirs=None):
        """
        Put RPATH wrapper script in place for compiler and linker commands

        :param rpath_filter_dirs: extra directories to include in RPATH filter (e.g. build dir, tmpdir, ...)
        """
        if get_os_type() == LINUX:
            self.log.info("Putting RPATH wrappers in place...")
        else:
            raise EasyBuildError("RPATH linking is currently only supported on Linux")

        if rpath_filter_dirs is None:
            rpath_filter_dirs = []

        # always include filter for 'stubs' library directory,
        # cfr. https://github.com/easybuilders/easybuild-framework/issues/2683
        rpath_filter_dirs.append('.*/lib(64)?/stubs/?')

        # directory where all wrappers will be placed
        wrappers_dir = os.path.join(tempfile.mkdtemp(), RPATH_WRAPPERS_SUBDIR)

        # must also wrap compilers commands, required e.g. for Clang ('gcc' on OS X)?
        c_comps, fortran_comps = self.compilers()

        rpath_args_py = find_eb_script('rpath_args.py')
        rpath_wrapper_template = find_eb_script('rpath_wrapper_template.sh.in')

        # figure out list of patterns to use in rpath filter
        rpath_filter = build_option('rpath_filter')
        if rpath_filter is None:
            rpath_filter = ['/lib.*', '/usr.*']
            self.log.debug("No general RPATH filter specified, falling back to default: %s", rpath_filter)
        rpath_filter = ','.join(rpath_filter + ['%s.*' % d for d in rpath_filter_dirs])
        self.log.debug("Combined RPATH filter: '%s'", rpath_filter)

        rpath_include = ','.join(rpath_include_dirs or [])
        self.log.debug("Combined RPATH include paths: '%s'", rpath_include)

        # create wrappers
        for cmd in nub(c_comps + fortran_comps + ['ld', 'ld.gold', 'ld.bfd']):
            orig_cmd = which(cmd)

            if orig_cmd:
                # bail out early if command already is a wrapped;
                # this may occur when building extensions
                if self.is_rpath_wrapper(orig_cmd):
                    self.log.info("%s already seems to be an RPATH wrapper script, not wrapping it again!", orig_cmd)
                    continue

                # determine location for this wrapper
                # each wrapper is placed in its own subdirectory to enable $PATH filtering per wrapper separately
                # avoid '+' character in directory name (for example with 'g++' command), which can cause trouble
                # (see https://github.com/easybuilders/easybuild-easyconfigs/issues/7339)
                wrapper_dir_name = '%s_wrapper' % cmd.replace('+', 'x')
                wrapper_dir = os.path.join(wrappers_dir, wrapper_dir_name)

                cmd_wrapper = os.path.join(wrapper_dir, cmd)

                # make *very* sure we don't wrap around ourselves and create a fork bomb...
                if os.path.exists(cmd_wrapper) and os.path.exists(orig_cmd) and os.path.samefile(orig_cmd, cmd_wrapper):
                    raise EasyBuildError("Refusing the create a fork bomb, which(%s) == %s", cmd, orig_cmd)

                # enable debug mode in wrapper script by specifying location for log file
                if build_option('debug'):
                    rpath_wrapper_log = os.path.join(tempfile.gettempdir(), 'rpath_wrapper_%s.log' % cmd)
                else:
                    rpath_wrapper_log = '/dev/null'

                # complete template script and put it in place
                cmd_wrapper_txt = read_file(rpath_wrapper_template) % {
                    'orig_cmd': orig_cmd,
                    'python': sys.executable,
                    'rpath_args_py': rpath_args_py,
                    'rpath_filter': rpath_filter,
                    'rpath_include': rpath_include,
                    'rpath_wrapper_log': rpath_wrapper_log,
                    'wrapper_dir': wrapper_dir,
                }
                write_file(cmd_wrapper, cmd_wrapper_txt)
                adjust_permissions(cmd_wrapper, stat.S_IXUSR)
                self.log.info("Wrapper script for %s: %s (log: %s)", orig_cmd, which(cmd), rpath_wrapper_log)

                # prepend location to this wrapper to $PATH
                setvar('PATH', '%s:%s' % (wrapper_dir, os.getenv('PATH')))
            else:
                self.log.debug("Not installing RPATH wrapper for non-existing command '%s'", cmd)