Files
smeng9andClaude Opus 5.5 b0fe495af6 build(kernel): run the kernel step on Linux
On a Linux host the driver sets the environment of the
scripts/linux/setup-toolchain.py toolchain: the xcrun shim answers SDK
queries with the build tree's fakeroot, clang targets plain arm64 for
iOS 17, and CCC_OVERRIDE_OPTIONS turns off two warnings upstream clang
has and Apple clang does not (the archive builders use -Werror and
replay xnu's compile records, so only the clang driver sees them).

- build-xnu-4k-baseline.sh runs scripts/linux/prepare-xnu.py: source
  rewrites the xnu sources that assume Apple's tools (SHA/AES assembler
  macros, makesyscalls.sh under mawk, kextsymboltool and /dev/null) and
  installs TargetConditionals.h into the fakeroot; libs builds
  libcc_kext.a from compiler-rt's cc_kext profile sources. xnu's make
  also gets the host compiler, mig and the machine sizes a Mac supplies.
  ditto becomes cp.
- build-firehose.sh compiles libfirehose_kernel from its xcconfig.
- build-corecrypto-14.py reads project.pbxproj with scripts/pbxproj.py
  (same JSON as plutil for all 13,112 objects).
- The kernelcache is decoded with lzfse.

The macOS path is unchanged apart from ditto. Built from the same
sources and configuration on Linux and with Xcode 27.1, the kernels have
the same global symbols, linked objects, archive symbols, 1,265
kalloc_type signatures, and prelink bindings and kexts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 19:48:06 +08:00

91 lines
2.7 KiB
Python

"""Read an Xcode project.pbxproj (an OpenStep property list) without plutil.
macOS `plutil -convert json` reads OpenStep plists; the plistlib-based plutil a Linux host has does not. Values come
back as plutil's JSON has them: dictionaries, lists and strings.
"""
from pathlib import Path
import re
BARE = re.compile(rb'[A-Za-z0-9_$+/:.\-]+')
ESCAPES = {b'n': b'\n', b't': b'\t', b'r': b'\r', b'"': b'"', b'\\': b'\\'}
def load(path):
data = Path(path).read_bytes()
at = 0
def skip():
nonlocal at
while at < len(data):
if data[at:at + 1].isspace():
at += 1
elif data.startswith(b'//', at):
end = data.find(b'\n', at)
at = len(data) if end < 0 else end + 1
elif data.startswith(b'/*', at):
end = data.find(b'*/', at + 2)
if end < 0:
raise ValueError(f'{path}: unterminated comment')
at = end + 2
else:
return
def expect(char):
nonlocal at
skip()
if data[at:at + 1] != char:
raise ValueError(f'{path}: expected {char.decode()} at offset {at}')
at += 1
def value():
nonlocal at
skip()
head = data[at:at + 1]
if head == b'{':
at += 1
result = {}
while True:
skip()
if data[at:at + 1] == b'}':
at += 1
return result
key = value()
expect(b'=')
result[key] = value()
expect(b';')
if head == b'(':
at += 1
result = []
while True:
skip()
if data[at:at + 1] == b')':
at += 1
return result
result.append(value())
skip()
if data[at:at + 1] == b',':
at += 1
if head == b'"':
at += 1
out = bytearray()
while True:
char = data[at:at + 1]
if not char:
raise ValueError(f'{path}: unterminated string')
at += 1
if char == b'"':
return out.decode()
if char == b'\\':
escape = data[at:at + 1]
at += 1
out += ESCAPES.get(escape, b'\\' + escape)
else:
out += char
match = BARE.match(data, at)
if not match:
raise ValueError(f'{path}: unexpected {head!r} at offset {at}')
at = match.end()
return match.group().decode()
return value()