Files
spoopyghosty0 dccdcaab96 SteamVR overlay apps on the Frame (agent v75)
Host OpenVR overlays are composited over Lepton (Quest) games on the Frame;
Windows overlay apps register through Proton's vrclient. Detect overlay apps
(bundled .vrmanifest with is_dashboard_overlay, or OpenVR + IVROverlay +
self-registration), patch pcvr.vr_overlay and the Linux entry fields
vr_overlay/vr_overlay_autostart, agent register/unregister/launch_vr_overlay
(IVRApplications through SteamVR's libopenvr_api.so; manifest with
binary_path_linux_arm, the only key the Frame's vrserver reads). Overlay apps
skip launch tests and the Linux Steam-parent watchdog and don't count as a
running game. Probe sources in native/vroverlay_probe.
2026-10-10 20:01:07 -04:00

419 lines
23 KiB
Python

#!/usr/bin/env python3
"""Rebuild the prebuilt native artifacts in ../artifacts (developers only; users never need this).
Everything is fetched on demand into native/.cache (git-ignored):
- Android NDK r27c (27.2.12479018) from Google's repository (checksummed via repository2-3.xml)
- OpenXR headers at the commits each component was written against (KhronosGroup/OpenXR-SDK)
- Temurin JDK 21 (javac) and Android build-tools (d8) for the Java stub classes
Then builds: FrameBridge adapter (arm64 + arm32), VrApi bridge, platform compat, language packs, GL shim, multiview twin interposer (glmv), oculusos stub dex, the
timefix OpenXR layer for Proton games (linux-arm64, glibc; the NDK's clang builds it freestanding), the
OculusHMDConnected helper for Rift games under Proton (win-x64 PE; the NDK's clang + lld-link, no Windows SDK), the
live view's hardware H.264 encoder fp_venc (linux-arm64-bin, static freestanding executable), and rewrites artifacts/SHA256SUMS. Run `frameport parity` afterwards to see which games change.
python native/build.py [--only adapter,bridge,compat,langpack,glshim,eglfmt,glmv,ovrtrace,dex,xrlayer,oculushmd,xrshim,vkshim,zinkfix,ovrpshim,vrsettings,venc,vroverlay_probe] [--ndk PATH]
"""
from __future__ import annotations
import argparse
import hashlib
import os
import platform
import re
import shutil
import subprocess
import sys
import urllib.request
import zipfile
from pathlib import Path
from xml.etree import ElementTree
HERE = Path(__file__).resolve().parent
ROOT = HERE.parent
CACHE = HERE / ".cache"
ART = Path(os.environ.get("FRAMEPORT_ARTIFACTS_OUT", ROOT / "artifacts"))
NDK_VERSION = "27.2.12479018" # r27c
REPO = "https://dl.google.com/android/repository/"
OPENXR = { # component -> OpenXR-SDK commit its headers came from
"adapter": "f2448a8797c85814aa892efc1ab8707900fbcc78",
"bridge": "7f9285bce1ce8b69bb75554bf788666579d0c35e",
"xrlayer": "f2448a8797c85814aa892efc1ab8707900fbcc78",
}
OPENXR_HEADERS = ("openxr.h", "openxr_platform.h", "openxr_platform_defines.h", "openxr_loader_negotiation.h")
def log(msg):
print(f"== {msg}", flush=True)
def fetch(url: str, dest: Path, sha1: str | None = None) -> Path:
if dest.exists():
return dest
dest.parent.mkdir(parents=True, exist_ok=True)
log(f"download {url}")
tmp = dest.with_suffix(dest.suffix + ".part")
with urllib.request.urlopen(url) as r, open(tmp, "wb") as f:
shutil.copyfileobj(r, f, 1 << 20)
if sha1 and hashlib.sha1(tmp.read_bytes()).hexdigest() != sha1:
raise SystemExit(f"checksum mismatch: {url}")
tmp.replace(dest)
return dest
def repo_archive(path_prefix: str) -> tuple[str, str]:
"""(url, sha1) of a package in Google's repository for this host OS."""
host = {"Linux": "linux", "Darwin": "macosx", "Windows": "windows"}[platform.system()]
idx = fetch(REPO + "repository2-3.xml", CACHE / "repository2-3.xml")
root = ElementTree.fromstring(idx.read_text())
for pkg in root.iter("remotePackage"):
if pkg.get("path") != path_prefix:
continue
for a in pkg.iter("archive"):
if a.findtext("host-os") in (host, None):
return REPO + a.findtext("complete/url"), a.findtext("complete/checksum")
raise SystemExit(f"{path_prefix} not found in the Android repository index")
def ndk(explicit: str | None) -> Path:
if explicit:
return Path(explicit)
dest = CACHE / f"ndk-{NDK_VERSION}"
if not (dest / "source.properties").exists():
url, sha1 = repo_archive(f"ndk;{NDK_VERSION}")
z = fetch(url, CACHE / Path(url).name, sha1)
log("extract NDK")
with zipfile.ZipFile(z) as zf:
zf.extractall(CACHE / "ndk-extract")
top = next((CACHE / "ndk-extract").iterdir())
shutil.move(str(top), str(dest)) # (a plain rename can fail on NTFS while the folder is being scanned)
shutil.rmtree(CACHE / "ndk-extract", ignore_errors=True)
restore_symlinks(z, dest)
for p in (dest / "toolchains/llvm/prebuilt").rglob("bin/*"):
p.chmod(0o755)
return dest
def restore_symlinks(archive: Path, dest: Path) -> None:
"""zipfile writes symlinks (clang -> clang-18, ...) as small text files; replace them with copies of their
targets (copies also work on filesystems without symlinks)."""
with zipfile.ZipFile(archive) as zf:
links = [i for i in zf.infolist() if (i.external_attr >> 16) & 0o170000 == 0o120000]
for _ in range(3): # links to links
for info in links:
rel = Path(*Path(info.filename).parts[1:])
path = dest / rel
target = (path.parent / zf.read(info).decode()).resolve()
if target.is_file() and (not path.exists() or path.stat().st_size < 256):
path.unlink(missing_ok=True)
shutil.copy2(target, path)
def clang_dir(ndk_root: Path) -> Path:
host = {"Linux": "linux-x86_64", "Darwin": "darwin-x86_64", "Windows": "windows-x86_64"}[platform.system()]
return ndk_root / "toolchains/llvm/prebuilt" / host
def openxr_include(component: str) -> Path:
commit = OPENXR[component]
inc = CACHE / f"openxr-{commit[:10]}"
for h in OPENXR_HEADERS:
fetch(f"https://raw.githubusercontent.com/KhronosGroup/OpenXR-SDK/{commit}/include/openxr/{h}", inc / "openxr" / h)
return inc
def run(cmd, cwd=None):
cmd = [str(c) for c in cmd]
p = subprocess.run(cmd, cwd=cwd)
if p.returncode:
raise SystemExit(f"failed: {' '.join(cmd[:3])} …")
def exe(tc: Path, name: str) -> Path:
return tc / "bin" / (name + (".cmd" if os.name == "nt" and "clang" in name and "-" in name else ""))
def build_adapter(tc: Path):
src = HERE / "adapter"
run([sys.executable, src / "gen_forwarders.py"])
inc = openxr_include("adapter")
common = ["-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror", "-Wno-gnu-conditional-omitted-operand",
"-Wno-missing-field-initializers", "-Wl,-Bsymbolic", "-Wl,-soname,libopenxr_loader_generic.so", "-I", inc]
run([exe(tc, "aarch64-linux-android29-clang"), *common, "-Wl,-z,max-page-size=16384", "frame_adapter.c", "forwarders.S",
"-ldl", "-llog", "-lm", "-o", ART / "arm64-v8a/libopenxr_loader_generic.so"], cwd=src)
run([exe(tc, "armv7a-linux-androideabi29-clang"), *common, "frame_adapter.c", "forwarders_arm32.S", "-ldl", "-llog", "-lm",
"-o", ART / "armeabi-v7a/libopenxr_loader_generic.so"], cwd=src)
def build_xrlayer(tc: Path):
"""OpenXR API layer for Linux aarch64 (glibc), loaded by Proton's wineopenxr host loader on the Frame. Freestanding:
no libc headers or link-time libs; clock_gettime binds to the process's libc at load time."""
src = HERE / "xrlayer"
inc = openxr_include("xrlayer")
out = ART / "linux-arm64"
out.mkdir(parents=True, exist_ok=True)
run([tc / "bin/clang", "--target=aarch64-linux-gnu", "-ffreestanding", "-nostdlibinc", "-fno-stack-protector",
"-fvisibility=hidden", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror", "-I", src / "include", "-I", inc,
f"-ffile-prefix-map={HERE}=native", "-shared", "-nostdlib", "-fuse-ld=lld", "-Wl,--build-id=none",
"-Wl,-z,max-page-size=65536", "-Wl,--hash-style=both", "-Wl,-soname,libxr_frameport_timefix.so",
"timefix_layer.c", "-o", out / "libxr_frameport_timefix.so"], cwd=src)
shutil.copy(src / "XR_APILAYER_FRAMEPORT_timefix.json", out / "XR_APILAYER_FRAMEPORT_timefix.json")
def uapi_arm64(tc: Path) -> Path:
"""Only the kernel UAPI headers of the NDK sysroot (linux/, asm-generic/, drm/, aarch64 asm/), rebuilt every time,
so no bionic libc header can end up on a freestanding build's include path."""
inc = tc / "sysroot/usr/include"
dest = CACHE / "uapi-arm64"
shutil.rmtree(dest, ignore_errors=True)
for d in ("linux", "asm-generic", "drm"):
shutil.copytree(inc / d, dest / d)
shutil.copytree(inc / "aarch64-linux-android/asm", dest / "asm")
return dest
def build_venc(tc: Path):
"""fp_venc (Linux aarch64, runs on the Frame): SteamVR headset view -> V4L2 hardware H.264 encoder -> stdout, for the
live view. Fully static and freestanding (raw syscalls, own _start), so it needs no libc at build or run time.
Goes to linux-arm64-bin/, not linux-arm64/ (that folder is uploaded whole as the OpenXR layer)."""
src = HERE / "venc"
uapi = uapi_arm64(tc)
out = ART / "linux-arm64-bin"
out.mkdir(parents=True, exist_ok=True)
run([tc / "bin/clang", "--target=aarch64-linux-gnu", "-ffreestanding", "-nostdlib", "-nostdlibinc", "-static",
"-fno-stack-protector", "-fno-builtin", "-O2", "-Wall", "-Wextra", "-Werror", "-fuse-ld=lld",
"-Wl,--build-id=none", "-Wl,-z,max-page-size=65536", "-e", "_start", "-I", src / "include", "-I", uapi,
f"-ffile-prefix-map={HERE}=native", "fp_venc.c", "convert.c", "sys.c", "-o", out / "fp_venc"], cwd=src)
OCULUSHMD_IMPORTS = ("CreateEventW", "CreateJobObjectW", "AssignProcessToJobObject", "QueryInformationJobObject",
"CreateProcessW", "ResumeThread", "WaitForSingleObject", "GetExitCodeProcess", "CloseHandle",
"Sleep", "GetCommandLineW", "GetLastError", "GetStdHandle", "WriteFile", "ExitProcess")
def build_oculushmd(tc: Path, out: Path | None = None, defines=()):
"""fp_oculushmd.exe (Windows x64, run by Proton on the Frame): provides the OculusHMDConnected event for Unreal
Rift games. Freestanding: no CRT; the kernel32 import library is generated from a .def by llvm-dlltool."""
src = HERE / "oculushmd"
out = out or ART / "win-x64"
out.mkdir(parents=True, exist_ok=True)
work = CACHE / "oculushmd"
work.mkdir(parents=True, exist_ok=True)
(work / "kernel32.def").write_text("LIBRARY kernel32.dll\nEXPORTS\n" + "".join(f" {n}\n" for n in OCULUSHMD_IMPORTS))
run([tc / "bin/llvm-dlltool", "-m", "i386:x86-64", "-d", work / "kernel32.def", "-l", work / "kernel32.lib"])
run([tc / "bin/clang", "--target=x86_64-pc-windows-msvc", "-ffreestanding", "-nostdlibinc", "-fno-stack-protector",
"-fno-builtin", "-O2", "-Wall", "-Wextra", "-Werror", f"-ffile-prefix-map={HERE}=native", *defines, "-c",
"fp_oculushmd.c", "-o", work / "fp_oculushmd.obj"], cwd=src)
run([tc / "bin/lld-link", "/nologo", "/Brepro", "/nodefaultlib", "/entry:start", "/subsystem:console",
"/dynamicbase", "/highentropyva", "/nxcompat", work / "fp_oculushmd.obj", work / "kernel32.lib",
f"/out:{out / 'fp_oculushmd.exe'}"])
VRSETTINGS_IMPORTS = ("LoadLibraryA", "GetProcAddress", "GetCommandLineA", "GetStdHandle", "WriteFile", "ExitProcess")
def build_vrsettings(tc: Path):
"""fp_vrsettings.exe (Windows x64, run on the PC): SteamVR settings through OpenVR's IVRSettings (see the source)."""
src = HERE / "vrsettings"
out = ART / "win-x64"
out.mkdir(parents=True, exist_ok=True)
work = CACHE / "vrsettings"
work.mkdir(parents=True, exist_ok=True)
(work / "kernel32.def").write_text("LIBRARY kernel32.dll\nEXPORTS\n" + "".join(f" {n}\n" for n in VRSETTINGS_IMPORTS))
run([tc / "bin/llvm-dlltool", "-m", "i386:x86-64", "-d", work / "kernel32.def", "-l", work / "kernel32.lib"])
run([tc / "bin/clang", "--target=x86_64-pc-windows-msvc", "-ffreestanding", "-nostdlibinc", "-fno-stack-protector",
"-fno-builtin", "-O2", "-Wall", "-Wextra", "-Werror", f"-ffile-prefix-map={HERE}=native", "-c",
"fp_vrsettings.c", "-o", work / "fp_vrsettings.obj"], cwd=src)
run([tc / "bin/lld-link", "/nologo", "/Brepro", "/nodefaultlib", "/entry:start", "/subsystem:console",
"/dynamicbase", "/highentropyva", "/nxcompat", work / "fp_vrsettings.obj", work / "kernel32.lib",
f"/out:{out / 'fp_vrsettings.exe'}"])
VROVERLAY_IMPORTS = VRSETTINGS_IMPORTS + ("Sleep",)
def build_vroverlay_probe(tc: Path):
"""fp_vroverlay_probe.exe (Windows x64 diagnostic, run under Proton on the Frame): does an OpenVR overlay app show
up? Not shipped: written to native/.cache/vroverlay_probe/ (opt-in: --only vroverlay_probe)."""
src = HERE / "vroverlay_probe"
work = CACHE / "vroverlay_probe"
work.mkdir(parents=True, exist_ok=True)
(work / "kernel32.def").write_text("LIBRARY kernel32.dll\nEXPORTS\n" + "".join(f" {n}\n" for n in VROVERLAY_IMPORTS))
run([tc / "bin/llvm-dlltool", "-m", "i386:x86-64", "-d", work / "kernel32.def", "-l", work / "kernel32.lib"])
run([tc / "bin/clang", "--target=x86_64-pc-windows-msvc", "-ffreestanding", "-nostdlibinc", "-fno-stack-protector",
"-fno-builtin", "-O2", "-Wall", "-Wextra", "-Werror", f"-ffile-prefix-map={HERE}=native", "-c",
"fp_vroverlay_probe.c", "-o", work / "fp_vroverlay_probe.obj"], cwd=src)
run([tc / "bin/lld-link", "/nologo", "/Brepro", "/nodefaultlib", "/entry:start", "/subsystem:console",
"/dynamicbase", "/highentropyva", "/nxcompat", work / "fp_vroverlay_probe.obj", work / "kernel32.lib",
f"/out:{work / 'fp_vroverlay_probe.exe'}"])
def build_bridge(tc: Path):
src = HERE / "vrapi-bridge"
inc = openxr_include("bridge")
run([tc / "bin/clang++", "--target=aarch64-linux-android29", f"--sysroot={tc / 'sysroot'}", "-std=c++17", "-O2", "-g",
"-fPIC", f"-ffile-prefix-map={src}=native/vrapi-bridge", f"-ffile-prefix-map={CACHE}=native/.cache",
"-fvisibility=hidden", "-fvisibility-inlines-hidden", "-Wall", "-Wextra",
"-Wno-missing-field-initializers", "-Werror=return-type", "-DXR_NO_PROTOTYPES", "-DXR_USE_PLATFORM_ANDROID",
"-DXR_USE_GRAPHICS_API_VULKAN", "-DXR_USE_GRAPHICS_API_OPENGL_ES", "-DXR_USE_TIMESPEC", f"-I{inc}", "-shared",
"-static-libstdc++", "runtime.cpp", "graphics.cpp", "input.cpp", "-Wl,--no-undefined",
"-Wl,-z,max-page-size=16384", "-Wl,--version-script=exports.map", "-llog", "-landroid", "-lvulkan", "-lEGL",
"-lGLESv3", "-ldl", "-o", ART / "arm64-v8a/libvrapi.so"], cwd=src)
def build_compat(tc: Path):
src = HERE / "platformcompat"
run([tc / "bin/clang++", "--target=aarch64-linux-android29", f"--sysroot={tc / 'sysroot'}", "-std=c++17", "-O2",
"-fPIC", "-fvisibility=hidden", "-fno-exceptions", "-fno-rtti", "-ffunction-sections", "-fdata-sections",
"-Wall", "-Wextra", "-Werror=return-type", "-shared", "-nostdlib++", "message_type.cpp", "-Wl,--no-undefined",
"-Wl,--gc-sections", "-Wl,-z,max-page-size=16384", "-Wl,-soname,libovrplatformcompat.so",
"-Wl,--version-script=exports.map", "-o", ART / "arm64-v8a/libovrplatformcompat.so"], cwd=src)
def build_langpack(tc: Path):
"""Language packs from the game's files (see langpack/langpack.c): DT_NEEDED-injected into overport's platform loader."""
src = HERE / "langpack"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-fvisibility=hidden", f"-ffile-prefix-map={src}=native/langpack", "-Wl,--no-undefined",
"-Wl,-z,max-page-size=16384", "-Wl,-soname,libfp_langpack.so", "langpack.c", "-ldl", "-llog",
"-o", ART / "arm64-v8a/libfp_langpack.so"], cwd=src)
def build_glshim(tc: Path):
src = HERE / "glshim"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-Wno-unused-function", "-Wno-incompatible-pointer-types", "-fvisibility=hidden", "-Wl,-soname,libglshim.so",
"-Wl,-z,max-page-size=16384", "glshim.c", "-ldl", "-llog", "-o", ART / "arm64-v8a/libglshim.so"], cwd=src)
def build_eglfmt(tc: Path):
"""Depth-format shim (see glshim/eglfmt.c) that GL-to-GLES wrappers (QuestCraft's LTW) load in place of libEGL.so.
Named libfpg.so: no longer than "libEGL.so", whose dlopen string in the wrapper is rewritten in place. Linked to
libEGL so a dlsym on its handle still finds every EGL function."""
src = HERE / "glshim"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-fvisibility=hidden", "-Wl,-soname,libfpg.so", "-Wl,-z,max-page-size=16384", "eglfmt.c",
"-Wl,--no-as-needed", "-lEGL", "-Wl,--as-needed", "-ldl", "-llog", "-o", ART / "arm64-v8a/libfpg.so"], cwd=src)
def build_glmv(tc: Path):
"""Single-view draws of OVR_multiview programs (see glmv/glmv.c, frame.gl_multiview_fbo). Named libfpglmv.so: as
long as "libGLESv3.so", whose dlopen string in the engine is rewritten in place. Linked to libGLESv3 so a dlsym on
its handle still finds every GL function it doesn't wrap."""
src = HERE / "glmv"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-fvisibility=hidden", f"-ffile-prefix-map={src}=native/glmv", "-Wl,-soname,libfpglmv.so",
"-Wl,-z,max-page-size=16384", "glmv.c", "-Wl,--no-as-needed", "-lGLESv3", "-Wl,--as-needed", "-lEGL", "-ldl",
"-llog", "-o", ART / "arm64-v8a/libfpglmv.so"], cwd=src)
def build_ovrtrace(tc: Path):
"""Meta Platform SDK tracer (see ovrtrace/ovrtrace.c): diagnostics, the game's engine library loads it first."""
src = HERE / "ovrtrace"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-Wl,-soname,libfp_ovrtrace.so", "-Wl,-z,max-page-size=16384", "ovrtrace.c", "-ldl", "-llog",
"-o", ART / "arm64-v8a/libfp_ovrtrace.so"], cwd=src)
def build_xrshim(tc: Path):
"""FrameBridge extension shim (see xrshim/xrshim.c): DT_NEEDED-injected in front of overport's libopenxr_loader.so."""
src = HERE / "xrshim"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-fvisibility=hidden", "-I", openxr_include("adapter"), "-Wl,-soname,libframe_xrshim.so",
"-Wl,-z,max-page-size=16384", "xrshim.c", "-ldl", "-llog", "-o", ART / "arm64-v8a/libframe_xrshim.so"], cwd=src)
def build_vkshim(tc: Path):
"""Vulkan shim (see vkshim/vkshim.c): the engine's dlopen("libvulkan.so") string is pointed at it."""
src = HERE / "vkshim"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-fvisibility=hidden", "-Wl,-soname,libfp_vk.so", "-Wl,-z,max-page-size=16384", "vkshim.c",
"-Wl,--no-as-needed", "-lvulkan", "-Wl,--as-needed", "-ldl", "-llog", "-o", ART / "arm64-v8a/libfp_vk.so"],
cwd=src)
def build_zinkfix(tc: Path):
"""Shader-fix Vulkan layer for OpenGL ES games (see zinkfix/zinkfix.c): between Zink and the driver."""
src = HERE / "zinkfix"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-fvisibility=hidden", "-Wl,-soname,libVkLayer_fp_shaderfix.so", "-Wl,-z,max-page-size=16384", "zinkfix.c",
"-ldl", "-llog", "-o", ART / "arm64-v8a/libVkLayer_fp_shaderfix.so"], cwd=src)
def build_ovrpshim(tc: Path):
"""OVRPlugin frame-loop shim (see ovrpshim/ovrpshim.c) for Unity 2017-2018 built-in Oculus support."""
src = HERE / "ovrpshim"
run([exe(tc, "aarch64-linux-android29-clang"), "-shared", "-fPIC", "-O2", "-Wall", "-Wextra", "-Werror",
"-fvisibility=hidden", "-Wl,-soname,libfp_ovrp.so", "-Wl,-z,max-page-size=16384", "ovrpshim.c", "-ldl",
"-llog", "-o", ART / "arm64-v8a/libfp_ovrp.so"], cwd=src)
def build_dex():
javac = shutil.which("javac")
if not javac:
raise SystemExit("javac not found: install a JDK (e.g. Temurin 21) to rebuild the Java stubs")
url, sha1 = repo_archive(next(p for p in _build_tools_paths()))
bt = fetch(url, CACHE / Path(url).name, sha1)
d8 = CACHE / "d8.jar"
if not d8.exists():
with zipfile.ZipFile(bt) as z:
d8.write_bytes(z.read(next(m for m in z.namelist() if m.endswith("lib/d8.jar"))))
out = CACHE / "java-classes"
shutil.rmtree(out, ignore_errors=True)
# compile-only stand-ins for the Android classes the stubs reference (not dexed; the device provides them)
android = CACHE / "android-stubs"
(android / "android/content").mkdir(parents=True, exist_ok=True)
(android / "android/content/Context.java").write_text("package android.content;\npublic abstract class Context {}\n")
android_out = CACHE / "android-classes"
run([javac, "--release", "8", "-d", android_out, android / "android/content/Context.java"])
srcs = sorted((HERE / "java-stubs").rglob("*.java"))
run([javac, "--release", "8", "-cp", android_out, "-d", out, *srcs])
classes = sorted(out.rglob("*.class"))
dexdir = CACHE / "dex"
shutil.rmtree(dexdir, ignore_errors=True)
dexdir.mkdir(parents=True)
run(["java", "-cp", d8, "com.android.tools.r8.D8", "--release", "--min-api", "24", "--output", dexdir, *classes])
shutil.copy(dexdir / "classes.dex", ART / "dex/oculusos-stubs.dex")
def _build_tools_paths():
idx = ElementTree.fromstring(fetch(REPO + "repository2-3.xml", CACHE / "repository2-3.xml").read_text())
paths = [p.get("path") for p in idx.iter("remotePackage") if p.get("path", "").startswith("build-tools;")
and "rc" not in p.get("path")]
return sorted(paths, key=lambda s: [int(x) for x in re.findall(r"\d+", s)], reverse=True)
def write_sums():
lines = []
for f in sorted(p for p in ART.rglob("*") if p.is_file() and p.name != "SHA256SUMS"):
lines.append(f"{hashlib.sha256(f.read_bytes()).hexdigest()} ./{f.relative_to(ART).as_posix()}")
(ART / "SHA256SUMS").write_text("\n".join(lines) + "\n")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--only", default="adapter,bridge,compat,langpack,glshim,eglfmt,glmv,ovrtrace,dex,xrlayer,oculushmd,xrshim,vkshim,zinkfix,ovrpshim,vrsettings,venc")
ap.add_argument("--ndk")
args = ap.parse_args()
parts = set(args.only.split(","))
for d in ("arm64-v8a", "armeabi-v7a", "dex"):
(ART / d).mkdir(parents=True, exist_ok=True)
tc = clang_dir(ndk(args.ndk)) if parts - {"dex"} else None
steps = {"adapter": lambda: build_adapter(tc), "bridge": lambda: build_bridge(tc), "compat": lambda: build_compat(tc),
"langpack": lambda: build_langpack(tc),
"glshim": lambda: build_glshim(tc), "eglfmt": lambda: build_eglfmt(tc), "glmv": lambda: build_glmv(tc), "ovrtrace": lambda: build_ovrtrace(tc), "xrshim": lambda: build_xrshim(tc), "dex": build_dex, "xrlayer": lambda: build_xrlayer(tc),
"oculushmd": lambda: build_oculushmd(tc), "vkshim": lambda: build_vkshim(tc),
"zinkfix": lambda: build_zinkfix(tc),
"ovrpshim": lambda: build_ovrpshim(tc),
"vrsettings": lambda: build_vrsettings(tc), "venc": lambda: build_venc(tc),
"vroverlay_probe": lambda: build_vroverlay_probe(tc)}
for name in ("adapter", "bridge", "compat", "langpack", "glshim", "eglfmt", "glmv", "ovrtrace", "xrshim", "vkshim", "zinkfix", "ovrpshim", "dex", "xrlayer", "oculushmd", "vrsettings", "venc",
"vroverlay_probe"):
if name in parts:
log(f"build {name}")
steps[name]()
write_sums()
log("artifacts/SHA256SUMS updated")
if __name__ == "__main__":
main()