Files
RobustToolbox/native/build.py
PJB3005 539d0563b8 "Add vorbis" she said. "It'll be easy"
Yeah so this adds libogg, libvorbis, and libopus to the Rust library on the client. This is intended for use by the client soon-ish to replace .NET ogg vorbis implementations and add opus support.

This turns out to be a huge pain thanks to https://github.com/rust-lang/rfcs/issues/2771 . To solve this I ended up compiling the projects as staticlib and creating a build.py script to invoke the linker. This sucks a *lot* and I have yet to write the linker invocations for Linux/Windows, but it's probably the best option we have.
2026-01-04 04:16:20 +01:00

120 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
#
# This helper exists SOLELY to work around https://github.com/rust-lang/rfcs/issues/2771
# Instead of compiling our libs directly to a cdylib, we compile them to a staticlib
# and link manually. This is cool and all, except that's a lot of work.
# And involves three platform-specific linkers. *sigh*
#
import argparse
import os
import subprocess
import platform
import shlex
import tempfile
from dataclasses import dataclass, field
@dataclass
class LinkerData:
out_file: str
inputs: list[str] = field(default_factory=list)
libs: list[str] = field(default_factory=list)
lib_search_paths: list[str] = field(default_factory=list)
pkg_config_libs: list[str] = field(default_factory=list)
export_symbols: list[str] = field(default_factory=list)
os.chdir(os.path.dirname(os.path.realpath(__file__)))
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--release", action="store_true")
args = parser.parse_args()
cmd = ["cargo", "build", "-p", "robust-native-server", "-p", "robust-native-client"]
if args.release:
cmd.append("--release")
subprocess.run(cmd, check=True)
target_dir = os.path.join("target", "debug")
if args.release:
target_dir = os.path.join("target", "release")
link(LinkerData(
out_file=os.path.join(target_dir, platform_dylib_name("robust_native_client")),
inputs=[os.path.join(target_dir, platform_staticlib_name("robust_native_client"))],
pkg_config_libs=["ogg", "opus", "vorbis"],
export_symbols=read_symbols_def("ogg")
+ read_symbols_def("vorbis")
+ read_symbols_def("opus")
+ read_symbols_def("client")))
link(LinkerData(
out_file=os.path.join(target_dir, platform_dylib_name("robust_native_server")),
inputs=[os.path.join(target_dir, platform_staticlib_name("robust_native_server"))]))
def link(data: LinkerData):
system = platform.system()
if system == "Darwin":
link_macos(data)
else:
raise NotImplementedError()
#
# Per-platform linker invocations are mostly inspired by what rustc does
#
def link_macos(data: LinkerData):
symbol_list_file = tempfile.NamedTemporaryFile("w+")
symbol_list_file.writelines((f"_{n.strip()}\n" for n in data.export_symbols))
symbol_list_file.flush()
# Todo: crosscompile
args = [
"cc",
"-Wl,-exported_symbols_list",
f"-Wl,{symbol_list_file.name}",
"-arch", "arm64",
"-nodefaultlibs",
"-Wl,-dead_strip",
"-mmacosx-version-min=11.0.0",
"-dynamiclib",
"-o", data.out_file,
"-liconv", "-lSystem", "-lc", "-lm"
] + get_pkg_config_linker_flags(data.pkg_config_libs) + data.inputs
args.extend(("-L" + p for p in data.lib_search_paths))
args.extend(("-l" + l for l in data.libs))
subprocess.run(args, check=True)
def get_pkg_config_linker_flags(names: list[str]) -> list[str]:
if not names:
return []
result = subprocess.run(["pkg-config", "--libs"] + names, check=True, stdout=subprocess.PIPE)
return shlex.split(result.stdout.decode("utf-8"))
def read_symbols_def(name: str) -> list[str]:
with open(os.path.join("symbols", name + ".txt"), "r") as f:
return [line.strip() for line in f.readlines() if not line.startswith("#")]
def platform_staticlib_name(name: str) -> str:
if platform.system() == "Windows":
return name + ".lib"
return f"lib{name}.a"
def platform_dylib_name(name: str) -> str:
if platform.system() == "Windows":
return name + ".dll"
if platform.system() == "Darwin":
return f"lib{name}.dylib"
return f"lib{name}.so"
main()