Skip to content

Commit

Permalink
Fixup library paths for executables
Browse files Browse the repository at this point in the history
Executables need to be fixed up to find where the libraries built
in the drake project are. Similarly to what is done for the libraries, on MacOS
the executable dependency paths are updated to correspond to the install tree
paths, and on Linux a relative RPATH is added to each executable to point to
the directory(ies) in which the libraries they depend on are.
  • Loading branch information
Francois Budin committed Dec 8, 2017
1 parent c84bceb commit 4b05960
Showing 1 changed file with 35 additions and 17 deletions.
52 changes: 35 additions & 17 deletions tools/install/install.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ list_only = False
# as a suffix (e.g. my_lib.so.x.y.z).
dylib_match = r"(.*\.so)(\.\d+)*$|(.*\.dylib)$"

def is_executable(dst):
# Checking file type with command `file` is the safest way to find
# executables. Files without an extension are likely to be executables, but
# it is not always the case.
file_output = check_output(["file", dst])
# On Linux, executables can be ELF shared objects.
executable_match = r"(.*ELF.*(executable|shared object).*)|(Mach-O.*executable.*)"
re_result = re.match(executable_match, file_output)
if re_result is not None:
return True
else:
return False

def needs_install(src, dst):
# Get canonical destination.
Expand Down Expand Up @@ -64,7 +76,7 @@ def install(src, dst):
else:
print("[Up to date] %s" % dst)
re_result = re.match(dylib_match, dst)
if re_result is not None:
if re_result is not None: # It is a library
basename = os.path.basename(re_result.group(0))
# Check that dependency is only referenced once
# in the library dictionary. If it is referenced multiple times,
Expand All @@ -74,27 +86,32 @@ def install(src, dst):
"Multiple installation rules found for %s." % (basename))
sys.exit(1)
libs[basename] = (dst_full, installed)
else: # It is not a library, it may be an executable.
if is_executable(dst_full): # It is an executable.
libs[dst_full] = (dst_full, installed)
else: # Neither a library nor an executable.
pass


def fix_libraries_rpaths():
# Only fix libraries that are installed now.
def fix_rpaths():
# Only fix files that are installed now.
fix_libs = [(k, libs[k][0]) for k in libs.keys() if libs[k][1]]
for basename, dst_full in fix_libs:
# Enable write permissions to allow modification.
os.chmod(dst_full, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
if sys.platform == "darwin":
macos_fix_libraries_rpaths(basename, dst_full)
macos_fix_rpaths(basename, dst_full)
else:
linux_fix_libraries_rpaths(dst_full)
linux_fix_rpaths(dst_full)


def macos_fix_libraries_rpaths(basename, dst_full):
# Update library ID (remove relative path).
def macos_fix_rpaths(basename, dst_full):
# Update file (library, executable) ID (remove relative path).
check_call(
['install_name_tool', "-id", "@rpath/" + basename, dst_full]
)
# Check if library dependencies are specified with relative paths.
# Check if file dependencies are specified with relative paths.
file_output = check_output(["otool", "-L", dst_full])
for line in file_output.splitlines():
# keep only file path, remove version information.
Expand All @@ -103,7 +120,8 @@ def macos_fix_libraries_rpaths(basename, dst_full):
if "@loader_path" not in relative_path:
continue
dep_basename = os.path.basename(relative_path)
# Look for the absolute path in the dictionary of libraries.
# Look for the absolute path in the dictionary of fixup files to
# find library paths.
if dep_basename not in libs.keys():
continue
lib_dirname = os.path.dirname(dst_full)
Expand All @@ -128,8 +146,8 @@ def macos_fix_libraries_rpaths(basename, dst_full):
)


def linux_fix_libraries_rpaths(dst_full):
# Check that library has an rpath or a runpath tag
def linux_fix_rpaths(dst_full):
# Check that file has an rpath or a runpath tag
try:
check_output(["chrpath", "-l", dst_full])
except OSError as ex:
Expand All @@ -142,15 +160,15 @@ def linux_fix_libraries_rpaths(dst_full):
# Cannot be modified with `chrpath`, so we skip it.
return
raise ex
# Check if library dependencies are found and not in install prefix.
# The found libraries outside of the install prefix are the system
# Check if file dependencies are found and not in install prefix.
# The found libraries outside of the install prefix are system
# libraries and we do not worry about these. We only make sure that
# the installed libraries would be found.
# drake libraries will be found.
file_output = check_output(["ldd", dst_full])
rpath = set()
for line in file_output.splitlines():
ldd_result = line.strip().split(' => ')
# If library found, then skip.
# If library found and not in install prefix, then skip.
if len(ldd_result) < 2 or \
not (ldd_result[1] == "not found"
or ldd_result[1].startswith(prefix)):
Expand Down Expand Up @@ -244,8 +262,8 @@ def main(args):
# Execute the install actions.
<<actions>>

# Libraries paths may need to be updated.
fix_libraries_rpaths()
# Libraries paths may need to be updated in libraries and executables.
fix_rpaths()


if __name__ == "__main__":
Expand Down

0 comments on commit 4b05960

Please sign in to comment.