feat(linux): support native package managers and host LLVM layouts

This commit is contained in:
rponeawa committed 2026-10-25 03:07:50 +08:00
1 parent dd284bc5a0
commit c8057bbcde
2 files changed
+160 -100

No files matched your search

+11 -25
View File
@@ -1,29 +1,12 @@
iig-port: parse for a Darwin target on a non-Apple host
iig-port: Darwin parsing and Clang resource headers
Against Nyktis/iig-port @ ea6d147.
libclang parses for its default triple. Apple's iig only runs on a Mac, where
that is the host's Darwin triple, so xnu's Makefiles never pass one. Built on
Linux, the default is the Linux host's triple and xnu's mach/machine/*.h stop
with "architecture not supported". On a non-Apple host, prepend
--target=arm64-apple-darwin unless the caller already passed --target=;
on a Mac nothing changes.
Any Darwin triple yields the same output, so the plain arm64-apple-darwin
is enough. iig's own argument check
rejects -target X and -arch, so --target=X is the only spelling that reaches
libclang.
It also hands libclang -resource-dir, the directory of clang's builtin
headers, unless the caller passed one. apt.llvm.org's libclang 24 works that
directory out only relative to an empty driver path (clang -v shows an empty
InstalledDir and "lib/clang/24/include"), so EXTERNAL_HEADERS/stdint.h's
#include_next found clang's stdint.h only from some working directories, and
the DriverKit .iig files failed with "'stdint.h' file not found". libclang's
C API has no call for the directory, so the patch derives it from the library
file dladdr() names and clang_getClangVersion()'s major version, trying
<prefix>/lib/clang/N (an upstream install, libclang in <prefix>/lib) and
/usr/lib/clang/N (Debian and apt.llvm.org, libclang in /usr/lib/<triple>).
On Linux, use --target=arm64-apple-darwin when the caller supplies no target.
The setup script defines UNIXNU_CLANG_RESOURCE_DIR from the selected clang's
-print-resource-dir output. Standalone builds derive the resource directory
from the loaded libclang library and its version. An explicit -resource-dir
argument takes priority.
--- a/iig.cpp
+++ b/iig.cpp
@@ -39,7 +22,7 @@ file dladdr() names and clang_getClangVersion()'s major version, trying
namespace {
const char kIIGVersion[] = "DriverKit-440";
@@ -1375,6 +1380,51 @@ void add_direct_iig_includes(const Options &opt, ParseState *state)
@@ -1375,6 +1380,54 @@ void add_direct_iig_includes(const Options &opt, ParseState *state)
void resolve_parameter_facts(ParseState *state);
@@ -56,6 +39,9 @@ file dladdr() names and clang_getClangVersion()'s major version, trying
+// apt.llvm.org). Empty when neither exists.
+static std::string libclang_resource_dir()
+{
+#ifdef UNIXNU_CLANG_RESOURCE_DIR
+ return UNIXNU_CLANG_RESOURCE_DIR;
+#endif
+ Dl_info info;
+ if (dladdr((void *)&clang_createIndex, &info) == 0 || info.dli_fname == nullptr) {
+ return "";
@@ -91,7 +77,7 @@ file dladdr() names and clang_getClangVersion()'s major version, trying
int parse_iig(const Options &opt, ParseState *state)
{
for (std::size_t i = 0; i < opt.clang_args.size(); ++i) {
@@ -1395,6 +1445,30 @@ int parse_iig(const Options &opt, ParseState *state)
@@ -1395,6 +1448,30 @@ int parse_iig(const Options &opt, ParseState *state)
CXIndex index = clang_createIndex(0, 0);
std::vector<const char *> args;
+149 -75
View File
@@ -9,11 +9,13 @@ corrupts the kernel link), mig from bootstrap_cmds, iig-port as iig, the host co
and lzfse for the kernelcache, and installs the shims of this directory. The Command Line Tools and Xcode toolchain
layouts that xnu's makefiles expect are under PREFIX.
Outside PREFIX it writes what xnu's makefiles and the host linker look for at fixed paths: the xcrun, plutil and
codesign shims in /usr/bin, libmacho.a and libstuff.a in /usr/lib, libdispatch (which ld64 needs) in /usr/local, and
the apt.llvm.org source list. Each step is skipped when its output exists.
Host dependencies come from apt, dnf, pacman, zypper, or a preconfigured environment.
Clang and libclang are selected through llvm-config. Libraries and build products live under PREFIX.
The xcrun, plutil and codesign shims are installed in /usr/bin for xnu's makefiles.
"""
import argparse
import os
import shlex
from pathlib import Path
import platform
import shutil
@@ -22,7 +24,6 @@ import sys
import tempfile
HERE = Path(__file__).resolve().parent
LLVM_VERSION = '24'
LLVM_COMMIT = '58c46bae21181fb4240cf964461bea738d96d50e'
CCTOOLS_PORT_REF = '1030.6.3-ld64-956.6'
LIBDISPATCH_REF = 'swift-6.4.0-RELEASE'
@@ -31,9 +32,20 @@ IIG_PORT_COMMIT = 'ea6d147d668ca15d490da638012243fcd705f30d'
LZFSE_COMMIT = 'e634ca58b4821d9f3d560cdc6df5dec02ffc93fd'
CLT_SHIMS = ('clang', 'clang++', 'cc', 'install', 'gm4')
USR_BIN_SHIMS = ('xcrun', 'plutil', 'codesign')
PACKAGES = ['build-essential', 'git', 'patch', 'pkg-config', 'ninja-build', 'flex', 'bison', 'python3', 'python3-dev',
'xz-utils', 'bzip2', 'cpio', 'rsync', 'curl', 'wget', 'ca-certificates', 'gnupg', 'lsb-release',
'libssl-dev', 'zlib1g-dev', 'tcsh', 'unifdef', 'cmake', 'libbsd-dev', 'uuid-dev']
COMMON_PACKAGES = ['git', 'patch', 'flex', 'bison', 'bzip2', 'cpio', 'rsync', 'curl',
'wget', 'ca-certificates', 'tcsh', 'unifdef', 'cmake', 'clang', 'lld', 'mtools']
PACKAGES = {
'apt': ['build-essential', 'pkg-config', 'ninja-build', 'python3', 'python3-dev',
'xz-utils', 'libssl-dev', 'zlib1g-dev', 'libbsd-dev', 'uuid-dev', 'llvm-dev', 'libclang-dev', 'device-tree-compiler'],
'dnf': ['gcc', 'gcc-c++', 'make', 'binutils', 'pkgconf-pkg-config', 'ninja-build', 'python3',
'python3-devel', 'xz', 'openssl-devel', 'zlib-devel', 'libbsd-devel', 'libuuid-devel',
'llvm-devel', 'clang-devel', 'dtc'],
'pacman': ['base-devel', 'pkgconf', 'ninja', 'python', 'xz', 'openssl', 'zlib', 'libbsd',
'util-linux', 'llvm', 'dtc'],
'zypper': ['gcc', 'gcc-c++', 'make', 'binutils', 'pkg-config', 'ninja', 'python3', 'python3-devel',
'xz', 'libopenssl-devel', 'zlib-devel', 'libbsd-devel', 'libuuid-devel',
'llvm-devel', 'clang-devel', 'dtc'],
}
XNU_BUILTIN_CHECK = '#if __has_builtin(__builtin_xnu_type_summary)\nok\n#endif\n'
# Darwin names glibc lacks, for xnu's SETUP host tools (installfile uses setmode/getmode, getprogname).
@@ -93,18 +105,19 @@ fcopyfile(int from_fd, int to_fd, copyfile_state_t state, unsigned int flags)
# distribution's clang. --ld-path: clang looks for "ld" next to itself first, and the Command Line Tools bin holds
# cctools' ld64; host tools link with the system ELF ld. A link (no -c) also gets cctools' libmacho and libstuff
# (the Mach-O arch table and byte swappers kextsymboltool and setsegname call; libmacho first, it calls into
# libstuff), libuuid and libbsd, all on the host linker's default path; xnu's SETUP makefiles set LDFLAGS literally.
# libstuff), libuuid and libbsd; xnu's SETUP makefiles set LDFLAGS literally.
HOST_CC = '''#!/bin/sh
# xnu's HOST_CC on Linux: the distribution's clang with the headers and libraries xnu's SETUP tools need.
libs=""
compile_only=0
for a in "$@"; do [ "$a" = "-c" ] && compile_only=1; done
[ "$compile_only" = 0 ] && libs="-lmacho -lstuff -luuid -lbsd"
if [ "$compile_only" = 0 ]; then
set -- "$@" "-L{lib}" -lmacho -lstuff -luuid -lbsd
fi
exec "{clang}" \\
--ld-path=/usr/bin/ld {arch_defs} \\
-include "{include}/unixnu-host-prefix.h" \\
-idirafter "{include}" \\
-idirafter "{include}/foreign" "$@" $libs
-idirafter "{include}/foreign" "$@"
'''
@@ -128,28 +141,74 @@ def has_xnu_builtins(command, env=None):
return any(line.strip() == 'ok' for line in out.splitlines())
def apt():
"""Build tools, the host packages xnu's own build uses, and apt.llvm.org's clang, lld and libclang."""
env = dict(os.environ, DEBIAN_FRONTEND='noninteractive')
run(['apt-get', 'update'], env=env)
run(['apt-get', 'install', '-y', '--no-install-recommends', *PACKAGES], env=env)
key = subprocess.run(['curl', '-fsSL', 'https://apt.llvm.org/llvm-snapshot.gpg.key'], check=True,
capture_output=True).stdout
subprocess.run(['gpg', '--dearmor', '--yes', '-o', '/usr/share/keyrings/llvm-archive-keyring.gpg'],
input=key, check=True)
codename = platform.freedesktop_os_release()['VERSION_CODENAME']
suite = f'llvm-toolchain-{codename}-{LLVM_VERSION}'
if subprocess.run(['curl', '-fsI', f'https://apt.llvm.org/{codename}/dists/{suite}/Release'],
capture_output=True).returncode:
suite = f'llvm-toolchain-{codename}'
Path('/etc/apt/sources.list.d/llvm.list').write_text(
f'deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] https://apt.llvm.org/{codename}/ {suite} main\n')
run(['apt-get', 'update'], env=env)
run(['apt-get', 'install', '-y', '--no-install-recommends', f'clang-{LLVM_VERSION}', f'lld-{LLVM_VERSION}',
f'llvm-{LLVM_VERSION}', f'libclang-{LLVM_VERSION}-dev'], env=env)
def package_manager(selected='auto'):
if selected != 'auto':
return selected
try:
release = platform.freedesktop_os_release()
except OSError:
release = {}
families = {'debian': 'apt', 'ubuntu': 'apt', 'fedora': 'dnf', 'rhel': 'dnf',
'centos': 'dnf', 'rocky': 'dnf', 'almalinux': 'dnf', 'arch': 'pacman',
'manjaro': 'pacman', 'opensuse': 'zypper', 'opensuse-tumbleweed': 'zypper',
'opensuse-leap': 'zypper', 'suse': 'zypper', 'sles': 'zypper'}
for name in [release.get('ID', ''), *release.get('ID_LIKE', '').split()]:
if name in families:
return families[name]
available = [name for name in PACKAGES if shutil.which('apt-get' if name == 'apt' else name)]
if len(available) == 1:
return available[0]
sys.exit('select --package-manager apt, dnf, pacman, zypper, or none for preinstalled dependencies')
def llvm(prefix, work, jobs):
def package_commands(manager):
if manager == 'none':
return []
packages = COMMON_PACKAGES + PACKAGES[manager]
return {
'apt': [['apt-get', 'update'], ['apt-get', 'install', '-y', '--no-install-recommends', *packages]],
'dnf': [['dnf', 'install', '-y', *packages]],
'pacman': [['pacman', '-S', '--needed', '--noconfirm', *packages]],
'zypper': [['zypper', '--non-interactive', 'install', '--no-recommends', *packages]],
}[manager]
def host_tools(llvm_config=None):
"""Resolve the host compiler and libclang from one LLVM installation."""
config = llvm_config or os.environ.get('LLVM_CONFIG') or shutil.which('llvm-config')
if not config or not shutil.which(str(config)):
sys.exit('llvm-config missing: install LLVM development files or pass --llvm-config FILE')
bindir = Path(run([config, '--bindir'], input='').strip())
libdir = Path(run([config, '--libdir'], input='').strip())
includedir = Path(run([config, '--includedir'], input='').strip())
cc, cxx = bindir / 'clang', bindir / 'clang++'
required = ['git', 'patch', 'pkg-config', 'ninja', 'flex', 'bison', 'python3', 'xz', 'bzip2',
'cpio', 'rsync', 'curl', 'wget', 'tcsh', 'unifdef', 'cmake', 'make', 'ld', 'ar',
'mformat', 'mcopy', 'dtc', 'fdtget', 'fdtput']
missing = [name for name in [str(cc), str(cxx), *required] if not shutil.which(name)]
if missing:
sys.exit('missing host tools: ' + ', '.join(missing))
resource = run([cc, '-print-resource-dir'], input='').strip()
if not (Path(resource) / 'include/stdint.h').is_file():
sys.exit(f'clang resource headers missing: {resource}')
# Compile and run against the selected libclang before the LLVM source build.
with tempfile.TemporaryDirectory(prefix='unixnu-host-check-') as tmp:
probe = Path(tmp) / 'probe'
run([cxx, '-x', 'c++', '-', '-o', probe, f'-I{includedir}', f'-L{libdir}',
f'-Wl,-rpath,{libdir}', '-lclang', '-lbsd', '-luuid', '-lssl', '-lz'],
input='#include <clang-c/Index.h>\n#include <bsd/unistd.h>\n#include <uuid/uuid.h>\n'
'#include <openssl/ssl.h>\n#include <zlib.h>\n'
'int main() { CXIndex i = clang_createIndex(0, 0); clang_disposeIndex(i); }\n')
run([probe])
warning = '-Wno-error=unused-but-set-global'
supports_warning = subprocess.run([cc, '-Werror=unknown-warning-option', warning, '-x', 'c',
'-fsyntax-only', '-'], input='', text=True, capture_output=True).returncode == 0
return argparse.Namespace(config=str(config), cc=cc, cxx=cxx, libdir=libdir,
includedir=includedir, resource=resource,
dispatch_cflags=warning if supports_warning else '')
def llvm(prefix, work, jobs, host):
"""clang with llvm-xnu-builtin-type.patch, for AArch64 only."""
clang = prefix / 'llvm/bin/clang'
if not clang.exists():
@@ -161,7 +220,7 @@ def llvm(prefix, work, jobs):
'-DCMAKE_BUILD_TYPE=Release', '-DLLVM_ENABLE_ASSERTIONS=ON',
'-DLLVM_INCLUDE_TESTS=OFF', '-DLLVM_INCLUDE_EXAMPLES=OFF', '-DLLVM_INCLUDE_BENCHMARKS=OFF',
'-DCLANG_ENABLE_STATIC_ANALYZER=OFF', '-DCLANG_ENABLE_ARCMT=OFF', '-DLLVM_PARALLEL_LINK_JOBS=1',
f'-DCMAKE_C_COMPILER=clang-{LLVM_VERSION}', f'-DCMAKE_CXX_COMPILER=clang++-{LLVM_VERSION}',
f'-DCMAKE_C_COMPILER={host.cc}', f'-DCMAKE_CXX_COMPILER={host.cxx}',
f'-DCMAKE_INSTALL_PREFIX={prefix / "llvm"}'])
run(['ninja', '-C', src / 'build', f'-j{jobs}', 'install-clang', 'install-clang-resource-headers'])
shutil.rmtree(src)
@@ -183,45 +242,45 @@ def shims(clt, xctoolchain, clang):
sys.exit('xcrun clang has no __builtin_xnu_type_summary')
def cctools(clt, include, work, jobs):
def cctools(clt, include, work, jobs, host):
"""ld64 that links a kernel Mach-O (ld64.lld rejects -kernel), the Mach-O tools, and the Darwin <mach-o/*.h>,
<mach/*.h> and libmacho that xnu's SETUP tools use. libtapi is not built: a kernel links from objects and
archives only."""
if (clt / 'bin/ld').exists():
if all((clt / path).exists() for path in ('bin/ld', 'lib/libmacho.a', 'lib/libstuff.a')):
return
env = dict(os.environ, CC=f'clang-{LLVM_VERSION}', CXX=f'clang++-{LLVM_VERSION}')
env = dict(os.environ, CC=str(host.cc), CXX=str(host.cxx))
src = work / 'cctools-port'
git_checkout(src, 'https://github.com/tpoechtrager/cctools-port.git', CCTOOLS_PORT_REF)
# ld64 from the 1009 branch on needs libdispatch and its blocks runtime, which Ubuntu does not package; cctools'
# configure finds it in /usr/local.
if not Path('/usr/local/lib/libdispatch.so').exists():
# Keep libdispatch and BlocksRuntime under the toolchain prefix.
dispatch_prefix = clt / 'libdispatch'
if not (dispatch_prefix / 'lib/libdispatch.so').exists():
dispatch = work / 'swift-corelibs-libdispatch'
git_checkout(dispatch, 'https://github.com/swiftlang/swift-corelibs-libdispatch.git', LIBDISPATCH_REF)
run(['cmake', '-G', 'Ninja', '-S', dispatch, '-B', dispatch / 'build', '-DCMAKE_BUILD_TYPE=Release',
'-DCMAKE_C_FLAGS=-Wno-error=unused-but-set-global'], env=env)
f'-DCMAKE_C_FLAGS={host.dispatch_cflags}',
f'-DCMAKE_INSTALL_PREFIX={dispatch_prefix}', '-DCMAKE_INSTALL_LIBDIR=lib'], env=env)
run(['ninja', '-C', dispatch / 'build', f'-j{jobs}'], env=env)
run(['ninja', '-C', dispatch / 'build', 'install'], env=env)
run(['ldconfig']) # the loader finds /usr/local/lib only through ld.so.cache
tree = src / 'cctools'
# --disable-lto-support: the kernel link uses no LTO, and libstuff/lto.c needs LLVM headers cctools-port does not
# ship. --target sets the default -arch; --program-prefix= and PROGRAM_PREFIX= keep the plain names (ld, lipo,
# otool) that xcrun is asked for and that the tools use to run each other.
run(['./configure', f'--prefix={clt}', '--target=arm64-apple-darwin', '--program-prefix=',
'--disable-lto-support'], cwd=tree, env=env)
'--disable-lto-support', f'--with-libdispatch={dispatch_prefix}',
f'--with-libblocksruntime={dispatch_prefix}'], cwd=tree, env=env)
run(['make', f'-j{jobs}', 'PROGRAM_PREFIX='], cwd=tree, env=env)
run(['make', 'install', 'PROGRAM_PREFIX='], cwd=tree, env=env)
# The headers xnu's SETUP tools include (<libc.h>, <mach-o/arch.h>, <mach-o/swap.h>, <mach/mach_init.h>); the
# host compiler wrapper adds them with -idirafter, after glibc's.
include.mkdir(parents=True, exist_ok=True)
shutil.copytree(tree / 'include', include, dirs_exist_ok=True)
# libmacho (the arch table) and libstuff (the byte swappers, and the host_info() stand-in libmacho calls), which
# cctools-port builds but does not install. /usr/lib is on the host linker's default path, as libmacho is part
# of libSystem on macOS.
# The host compiler wrapper links these archives from the toolchain's lib directory.
(clt / 'lib').mkdir(exist_ok=True)
for name in ('libmacho/.libs/libmacho.a', 'libstuff/.libs/libstuff.a'):
shutil.copy2(tree / name, '/usr/lib/')
shutil.copy2(tree / name, clt / 'lib')
def mig(clt, include, work):
def mig(clt, include, work, host):
"""mig and migcom from bootstrap_cmds, built with make-free compiler calls: the Xcode project's only work is the
yacc/lex C program migcom.tproj and the mig.sh driver, which finds migcom as ../libexec/migcom."""
if (clt / 'libexec/migcom').exists():
@@ -240,42 +299,34 @@ def mig(clt, include, work):
'string.c', 'type.c', 'user.c', 'utils.c', 'mig.c']
arch = ['-D__arm64__=1'] if platform.machine() == 'aarch64' else []
(clt / 'libexec').mkdir(parents=True, exist_ok=True)
run([f'clang-{LLVM_VERSION}', *arch, '-Ulinux', f'-DMIG_VERSION="{BOOTSTRAP_CMDS_TAG}"',
run([host.cc, *arch, '-Ulinux', f'-DMIG_VERSION="{BOOTSTRAP_CMDS_TAG}"',
'-idirafter', include / 'foreign', '-o', clt / 'libexec/migcom', *sources], cwd=tproj)
shutil.copyfile(tproj / 'mig.sh', clt / 'bin/mig')
(clt / 'bin/mig').chmod(0o755)
def iig(xctoolchain, work):
"""iig-port, an open implementation of the DriverKit interface generator, where xcrun looks for iig. The patch
defaults libclang to a Darwin target and finds libclang's builtin headers."""
if (xctoolchain / 'bin/iig').exists():
return
llvm_config = shutil.which(f'llvm-config-{LLVM_VERSION}') or shutil.which('llvm-config')
if not llvm_config:
sys.exit('no llvm-config')
def iig(xctoolchain, work, host):
"""Build iig with the selected libclang and its resource headers."""
src = work / 'iig-port'
git_checkout(src, 'https://github.com/Nyktis/iig-port', IIG_PORT_COMMIT)
run(['patch', '-d', src, '-p1', '--batch', '--forward', '-i', HERE / 'iig-port-darwin-target.patch'])
flags = run([llvm_config, '--cxxflags', '--ldflags'], input='').split()
bindir = run([llvm_config, '--bindir'], input='').strip()
flags = shlex.split(run([host.config, '--cxxflags', '--ldflags'], input=''))
(xctoolchain / 'bin').mkdir(parents=True, exist_ok=True)
# libclang-N.so.N is in the multiarch libdir the loader searches, so no rpath.
run([f'{bindir}/clang++', '-std=c++17', *flags, src / 'iig.cpp', '-lclang', '-o', xctoolchain / 'bin/iig'])
run([host.cxx, *flags, '-std=c++17',
f'-DUNIXNU_CLANG_RESOURCE_DIR="{host.resource}"', src / 'iig.cpp',
f'-Wl,-rpath,{host.libdir}', '-lclang', '-ldl', '-o', xctoolchain / 'bin/iig'])
def host_cc(clt, include):
def host_cc(clt, include, host):
"""xnu-host-cc, xnu's HOST_CC: SETUP tools built for this machine with the distribution's clang."""
include.mkdir(parents=True, exist_ok=True)
(include / 'unixnu-host-prefix.h').write_text(HOST_PREFIX_H)
(include / 'copyfile.h').write_text(COPYFILE_H)
clangs = sorted(Path('/usr/lib').glob('llvm-*/bin/clang'), key=lambda p: int(p.parts[-3].split('-')[1]))
clang = str(clangs[-1]) if clangs else shutil.which('cc') or '/usr/bin/cc'
# cctools' foreign/machine/*.h select on __arm64__; arm64 Linux also makes plain char unsigned, and
# SETUP/decomment compares a char from getc() to EOF.
arch = '-D__arm64__ -fsigned-char' if platform.machine() == 'aarch64' else ''
wrapper = clt / 'bin/xnu-host-cc'
wrapper.write_text(HOST_CC.format(clang=clang, arch_defs=arch, include=include))
wrapper.write_text(HOST_CC.format(clang=host.cc, arch_defs=arch, include=include, lib=clt / 'lib'))
wrapper.chmod(0o755)
@@ -288,25 +339,48 @@ def lzfse(prefix, work):
run(['make', '-C', src, 'install', f'INSTALL_PREFIX={prefix}', 'CC=cc'])
def main():
if os.geteuid():
sys.exit('setup-toolchain.py: run as root')
prefix = Path(sys.argv[1] if len(sys.argv) > 1 else os.environ.get('UNIXNU_TOOLCHAIN', '/opt/unixnu-linux')).resolve()
def main(argv=None):
parser = argparse.ArgumentParser(description='Install the UniXNU toolchain on a glibc Linux host.')
parser.add_argument('prefix', nargs='?', default=os.environ.get('UNIXNU_TOOLCHAIN', '/opt/unixnu-linux'))
parser.add_argument('--package-manager', choices=('auto', *PACKAGES, 'none'), default='auto')
parser.add_argument('--print-packages', action='store_true', help='print package commands and exit')
parser.add_argument('--check', action='store_true', help='check installed host tools and libraries without installing')
parser.add_argument('--llvm-config', help='llvm-config for the host Clang/libclang installation; or LLVM_CONFIG')
parser.add_argument('-j', '--jobs', type=int, default=os.cpu_count() or 4)
args = parser.parse_args(argv)
if args.print_packages:
for command in package_commands(package_manager(args.package_manager)):
print(shlex.join(command))
return
if platform.system() != 'Linux':
parser.error('setup requires a Linux host')
if platform.libc_ver()[0] != 'glibc':
parser.error('the XNU host tools require glibc')
if args.jobs < 1:
parser.error('--jobs must be positive')
if not args.check:
if os.geteuid():
parser.error('installation requires root; use --check to inspect host dependencies')
for command in package_commands(package_manager(args.package_manager)):
run(command, env=dict(os.environ, DEBIAN_FRONTEND='noninteractive'))
host = host_tools(args.llvm_config)
if args.check:
print(f'host dependencies ready: {host.cc}, {host.config}')
return
prefix = Path(args.prefix).expanduser().resolve()
clt, xctoolchain = prefix / 'CommandLineTools/usr', prefix / 'XcodeDefault.xctoolchain/usr'
include = clt.parent / 'SDKs/MacOSX.sdk/usr/include'
jobs = os.cpu_count() or 4
jobs = args.jobs
prefix.mkdir(parents=True, exist_ok=True)
os.environ['PATH'] = f'/usr/lib/llvm-{LLVM_VERSION}/bin' + os.pathsep + os.environ.get('PATH', '')
apt()
# The llvm-project build tree takes several GB: build on disk ($TMPDIR, else /var/tmp), not in a tmpfs /tmp.
with tempfile.TemporaryDirectory(prefix='unixnu-toolchain-', dir=os.environ.get('TMPDIR', '/var/tmp')) as tmp:
work = Path(tmp)
clang = llvm(prefix, work, jobs)
clang = llvm(prefix, work, jobs, host)
shims(clt, xctoolchain, clang)
cctools(clt, include, work, jobs)
mig(clt, include, work)
iig(xctoolchain, work)
host_cc(clt, include)
cctools(clt, include, work, jobs, host)
mig(clt, include, work, host)
iig(xctoolchain, work, host)
host_cc(clt, include, host)
lzfse(prefix, work)
for tool in (clt / 'bin/ld', clt / 'bin/libtool', clt / 'libexec/migcom', xctoolchain / 'bin/iig',
clt / 'bin/xnu-host-cc', prefix / 'bin/lzfse'):