import os
import shutil
import subprocess
import sys
from pathlib import Path
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
_LIB_NAMES = {
"linux": "libmedius_capi.so",
"darwin": "libmedius_capi.dylib",
"win32": "medius_capi.dll",
}
_VENDOR_DIRS = ["src", "examples", "medius-capi/src", "medius-capi/include"]
_VENDOR_FILES = [
"Cargo.toml",
"Cargo.lock",
"LICENSE",
"medius-capi/Cargo.toml",
"medius-capi/cbindgen.toml",
]
def _lib_name() -> str:
return _LIB_NAMES.get(sys.platform, "libmedius_capi.so")
def _locate_workspace(root: Path) -> Path:
vendored = root / "_rust"
if (vendored / "Cargo.toml").exists():
return vendored
return root.parent.parent
class CustomBuildHook(BuildHookInterface):
def initialize(self, version, build_data):
if self.target_name == "sdist":
self._vendor_rust(build_data)
return
build_data["pure_python"] = False
cargo_target = None
host_platform = os.environ.get("_PYTHON_HOST_PLATFORM")
if sys.platform == "darwin" and host_platform:
arch = host_platform.rsplit("-", 1)[-1]
cargo_target = {"x86_64": "x86_64-apple-darwin", "arm64": "aarch64-apple-darwin"}.get(arch)
deploy = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "11.0")
build_data["tag"] = "py3-none-macosx_{}_{}".format(deploy.replace(".", "_"), arch)
else:
try:
from packaging.tags import sys_tags
build_data["tag"] = "py3-none-{}".format(next(iter(sys_tags())).platform)
except Exception:
build_data["infer_tag"] = True
root = Path(self.root)
workspace = _locate_workspace(root)
pkg = root / "medius"
name = _lib_name()
build_cmd = ["cargo", "build", "--release", "-p", "medius-capi"]
if cargo_target:
build_cmd += ["--target", cargo_target]
if not os.environ.get("MEDIUS_SKIP_CARGO"):
subprocess.run(build_cmd, cwd=str(workspace), check=True)
reldir = "{}/release".format(cargo_target) if cargo_target else "release"
src = workspace / "target" / reldir / name
if not src.exists():
dbgdir = "{}/debug".format(cargo_target) if cargo_target else "debug"
debug = workspace / "target" / dbgdir / name
if debug.exists():
src = debug
if not src.exists():
raise FileNotFoundError(
"could not find {} under {}/target; build medius-capi first".format(name, workspace)
)
dest = pkg / name
shutil.copy2(src, dest)
rel = "medius/{}".format(name)
build_data["artifacts"].append(rel)
build_data["force_include"][str(dest)] = rel
def _vendor_rust(self, build_data):
workspace = Path(self.root).parent.parent
for rel_dir in _VENDOR_DIRS:
base = workspace / rel_dir
if not base.is_dir():
continue
for path in base.rglob("*"):
if path.is_file() and "target" not in path.relative_to(workspace).parts:
rel = path.relative_to(workspace)
build_data["force_include"][str(path)] = "_rust/{}".format(rel.as_posix())
for rel_file in _VENDOR_FILES:
path = workspace / rel_file
if path.is_file():
build_data["force_include"][str(path)] = "_rust/{}".format(rel_file)