abigen: Some more experiments

This commit is contained in:
Artemis Tosini 2024-09-13 05:43:52 +00:00
parent 1084402c28
commit 8f3c2cad6a
Signed by: artemist
GPG key ID: EE5227935FE3FF18
6 changed files with 282 additions and 28 deletions

105
Cargo.lock generated
View file

@ -137,6 +137,26 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bindgen"
version = "0.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"itertools",
"log",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
]
[[package]]
name = "bitflags"
version = "2.6.0"
@ -191,22 +211,21 @@ dependencies = [
"shlex",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c044c781163c001b913cd018fc95a628c50d0d2dfea8bca77dad71edb16e37"
dependencies = [
"clang-sys",
"libc",
]
[[package]]
name = "clang-sys"
version = "1.8.1"
@ -215,6 +234,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
@ -763,6 +783,15 @@ version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
@ -815,6 +844,16 @@ version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libloading"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]]
name = "libm"
version = "0.2.8"
@ -845,6 +884,12 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
@ -875,6 +920,16 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
@ -1089,6 +1144,16 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "prettyplease"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro-crate"
version = "3.2.0"
@ -1477,18 +1542,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.209"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.209"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
@ -1497,9 +1562,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.127"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
@ -1642,9 +1707,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.75"
version = "2.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
dependencies = [
"proc-macro2",
"quote",
@ -1985,11 +2050,13 @@ dependencies = [
name = "vapore-abigen"
version = "0.1.0"
dependencies = [
"clang",
"bindgen",
"color-eyre",
"env_logger",
"log",
"regex",
"serde",
"serde_json",
]
[[package]]

View file

@ -4,8 +4,10 @@ edition = "2021"
version.workspace = true
[dependencies]
clang = { version = "2.0.0", features = ["clang_10_0"] }
bindgen = "0.70.1"
color-eyre.workspace = true
env_logger.workspace = true
log.workspace = true
regex = "1.10.6"
serde = { version = "1.0.209", features = ["derive"] }
serde_json = "1.0.128"

79
abigen/src/apidesc.rs Normal file
View file

@ -0,0 +1,79 @@
use color_eyre::eyre;
use std::{fs, path::Path};
#[derive(Debug, serde::Deserialize)]
pub struct ApiDescription {
pub callback_structs: Vec<CallbackStruct>,
pub consts: Vec<Const>,
pub enums: Vec<Enum>,
pub interfaces: Vec<Interface>,
pub structs: Vec<Struct>,
pub typedefs: Vec<Typedef>,
}
#[derive(Debug, serde::Deserialize)]
pub struct CallbackStruct {
pub callback_id: u64,
#[serde(rename = "struct")]
pub _struct: String,
pub fields: Vec<StructField>,
}
#[derive(Debug, serde::Deserialize)]
pub struct StructField {
#[serde(rename = "fieldname")]
pub field_name: String,
#[serde(rename = "fieldtype")]
pub field_type: String,
}
#[derive(Debug, serde::Deserialize)]
pub struct Const {
#[serde(rename = "constname")]
pub const_name: String,
#[serde(rename = "consttype")]
pub const_type: String,
#[serde(rename = "constval")]
pub const_val: String,
}
#[derive(Debug, serde::Deserialize)]
pub struct Enum {
#[serde(rename = "enumname")]
pub enum_name: String,
}
#[derive(Debug, serde::Deserialize)]
pub struct Interface {
#[serde(rename = "classname")]
pub class_name: String,
pub version_string: Option<String>,
}
#[derive(Debug, serde::Deserialize)]
pub struct Struct {
#[serde(rename = "struct")]
pub _struct: String,
}
#[derive(Debug, serde::Deserialize)]
pub struct Typedef {
pub typedef: String,
}
impl ApiDescription {
pub fn read(sdk_path: &Path) -> eyre::Result<ApiDescription> {
let content = fs::read(sdk_path.join("steam_api.json"))?;
Ok(serde_json::from_slice(&content)?)
}
pub fn non_interface_names(&self) -> Vec<&str> {
self.callback_structs
.iter()
.map(|item| item._struct.as_str())
.chain(self.consts.iter().map(|item| item.const_name.as_str()))
.chain(self.enums.iter().map(|item| item.enum_name.as_str()))
.chain(self.structs.iter().map(|item| item._struct.as_str()))
.chain(self.typedefs.iter().map(|item| item.typedef.as_str()))
.collect()
}
}

View file

@ -6,9 +6,12 @@ use std::{
path::{Path, PathBuf},
};
use color_eyre::eyre;
use color_eyre::eyre::{self, OptionExt};
use regex::Regex;
mod apidesc;
mod rename;
fn main() -> eyre::Result<()> {
env_logger::init();
color_eyre::install()?;
@ -17,23 +20,70 @@ fn main() -> eyre::Result<()> {
PathBuf::from(env::var_os("PROTON_SOURCE").unwrap_or_else(|| "proton".into()))
.join("lsteamclient");
let mut versions = BTreeMap::new();
let mut interface_versions = BTreeMap::new();
for maybe_sdk in fs::read_dir(lsteamclient_path)? {
for maybe_sdk in fs::read_dir(&lsteamclient_path)? {
let sdk = maybe_sdk?;
if !sdk.file_type()?.is_dir() {
continue;
}
let this_versions = interface_versions(&sdk.path())?;
versions.insert(sdk.file_name(), this_versions);
let this_versions = parse_interface_versions(&sdk.path())?;
interface_versions.insert(sdk.file_name(), this_versions);
}
println!("{:#?}", versions);
let greatest_sdk_version = interface_versions
.keys()
.next_back()
.ok_or_eyre("No SDKs found")?
.clone();
log::info!("Last SDK found is {:?}", greatest_sdk_version);
let mut sdk_by_iface = BTreeMap::new();
for (sdk, sdk_versions) in interface_versions.iter() {
for iface_version in sdk_versions.values() {
sdk_by_iface.insert(iface_version.to_string(), sdk.clone());
}
}
println!("{:#?}", sdk_by_iface);
let api_desc = apidesc::ApiDescription::read(&lsteamclient_path.join(&greatest_sdk_version))?;
let mut latest_builder = bindgen::builder()
.header(
lsteamclient_path
.join(&greatest_sdk_version)
.join("steam_api.h")
.to_str()
.ok_or_eyre("Invalid path")?,
)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.parse_callbacks(Box::new(rename::RenameEnumCallbacks))
.ignore_methods()
.ignore_functions()
.clang_args(["-x", "c++"])
.default_enum_style(bindgen::EnumVariation::NewType {
is_bitfield: false,
is_global: false,
})
.bitfield_enum(".*Flags")
.vtable_generation(true);
// We only need the public API, steam_api.h implementation details aren't super useful
for item in api_desc.non_interface_names() {
latest_builder = latest_builder.allowlist_item(item);
log::trace!("Allowing item `{}`", item);
}
let latest_bindings = latest_builder.generate()?;
latest_bindings.write_to_file("target/src/deps.rs")?;
Ok(())
}
fn interface_versions(path: &Path) -> eyre::Result<BTreeMap<String, String>> {
fn parse_interface_versions(path: &Path) -> eyre::Result<BTreeMap<String, String>> {
let re = Regex::new(r#"#define\s*(STEAM[A-Z]*)_INTERFACE_VERSION\s*"([a-zA-Z0-9_]*)""#)?;
let mut versions = BTreeMap::new();

56
abigen/src/rename.rs Normal file
View file

@ -0,0 +1,56 @@
use std::collections::BTreeMap;
use bindgen::callbacks::ParseCallbacks;
use regex::Regex;
#[derive(Debug)]
pub struct RenameEnumCallbacks;
impl ParseCallbacks for RenameEnumCallbacks {
fn enum_variant_name(
&self,
enum_name: Option<&str>,
original_variant_name: &str,
_variant_value: bindgen::callbacks::EnumVariantValue,
) -> Option<String> {
let base_name = enum_name?;
let name = if base_name.ends_with("Flags") {
base_name.split_at(base_name.len() - 1).0
} else {
base_name
};
let re = Regex::new(&format!("^k_{}_?(.*)$", regex::escape(name))).unwrap();
let c = re.captures(original_variant_name)?;
let new_name = c.get(1)?.as_str();
if new_name.chars().next()?.is_ascii_digit() {
Some(format!("_{}", new_name))
} else {
Some(new_name.to_string())
}
}
}
#[derive(Debug)]
pub struct RenameInterfaceCallbacks {
interface_versions: BTreeMap<String, String>,
re: Regex,
}
impl RenameInterfaceCallbacks {
pub fn new(interface_versions: BTreeMap<String, String>) -> Self {
Self {
interface_versions,
re: Regex::new("^I(Steam[a-zA-Z]+)$").unwrap(),
}
}
}
impl ParseCallbacks for RenameInterfaceCallbacks {
fn item_name(&self, original_item_name: &str) -> Option<String> {
let c = self.re.captures(original_item_name)?;
let full_version = self.interface_versions.get(c.get(1)?.as_str())?;
let (_, short_version) = full_version.split_at(full_version.len() - 3);
Some(format!("{}{}", original_item_name, short_version))
}
}

View file

@ -30,7 +30,7 @@
LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib";
RUST_SRC_PATH = "${rustPackages.rustPlatform.rustLibSrc}";
RUST_LOG = "debug,vapore=trace,vapore-client=trace";
RUST_LOG = "debug,vapore=trace,vapore-client=trace,bindgen=error";
RUST_BACKTRACE = "1";
};