forked from WebKit/WebKit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathextract-tbds-from-internal-sdk
executable file
·154 lines (134 loc) · 6.44 KB
/
extract-tbds-from-internal-sdk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env python3
import argparse
import os
import re
import shutil
import stat
import subprocess
import sys
import tempfile
parser = argparse.ArgumentParser(description='''\
This script looks at build products produced by an Apple internal build to
identify frameworks that WebKit uses which are not provided by a public
SDK. It copies TBD stubs of those frameworks from an internal SDK, and strips
them of symbols not used by the WebKit build. This process is manually run as
part of the open-source build bringup when new Xcode SDKs are released.
''')
group1 = parser.add_argument_group('Platform flags', '''
Pass standard WebKit platform and configuration arguments (e.g. --ios-simulator
--release) to select a build directory and SDK directories.
''')
group2 = parser.add_argument_group('Custom paths')
group2.add_argument('src_sdk', help='path to an internal SDK to extract framework stubs from', nargs='?')
group2.add_argument('dst_sdk', help='path to copy framework stubs to, typically in WebKitLibraries/SDKs', nargs='?')
group2.add_argument('--base-sdk', '-s', help='path to the public SDK that DST_SDK will be adding to')
group2.add_argument('--build-dir', '-b', help='directory to read WebKit binaries from')
opts, webkitdirs_args = parser.parse_known_args()
scripts_dir = os.path.dirname(sys.argv[0])
if not opts.build_dir:
opts.build_dir = subprocess.check_output(
['webkit-build-directory', *webkitdirs_args, '--configuration'],
text=True
).rstrip()
if not opts.base_sdk or not opts.src_sdk:
webkitdirs_sdk_name = subprocess.check_output(
['perl', f'-I{scripts_dir}', '-Mwebkitdirs', '-e', 'print xcodeSDK()', '--', *webkitdirs_args],
text=True
).rstrip()
if not opts.base_sdk:
opts.base_sdk = subprocess.check_output(
('xcrun', '--show-sdk-path', '--sdk', webkitdirs_sdk_name.removesuffix('.internal')),
text=True
).rstrip()
if not opts.src_sdk:
opts.src_sdk = subprocess.check_output(
('xcrun', '--show-sdk-path', '--sdk', webkitdirs_sdk_name),
text=True
).rstrip()
if opts.base_sdk == opts.src_sdk:
parser.error(f"Can't find a separate SRC_SDK and BASE_SDK for {webkitdirs_args}. "
"Either switch to an Xcode which has internal SDKs, or pass these paths manually as arguments.")
if not opts.dst_sdk:
major_release_sdk_name = re.sub(
r'(\d+)\.\d+', r'\1.0',
os.path.splitext(os.path.basename(opts.base_sdk))[0].lower()
)
opts.dst_sdk = os.path.normpath(f'{scripts_dir}/../../WebKitLibraries/SDKs/{major_release_sdk_name}-additions.sdk')
print(f'Using symbols referenced from build artifacts in ... {opts.build_dir}')
print(f'.... copying API stubs from ........................ {opts.src_sdk}')
print(f'.... for libraries not present in .................. {opts.base_sdk}')
print(f'Writing extracted API stubs to ..................... {opts.dst_sdk}')
print()
export_list_fd, export_list = tempfile.mkstemp(prefix='WebKit.exp')
def echo_system_commands(name, args):
if name == 'subprocess.Popen':
print('\033[1mRun\033[0m', *args[1])
elif name == 'shutil.copyfile':
print('\033[1mCopy\033[0m', args[0], '->', args[1])
elif name == 'os.symlink':
print('\033[1mSymlink\033[0m', args[1], '->', args[0])
sys.addaudithook(echo_system_commands)
# Select static archives and any Mach-O executables from the build directory.
executables = []
for dirpath, dirnames, filenames in os.walk(opts.build_dir):
for name in filenames:
path = f'{dirpath}/{name}'
if name.endswith('.a'):
executables.append(path)
continue
# Find any executable file, and check its first four bytes for a Mach-O magic number.
mode = os.lstat(path).st_mode
if not (stat.S_ISREG(mode) and mode & stat.S_IEXEC):
continue
mach_magic = open(path, 'rb').read(4)
if int.from_bytes(mach_magic, 'big') in (0xfeedface, 0xfeedfacf, 0xcafebabe, 0xcafebabf,
0xcefaedfe, 0xcffaedfe, 0xbebafeca, 0xbfbafeca):
executables.append(path)
if not executables:
raise SystemExit(f'no executables found in {opts.build_dir}')
# Using the build products given, find the dylibs in missing from the base SDK
# that we link against.
tbds = set()
otool = subprocess.Popen(['otool', '-XL'] + executables, text=True, stdout=subprocess.PIPE)
for line in otool.stdout:
framework, *_ = line.lstrip().partition(' ')
name = os.path.basename(framework)
stem, _ = os.path.splitext(name)
tbd = os.path.splitext(framework)[0] + '.tbd'
built_by_webkit_or_public_paths = (f'{opts.build_dir}/{name}',
f'{opts.build_dir}/{name}.framework',
f'{opts.base_sdk}/{tbd}')
if os.path.exists(f'{opts.src_sdk}/{tbd}') and not any(filter(os.path.exists, built_by_webkit_or_public_paths)):
tbds.add(tbd)
otool.wait()
if not tbds:
raise SystemExit(f'No TBDs matching linked-against frameworks found in {opts.src_sdk}')
# Create an export list from the build products scanned, which will included
# the needed symbols from these dylibs.
subprocess.check_call(['nm', '-gj'] + executables, stdout=export_list_fd)
# Copy TBDs for the identified dylibs to the sparse SDK.
copied_tbds = []
for tbd in tbds:
src_tbd = f'{opts.src_sdk}/{tbd}'
dst_tbd = f'{opts.dst_sdk}/{tbd}'
os.makedirs(os.path.dirname(dst_tbd), exist_ok=True)
shutil.copy(src_tbd, dst_tbd)
copied_tbds.append(dst_tbd)
framework, versions, versioned_tbd = tbd.partition('/Versions/')
if versions == '/Versions/':
# Preserve the versioned bundle structure if it exists.
version, name = os.path.split(versioned_tbd)
for src, symlink_dst in (
# e.g. "Versions/Current/IOKit.tbd", "<sdk>/S/L/F/IOKit.framework/IOKit.tbd"
(f'Versions/Current/{name}', f'{opts.dst_sdk}/{framework}/{name}'),
# e.g. "A", "<sdk>/S/L/F/IOKit.framework/Versions/Current"
(version, f'{opts.dst_sdk}/{framework}/Versions/Current')
):
try:
os.unlink(symlink_dst)
except FileNotFoundError:
pass
os.symlink(src, symlink_dst)
# Strip the symbols WebKit is not using from the TBDs.
subprocess.check_call([f'{scripts_dir}/strip-tbd', '-s', export_list] + copied_tbds)
print('\nCopied', len(copied_tbds), f'TBDs to {opts.dst_sdk}, and stripped them of unneeded symbols.')