|
| 1 | +"""Patch koharu-diffusion's C enum wrappers for the current target ABI.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import json |
| 6 | +import subprocess |
| 7 | +from pathlib import Path |
| 8 | + |
| 9 | + |
| 10 | +def koharu_enums_path() -> Path: |
| 11 | + metadata = subprocess.run( |
| 12 | + ["cargo", "metadata", "--locked", "--format-version", "1"], |
| 13 | + check=True, |
| 14 | + stdout=subprocess.PIPE, |
| 15 | + text=True, |
| 16 | + encoding="utf-8", |
| 17 | + ) |
| 18 | + packages = json.loads(metadata.stdout)["packages"] |
| 19 | + matches = [ |
| 20 | + package |
| 21 | + for package in packages |
| 22 | + if package["name"] == "koharu-diffusion" |
| 23 | + and package["source"].startswith("git+") |
| 24 | + ] |
| 25 | + if len(matches) != 1: |
| 26 | + raise RuntimeError( |
| 27 | + f"expected one Git koharu-diffusion package, found {len(matches)}" |
| 28 | + ) |
| 29 | + return Path(matches[0]["manifest_path"]).parent / "src" / "enums.rs" |
| 30 | + |
| 31 | + |
| 32 | +path = koharu_enums_path() |
| 33 | +source = path.read_text(encoding="utf-8") |
| 34 | + |
| 35 | +if "type FfiEnumRepr" not in source: |
| 36 | + anchor = "use crate::{Error, Result, ffi::NativeCall, sys};\n" |
| 37 | + replacement = anchor + """ |
| 38 | +
|
| 39 | +// Clang uses an unsigned C enum representation for these non-negative values, |
| 40 | +// while MSVC uses a signed representation. Match bindgen's target ABI. |
| 41 | +#[cfg(target_env = "msvc")] |
| 42 | +type FfiEnumRepr = i32; |
| 43 | +#[cfg(not(target_env = "msvc"))] |
| 44 | +type FfiEnumRepr = u32; |
| 45 | +""" |
| 46 | + if source.count(anchor) != 1: |
| 47 | + raise RuntimeError("koharu-diffusion import anchor changed") |
| 48 | + source = source.replace(anchor, replacement) |
| 49 | + |
| 50 | +replacements = { |
| 51 | + " #[repr(i32)]\n": ( |
| 52 | + " #[cfg_attr(target_env = \"msvc\", repr(i32))]\n" |
| 53 | + " #[cfg_attr(not(target_env = \"msvc\"), repr(u32))]\n" |
| 54 | + ), |
| 55 | + "pub const fn as_raw(self) -> i32": "pub const fn as_raw(self) -> FfiEnumRepr", |
| 56 | + "impl TryFrom<i32> for $name": "impl TryFrom<FfiEnumRepr> for $name", |
| 57 | + "fn try_from(value: i32) -> Result<Self>": ( |
| 58 | + "fn try_from(value: FfiEnumRepr) -> Result<Self>" |
| 59 | + ), |
| 60 | + "value => Err(Error::InvalidEnum { kind: $kind, value }),": ( |
| 61 | + "value => Err(Error::InvalidEnum { kind: $kind, value: value as i32 })," |
| 62 | + ), |
| 63 | +} |
| 64 | + |
| 65 | +expected_counts = { |
| 66 | + " #[repr(i32)]\n": 2, |
| 67 | + "pub const fn as_raw(self) -> i32": 2, |
| 68 | + "impl TryFrom<i32> for $name": 2, |
| 69 | + "fn try_from(value: i32) -> Result<Self>": 2, |
| 70 | + "value => Err(Error::InvalidEnum { kind: $kind, value }),": 2, |
| 71 | +} |
| 72 | + |
| 73 | +for old, new in replacements.items(): |
| 74 | + count = source.count(old) |
| 75 | + if count == 0 and new in source: |
| 76 | + continue |
| 77 | + if count != expected_counts[old]: |
| 78 | + raise RuntimeError(f"unexpected occurrence count for {old!r}: {count}") |
| 79 | + source = source.replace(old, new) |
| 80 | + |
| 81 | +path.write_text(source, encoding="utf-8") |
| 82 | +print(f"patched {path}") |
0 commit comments