Files
codex/third_party/voice/sdk.py
Benjamin Carlsson 47ca4619be Include GIO in voice SDKs and native runtimes (#43102)
## Why

The Rust GStreamer bindings link GLib's GIO library even when none of the selected plugins imports it. Following only plugin dependencies can omit a library the bindings need.

## What changed

- Export `gio-2.0` pkg-config metadata in the voice SDK.
- Require GIO as a runtime dependency root on Linux, macOS, and Windows, including its transitive dependencies under the existing import checks.
- Reject runtime packages that omit the required GIO library.

## Testing

Extend fixtures to cover GIO inclusion and missing-library rejection across platforms, loading GIO and its transitive dependency after moving a Linux runtime, and resolving `gio-2.0` through pkg-config after moving an SDK.

GitOrigin-RevId: d12e45d387d56c54d94d788cf07d82bb22c2b4a3
2026-09-05 21:18:44 +00:00

142 lines
5.4 KiB
Python

"""Export development inputs from the same inspected build as the private runtime.
SDKs are build inputs, never installed user payloads. Their receipt records byte
identity and provenance, not authenticity or approval. Native loader relocation
and final helper linking remain the runtime preparer and consumer's jobs.
"""
import json
from pathlib import Path
import re
import shutil
from runtime import digest
MODULES = (
"glib-2.0",
"gobject-2.0",
"gio-2.0",
"gmodule-no-export-2.0",
"gstreamer-1.0",
"gstreamer-base-1.0",
"gstreamer-app-1.0",
"gstreamer-audio-1.0",
"gstreamer-tag-1.0",
)
def export_sdk(prefix: Path, receipts: Path, target: str, output: Path):
prefix, receipts = prefix.resolve(strict=True), receipts.resolve(strict=True)
output = output.absolute()
if (
output.exists()
or output.is_symlink()
or any(output.resolve().is_relative_to(root) for root in (prefix, receipts))
):
raise ValueError("SDK output must be fresh and outside the inputs")
if not re.fullmatch(
r"(?:aarch64|x86_64)-(?:apple-darwin|unknown-linux-gnu|pc-windows-msvc)",
target,
):
raise ValueError("unsupported SDK target")
ci = json.loads((receipts / "ci.json").read_text())
source_hash = digest(Path(__file__).with_name("sources.json"))
if (
ci.get("target") != target
or ci.get("build_complete") is not True
or ci.get("inspection_complete") is not True
or ci.get("manifest_sha256") != source_hash
or not re.fullmatch(r"[0-9a-f]{40}", ci.get("commit", ""))
):
raise ValueError("SDK build receipt does not match the sources and target")
inventory = json.loads((receipts / "inspection/binaries.json").read_text())
if not 1 <= len(inventory) <= 128:
raise ValueError("unexpected native inventory size")
binaries = {}
for record in inventory:
name = record["path"].replace("\\", "/")
if not re.fullmatch(r"(?:lib(?:/gstreamer-1\.0)?|bin)/[A-Za-z0-9_+.-]+", name):
raise ValueError("invalid native inventory path")
path = prefix / name
if (
name in binaries
or path.is_symlink()
or not path.resolve(strict=True).is_relative_to(prefix)
or record["target"] != target
or digest(path) != record["sha256"]
):
raise ValueError("native SDK library does not match its receipt")
binaries[name] = record["sha256"]
selected = []
names = set()
size = 0
for path in sorted(prefix.rglob("*")):
name = path.relative_to(prefix).as_posix()
if path.is_dir():
if path.is_symlink():
raise ValueError("SDK input directories must not be links")
continue
if not (
name.startswith("include/")
or name == "lib/glib-2.0/include/glibconfig.h"
or name
in {
f"lib/pkgconfig/{module}.pc"
for module in (*MODULES, "libffi", "libpcre2-8", "zlib")
}
or re.fullmatch(
r"lib/[A-Za-z0-9_+.-]+\.(?:a|lib|dylib|so(?:\.[0-9]+)*)", name
)
):
continue
source = path.resolve(strict=True)
if not source.is_relative_to(prefix) or not source.is_file():
raise ValueError("SDK inputs must be files inside the prefix")
if name.casefold() in names:
raise ValueError("SDK paths collide across platforms")
names.add(name.casefold())
size += source.stat().st_size
if len(selected) >= 4096 or size > 512 * 1024 * 1024:
raise ValueError("SDK inputs exceed the size limit")
source_name = source.relative_to(prefix).as_posix()
expected = digest(source)
if re.search(r"\.(?:dylib|so(?:\.[0-9]+)*)$", name):
if binaries.get(source_name) != expected:
raise ValueError("SDK shared library is missing from the receipt")
selected.append((name, source, expected))
for module in MODULES:
path = prefix / f"lib/pkgconfig/{module}.pc"
if path.relative_to(prefix).as_posix().casefold() not in names or not re.search(
r"^prefix=\$\{pcfiledir\}/\.\./\.\.$", path.read_text(), re.MULTILINE
):
raise ValueError("rebuild the SDK with relocatable Meson pkg-config files")
if "lib/glib-2.0/include/glibconfig.h" not in names:
raise ValueError("SDK is missing the target's GLib configuration header")
output.mkdir()
try:
files = []
for name, source, expected in selected:
destination = output / name
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)
if digest(destination) != expected:
raise ValueError("SDK input changed while copying")
files.append({"path": name, "sha256": expected})
(output / "sdk.json").write_text(
json.dumps(
{
"schemaVersion": 1,
"target": target,
"sourceCommit": ci["commit"],
"sourceManifestSha256": source_hash,
"files": files,
},
indent=2,
)
+ "\n",
encoding="utf-8",
)
except BaseException:
shutil.rmtree(output)
raise