#!/usr/bin/env python3 """Build and audit an unsigned iOS prototype; does not sign or install it.""" import hashlib import json import subprocess import zipfile from pathlib import Path root = Path(__file__).resolve().parent.parent subprocess.run(["xcodegen", "generate", "--spec", "platform/ios/project.yml"], cwd=root, check=True) build = subprocess.run([ "xcodebuild", "-quiet", "-project", "platform/ios/FactorioPort.xcodeproj", "-scheme", "FactorioPort", "-sdk", "iphoneos", "-destination", "generic/platform=iOS", "-configuration", "Release", "-derivedDataPath", "build/ios", "CODE_SIGNING_ALLOWED=NO", "build" ], cwd=root, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (root / "build/ios-build.log").write_text(build.stdout) if build.returncode: print(build.stdout[-12000:]) raise SystemExit(build.returncode) app = root / "build/ios/Build/Products/Release-iphoneos/FactorioPort.app" binary = app / "FactorioPort" dependencies = subprocess.check_output(["xcrun", "otool", "-L", str(binary)], text=True) imports = subprocess.check_output(["xcrun", "nm", "-u", str(binary)], text=True) if "steam" in (dependencies + imports).lower(): raise RuntimeError("Steam dependency/import found in local single-player target") ipa = root / "build/FactorioRNG-unsigned.ipa" with zipfile.ZipFile(ipa, "w", zipfile.ZIP_DEFLATED) as archive: for path in sorted(app.rglob("*")): if path.is_file(): archive.write(path, Path("Payload") / app.name / path.relative_to(app)) report = {"status": "pass", "ios_build": "unsigned device build", "device_execution": "not run", "steam_dependency_or_import": False, "binary_sha256": hashlib.sha256(binary.read_bytes()).hexdigest(), "dynamic_dependencies": dependencies.splitlines()[1:], "ipa": str(ipa)} (root / "build/ios-verification.json").write_text(json.dumps(report, indent=2) + "\n") print(json.dumps(report, indent=2))