101 lines
5.2 KiB
Python
101 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Link raster/HarfBuzz and EGL/GLES executables against cross-built archives."""
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import re
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser(description=__doc__)
|
|
p.add_argument('--toolchain', type=Path, required=True)
|
|
p.add_argument('--skia', type=Path, required=True)
|
|
p.add_argument('--angle', type=Path, required=True)
|
|
p.add_argument('--libraries', type=Path, required=True)
|
|
p.add_argument('--output', type=Path, required=True)
|
|
p.add_argument('--component', choices=['all', 'skia'], default='all')
|
|
a = p.parse_args()
|
|
config = json.loads((a.toolchain / 'toolchain.json').read_text())
|
|
windows = 'windows' in config['target']
|
|
a.output.mkdir(parents=True, exist_ok=True)
|
|
target = config['target']
|
|
flags = []
|
|
mac = 'macos' in target
|
|
linux = 'linux' in target
|
|
if mac:
|
|
# ANGLE's pinned Chromium build has a macOS 13 deployment minimum.
|
|
target = target.split('-macos.')[0] + '-macos.13.0'
|
|
sdk = config['sdk']
|
|
flags = ['-isysroot', sdk, '-L' + sdk + '/usr/lib', '-F' + sdk + '/System/Library/Frameworks']
|
|
for name in (('raster', 'angle') if a.component == 'all' else ('raster',)):
|
|
obj = a.output / ('smoke-' + name + '.o')
|
|
includes = [a.skia, a.skia / 'third_party/externals/harfbuzz/src', a.angle / 'include']
|
|
command = [str(a.toolchain / 'cc')]
|
|
if name == 'angle':
|
|
command.extend(('-DANGLE_STATIC=1', '-DKHRONOS_STATIC'))
|
|
else:
|
|
header = (a.skia / 'include/core/SkMilestone.h').read_text()
|
|
match = re.search(r'^\s*#define\s+SK_MILESTONE\s+(\d+)', header, re.MULTILINE)
|
|
if not match:
|
|
raise SystemExit('Cannot discover the Skia milestone; inspect the new source layout')
|
|
command.append('-DSTATICLINK_EXPECTED_MILESTONE=' + match[1])
|
|
command += ['-I' + str(path) for path in includes]
|
|
command += ['-c', str(HERE / ('smoke-' + name + '.c')), '-o', str(obj)]
|
|
subprocess.run(command, check=True)
|
|
libs = ('SkiaSharp', 'skia', 'HarfBuzzSharp') if name == 'raster' else ('EGL', 'GLESv2', 'ANGLE')
|
|
command = [config['zig'], 'c++', '-target', target, '-O2', *flags, str(obj)]
|
|
command += [str(a.libraries / ('lib' + library + '.a')) for library in libs]
|
|
if windows:
|
|
command += ['-l' + lib for lib in ('dwrite', 'gdi32', 'ole32', 'oleaut32', 'user32', 'uuid',
|
|
'windowscodecs', 'dxguid', 'dxgi', 'd3d11', 'd3d9', 'dwmapi', 'shlwapi', 'setupapi', 'version', 'advapi32', 'api-ms-win-core-synch-l1-2-0')]
|
|
elif mac:
|
|
for framework in ('CoreFoundation', 'CoreGraphics', 'CoreText', 'Foundation', 'AppKit',
|
|
'Metal', 'QuartzCore', 'OpenGL', 'IOKit', 'IOSurface'):
|
|
command += ['-framework', framework]
|
|
command += ['-lobjc']
|
|
availability = a.toolchain / 'os_version_check.o'
|
|
if availability.is_file():
|
|
command.append(str(availability))
|
|
elif linux:
|
|
sysroot = Path(config.get('linux_sysroot') or '/')
|
|
for path in (sysroot / 'usr/lib', sysroot / 'lib', sysroot / 'usr/lib' / ('x86_64-linux-gnu' if target.startswith('x86_64') else 'aarch64-linux-gnu')):
|
|
if path.is_dir():
|
|
command += ['-L' + str(path)]
|
|
command += ['-lfontconfig', '-ldl', '-lm', '-lpthread'] if name == 'raster' else ['-lX11', '-lXext', '-lxcb', '-lwayland-client', '-lwayland-egl', '-lffi', '-ldl', '-lm', '-lpthread']
|
|
command += ['-v']
|
|
command += ['-o', str(a.output / ('smoke-' + name + ('.exe' if windows else '')))]
|
|
result = subprocess.run(command, capture_output=True, text=True)
|
|
print(result.stdout, end='')
|
|
print(result.stderr, end='')
|
|
result.check_returncode()
|
|
# Keep the exact runtime archives chosen by this Zig target. AOT consumes
|
|
# these without substituting the consumer machine's C++ toolchain.
|
|
import shlex, shutil
|
|
runtimes = {}
|
|
project_archives = {path.resolve() for path in a.libraries.glob('lib*.a')}
|
|
for line in (result.stdout + '\n' + result.stderr).splitlines():
|
|
try:
|
|
tokens = shlex.split(line)
|
|
except ValueError:
|
|
continue
|
|
for token in tokens:
|
|
path = Path(token)
|
|
if path.suffix in ('.a', '.lib') and path.is_file() and path.resolve() not in project_archives and not path.resolve().is_relative_to(a.libraries.resolve()):
|
|
dest = a.libraries / 'zig-runtime' / path.name
|
|
dest.parent.mkdir(exist_ok=True)
|
|
shutil.copy2(path, dest)
|
|
runtimes[path.name] = str(path)
|
|
descriptor = a.libraries / 'zig-runtime' / 'inputs.json'
|
|
if descriptor.exists():
|
|
runtimes = dict(json.loads(descriptor.read_text()), **runtimes)
|
|
descriptor.parent.mkdir(exist_ok=True)
|
|
descriptor.write_text(json.dumps(runtimes, indent=2) + '\n')
|
|
print('Executable link tests passed. Run them on the target OS to validate behavior.')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|