radare2 0.1.0

Rust integration helpers for radare2 core plugins
Documentation
//! Discover radare2 via pkg-config and export version macros for the plugin ABI.

use std::env;
use std::fs;
use std::path::Path;

/// Locate `r_core` when native integration is enabled and bake version macros.
fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=native/radare2_shim.c");
    println!("cargo:rerun-if-changed=native/radare2_shim.h");

    if env::var("CARGO_FEATURE_NATIVE").is_err() {
        return;
    }

    let lib = pkg_config::Config::new()
        .probe("r_core")
        .expect("pkg-config could not find r_core; are radare2 development files installed?");

    let mut build = cc::Build::new();
    build
        .file("native/radare2_shim.c")
        .warnings(true)
        .extra_warnings(true);
    for include in &lib.include_paths {
        build.include(include);
    }
    build.compile("radare2_shim");

    println!("cargo:rustc-env=R2_VERSION={}", lib.version);

    let abi = lib
        .include_paths
        .iter()
        .map(|p| p.join("r_lib.h"))
        .find_map(|header| parse_abiversion(&header))
        .unwrap_or(83);
    println!("cargo:rustc-env=R2_ABIVERSION={abi}");
}

/// Read `#define R2_ABIVERSION N` from a radare2 header.
fn parse_abiversion(path: &Path) -> Option<u32> {
    let text = fs::read_to_string(path).ok()?;
    for line in text.lines() {
        let line = line.trim();
        if let Some(rest) = line.strip_prefix("#define R2_ABIVERSION ") {
            let token = rest.split_whitespace().next()?;
            return token.parse().ok();
        }
    }
    None
}