mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
In Python 3, subprocess output is read as binary data by default, which isn’t what we want. Instead of reading process output as byte strings, then manually decoding them into strings, simply pass `text=True` to functions in the `subprocess` module, so that we get properly decoded strings right out the box. This fixes places that forget to do the decoding step — most especially, the `update-checkout` script. That script prints Git output as byte strings, which leads to unreadable results. Additionally, in shell.run, use the same pipe for capturing both stdout and stderr. The distinction is pretty pointless in this use case; however, keeping the two channels separate means that we lose the original ordering of printed messages, which does matter.
88 lines
3.5 KiB
Python
Executable File
88 lines
3.5 KiB
Python
Executable File
#!/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],
|
|
text=True)
|
|
|
|
# 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:]))
|