#!/usr/bin/env python3 import argparse import json import os from pathlib import Path import shutil import subprocess import sys import time import zipfile RIDS = ( 'osx-arm64', 'osx-x64', 'linux-x64', 'linux-arm64', 'linux-musl-x64', 'linux-musl-arm64', 'win-x64', 'win-x86', 'win-arm64', ) def run(command, logfile): print('+', ' '.join(map(str, command)), flush=True) with logfile.open('a') as output: process = subprocess.Popen(list(map(str, command)), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) for line in process.stdout: print(line, end='', flush=True) output.write(line) return process.wait() def main(): parser = argparse.ArgumentParser() parser.add_argument('--sources-manifest', type=Path, required=True) parser.add_argument('--results', type=Path, required=True) args = parser.parse_args() args.results.mkdir(parents=True, exist_ok=True) lock = json.loads(Path('build/linux-toolchain/release.lock.json').read_text()) jobs = subprocess.check_output(['nproc'], text=True).strip() report = {'sources': lock, 'jobs': int(jobs), 'targets': {}, 'package': 'not-built', 'runtime_tests': 'not performed; cross compilation and executable linking only'} descriptor = args.results / 'nine-platform-results.json' def save(): descriptor.write_text(json.dumps(report, indent=2) + '\n') save() for rid in RIDS: print(f'=== {rid}: compile and link with {jobs} jobs ===', flush=True) started = time.monotonic() logfile = args.results / (rid + '.log') libraries = Path('External/NativeStatic/zig-cross') / lock['skiasharp_version'] / rid native = Path('External/NativeStatic') / rid / 'native' command = [sys.executable, 'build/linux-toolchain/build-cross.py', '--target', rid, '--sources-manifest', args.sources_manifest, '--work', 'External/NativeStatic/.work/toolchains', '--output', libraries, '--jobs', jobs] status = run(command, logfile) if status == 0: status = run([sys.executable, 'build/linux-toolchain/package-zig.py', '--rid', rid, '--input', libraries, '--output', native], logfile) if status == 0: shutil.copytree(native, args.results / rid / 'native', dirs_exist_ok=True) shutil.copytree(libraries / 'link-tests', args.results / rid / 'link-tests', dirs_exist_ok=True) report['targets'][rid] = {'exit_code': status, 'passed': status == 0, 'elapsed_seconds': round(time.monotonic() - started, 1)} save() print(f'=== {rid}: {"PASS" if status == 0 else "FAIL"} ===', flush=True) failed = [rid for rid in RIDS if not report['targets'][rid]['passed']] if failed: print('Failed platforms: ' + ', '.join(failed), flush=True) return 1 os.environ['SOURCE_LOCK_JSON'] = json.dumps(lock) os.environ['PACKAGE_VERSION'] = lock['skiasharp_version'] + '-' + lock['angle_branch'] + '.2' logfile = args.results / 'pack.log' status = run([sys.executable, 'scripts/apply-release-version.py'], logfile) if status == 0: status = run(['dotnet', 'pack', 'NuGet/StaticGraphics/StaticLink.Avalonia.csproj', '-c', 'Release', '-o', args.results / 'nuget'], logfile) if status == 0: package = next((args.results / 'nuget').glob('*.nupkg')) with zipfile.ZipFile(package) as archive: names = set(archive.namelist()) required = {'buildTransitive/StaticLink.Avalonia.props', 'buildTransitive/StaticLink.Avalonia.targets'} required.update('static/' + rid + '/native/' + ('skia.lib' if rid.startswith('win-') else 'libskia.a') for rid in RIDS) if not required.issubset(names) or any('\\' in name for name in names): print('NuGet package is missing target archives or has non-portable paths', flush=True) status = 1 report['package'] = 'passed' if status == 0 else 'failed' save() print('Nine-platform build/link/package: ' + report['package'], flush=True) return status if __name__ == '__main__': raise SystemExit(main())