Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test the just-built dylibs when building unittests on Darwin
- Loading branch information
Showing
2 changed files
with
91 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
#!/usr/bin/env python | ||
|
||
# On Darwin, dynamic libraries have an install name. At link time, the | ||
# linker can work with a dylib anywhere in the filesystem, but it will | ||
# write the dylib's install name into the resulting image, and at load | ||
# time that dylib will normally be expected to be found at exactly that | ||
# path. However, if the install name in an image begins with `@rpath`, | ||
# it will instead be searched for in the image's runtime search path | ||
# list. That list may contain absolute paths, but it may also contain | ||
# paths beginning with `@executable_path` or `@loader_path`, meaning the | ||
# path containing the running executable or the image being loaded, | ||
# respectively. | ||
# | ||
# Many of Swift's dylibs are meant to be installed on the system, which | ||
# means they have install names like this: | ||
# /usr/lib/swift/libswiftFoo.dylib | ||
# To support back-deployment, they also provide magic override symbols | ||
# ($ld$install_name) for all the OS versions preceding the addition of | ||
# of the library. When the linker finds a dylib with a matching override | ||
# for the OS deployment target, it ignores the normal install name and | ||
# uses the override path in the linked image's load command. Swift's | ||
# libraries use override paths that begin with `@rpath`, and Swift | ||
# builds images with a runtime search path list that starts with | ||
# /usr/lib/swift but then falls back on a path relative to the image; | ||
# thus, apps will use the system libraries if available but will | ||
# otherwise use fallback libraries. | ||
# | ||
# When we're working on Swift, we usually want to test the libraries | ||
# we just built rather than the system libraries. There are two ways | ||
# to achieve that. The first is to override dyld's runtime search path | ||
# with DYLD_LIBRARY_PATH; this will take precedence over even an | ||
# absolute install name. The second is to make sure the dylibs are | ||
# loaded via an @rpath install name and then link the program with an | ||
# rpath that will use the just-built libraries. Unfortunately, the | ||
# toolchain will ordinarily use an absolute install name instead of | ||
# an @rpath if the deployment target is old enough, subverting testing. | ||
# | ||
# This script looks for dependent dylibs with an absolute path in | ||
# /usr/lib/swift and changes them to use @rpath. | ||
|
||
import argparse | ||
import re | ||
import subprocess | ||
import sys | ||
|
||
|
||
def main(arguments): | ||
parser = argparse.ArgumentParser( | ||
description='Change absolute install names to use @rpath') | ||
parser.add_argument('bin', help='the binary') | ||
|
||
args = parser.parse_args(arguments) | ||
rpathize(args.bin) | ||
|
||
|
||
def rpathize(filename): | ||
dylibsOutput = subprocess.check_output( | ||
['xcrun', 'dyldinfo', '-dylibs', filename]) | ||
|
||
# The output from dyldinfo -dylibs is a line of header followed by one | ||
# install name per line, indented with spaces. | ||
dylib_regex = re.compile( | ||
r"^\s*(?P<path>/usr/lib/swift/(?P<filename>.*\.dylib))\s*$") | ||
|
||
# Build a command to invoke install_name_tool. | ||
command = ['install_name_tool'] | ||
for line in dylibsOutput.splitlines(): | ||
match = dylib_regex.match(line) | ||
if match: | ||
command.append('-change') | ||
command.append(match.group('path')) | ||
command.append('@rpath/' + match.group('filename')) | ||
continue | ||
|
||
# Don't run the command if we didn't find any dylibs to change: | ||
# it's invalid to invoke install_name_tool without any operations. | ||
if len(command) == 1: | ||
return | ||
|
||
# The last argument is the filename to operate on. | ||
command.append(filename) | ||
|
||
subprocess.check_call(command) | ||
|
||
|
||
sys.exit(main(sys.argv[1:])) |