mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-29 09:14:08 +08:00
[perf] avoid redundant iOS native archive rebuilds
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"""Exercise the native build phase with fake Xmake and real Apple archives."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
@unittest.skipUnless(sys.platform == "darwin", "requires Apple libtool and zsh")
|
||||
class NativeBuildTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory(prefix="crossdesk-ios-build-")
|
||||
self.addCleanup(self.temp.cleanup)
|
||||
self.root = Path(self.temp.name)
|
||||
self.ios = self.root / "apps/ios"
|
||||
scripts = self.ios / "scripts"
|
||||
scripts.mkdir(parents=True)
|
||||
source = Path(__file__).resolve().parents[1] / "scripts/build_minirtc_ios.sh"
|
||||
self.script = scripts / source.name
|
||||
shutil.copy2(source, self.script)
|
||||
(self.root / "xmake.lua").write_text('-- fixture\n')
|
||||
(self.root / "deps/submodules/minirtc").mkdir(parents=True)
|
||||
self.bin = self.root / "bin"
|
||||
self.bin.mkdir()
|
||||
self.library_dir = self.root / "packages/lib"
|
||||
self.library_dir.mkdir(parents=True)
|
||||
obj = self.root / "fixture.o"
|
||||
subprocess.run(["xcrun", "clang", "-x", "c", "-c", "-", "-o", str(obj)],
|
||||
input="int fixture(void) { return 1; }", text=True, check=True)
|
||||
self.archive = self.root / "fixture.a"
|
||||
subprocess.run(["/usr/bin/libtool", "-static", "-o", str(self.archive), str(obj)],
|
||||
check=True, capture_output=True)
|
||||
links = re.search(r"REQUIRED_LINKS=\((.*?)\)", source.read_text(), re.S)[1].split()
|
||||
for name in links:
|
||||
shutil.copy2(self.archive, self.library_dir / f"lib{name}.a")
|
||||
self.write_tool("xcodebuild", '#!/bin/sh\ncat "$(dirname "$0")/../xcode-version"\n')
|
||||
self.write_tool("xcrun", '#!/bin/sh\necho fixture-iphoneos-sdk\n')
|
||||
(self.root / "xcode-version").write_text("Xcode fixture-1\n")
|
||||
self.write_tool("xmake", f"#!{sys.executable}\n" + r'''
|
||||
import json, pathlib, shutil, sys
|
||||
root = pathlib.Path(__file__).resolve().parents[1]
|
||||
args = sys.argv[1:]
|
||||
with (root / "calls.jsonl").open("a") as log:
|
||||
log.write(json.dumps(args) + "\n")
|
||||
if args[0] == "--version":
|
||||
print("xmake fixture")
|
||||
elif args[0] == "f":
|
||||
if (root / "fail-config").exists():
|
||||
sys.exit(1)
|
||||
project = pathlib.Path(args[args.index("-P") + 1])
|
||||
(project / "config.json").write_text(json.dumps(args))
|
||||
elif args[0] == "b":
|
||||
project = pathlib.Path(args[args.index("-P") + 1])
|
||||
config = json.loads((project / "config.json").read_text())
|
||||
output = pathlib.Path(config[config.index("-o") + 1])
|
||||
output /= "iphoneos/arm64/" + config[config.index("-m") + 1]
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
library = output / ("lib" + args[-1] + ".a")
|
||||
if not library.exists():
|
||||
shutil.copy2(root / "fixture.a", library)
|
||||
elif args[0] == "show":
|
||||
print(" -> " + str(root / "packages/lib") + " -> package fixture")
|
||||
''')
|
||||
self.env = dict(os.environ, PATH=f"{self.bin}:{os.environ['PATH']}",
|
||||
XMAKE_BIN=str(self.bin / "xmake"), CONFIGURATION="Release",
|
||||
CURRENT_ARCH="arm64", DEVELOPER_DIR=subprocess.check_output(
|
||||
["xcode-select", "-p"], text=True).strip())
|
||||
self.output = self.ios / "Vendor/iphoneos/Release/libCrossDeskMiniRTC.a"
|
||||
|
||||
def write_tool(self, name, contents):
|
||||
path = self.bin / name
|
||||
path.write_text(contents)
|
||||
path.chmod(0o755)
|
||||
|
||||
def run_build(self, success=True):
|
||||
result = subprocess.run(["/bin/zsh", str(self.script)], env=self.env,
|
||||
text=True, capture_output=True)
|
||||
if success:
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
else:
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
return result
|
||||
|
||||
def config_calls(self):
|
||||
import json
|
||||
return [args for line in (self.root / "calls.jsonl").read_text().splitlines()
|
||||
if (args := json.loads(line))[0] == "f"]
|
||||
|
||||
def test_noop_preserves_archives_and_manifest(self):
|
||||
self.run_build()
|
||||
manifest = self.ios / ".xmake/wire-work/xmake.lua"
|
||||
old_times = (self.output.stat().st_mtime_ns, manifest.stat().st_mtime_ns)
|
||||
self.run_build()
|
||||
self.assertEqual(old_times, (self.output.stat().st_mtime_ns, manifest.stat().st_mtime_ns))
|
||||
self.assertEqual(["-c" in args for args in self.config_calls()], [True, True, False, False])
|
||||
|
||||
def test_archive_content_change_with_same_size_and_mtime_is_merged(self):
|
||||
self.run_build()
|
||||
original = self.output.read_bytes()
|
||||
library = self.library_dir / "libopus.a"
|
||||
stat = library.stat()
|
||||
data = library.read_bytes()
|
||||
# Rename an archive member without changing the archive layout or size.
|
||||
self.assertIn(b"fixture.o", data)
|
||||
library.write_bytes(data.replace(b"fixture.o", b"changed.o"))
|
||||
os.utime(library, ns=(stat.st_atime_ns, stat.st_mtime_ns))
|
||||
self.run_build()
|
||||
self.assertNotEqual(original, self.output.read_bytes())
|
||||
|
||||
def test_toolchain_and_mode_changes_clear_probes(self):
|
||||
self.run_build()
|
||||
(self.root / "xcode-version").write_text("Xcode fixture-2\n")
|
||||
self.run_build()
|
||||
self.env["CONFIGURATION"] = "Debug"
|
||||
self.run_build()
|
||||
self.assertEqual(["-c" in args for args in self.config_calls()], [True] * 6)
|
||||
self.assertTrue((self.ios / "Vendor/iphoneos/Debug/libCrossDeskMiniRTC.a").exists())
|
||||
|
||||
def test_failed_configuration_does_not_commit_signature(self):
|
||||
self.run_build()
|
||||
stamp = self.ios / ".xmake/minirtc-build/ios-config-signature"
|
||||
original = stamp.read_bytes()
|
||||
(self.root / "xcode-version").write_text("Xcode fixture-2\n")
|
||||
(self.root / "fail-config").touch()
|
||||
self.run_build(success=False)
|
||||
self.assertEqual(stamp.read_bytes(), original)
|
||||
(self.root / "fail-config").unlink()
|
||||
self.run_build()
|
||||
self.assertTrue(all("-c" in args for args in self.config_calls()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user