feat(linux): support native package managers and host LLVM layouts
This commit is contained in:
1 parent
dd284bc5a0
commit
c8057bbcde
2 files changed
+160
-100
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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'):
|
||||
|
||||
Reference in new issue
Block a user