#header-file #generate #helper #macro #content #language #ffizz

ffizz-header

为您的库生成C头文件的FFI辅助工具

7个版本

0.5.0 2023年6月19日
0.4.1 2023年2月4日
0.3.4 2023年1月17日
0.3.2 2022年8月30日

#1013 in Rust patterns

Download history 111/week @ 2024-03-15 255/week @ 2024-03-22 357/week @ 2024-03-29 251/week @ 2024-04-05 514/week @ 2024-04-12 650/week @ 2024-04-19 509/week @ 2024-04-26 646/week @ 2024-05-03 305/week @ 2024-05-10 195/week @ 2024-05-17 328/week @ 2024-05-24 384/week @ 2024-05-31 275/week @ 2024-06-07 600/week @ 2024-06-14 683/week @ 2024-06-21 147/week @ 2024-06-28

1,725 每月下载量
2 crate 中使用

MIT 许可证

12KB
152

此crate支持在库crate本身中直接生成C头文件。

通常,crate会使用ffizz_header宏来定义头文件内容。然后,通过调用generate来生成头文件。

生成头文件

以下是一个简单有效的方法来生成头文件,使用优秀的cargo-xtask。将此设置完成后,只需运行cargo xtask codegen即可生成头文件。该文件可以是已签入的(在这种情况下,CI应验证其是否最新),或者作为发布/打包过程的一部分生成。

设置

在您的库顶级目录中添加一个调用

#[cfg(debug_assertions)] // only include this in debug builds
/// Generate the header
pub fn generate_header() -> String {
    ffizz_header::generate()
}

按照项目的文档中描述设置一个xtask项目。将您的库作为xtask crate的依赖项。在xtask/src/main.rs中,对于mysupercool-lib crate

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let workspace_dir = manifest_dir.parent().unwrap();

    // assume the mysupercool-lib crate is in `lib/`..
    let lib_crate_dir = workspace_dir.join("tests").join("lib");
    let mut file = File::create(lib_crate_dir.join("mysupercoollib.h")).unwrap();
    write!(&mut file, "{}", ::mysupercool_lib::generate_header()).unwrap();
}

您可能希望改进此实现,以进行适当的命令行解析和错误处理。

注意事项

此方法不支持为单个工作区生成多个头文件。由于存在重复符号,Rust拒绝链接它们。如果您的工空间包含多个库,另一个选项是为每个库构建一个二进制文件,该二进制文件只为该库生成头文件。

定义头文件

通常,导出头文件的库会在src/lib.rs中定义其顶部内容和相应的底部,使用snippet

ffizz_header::snippet! {
#[ffizz(name="topmatter", order=1)]
/// ```c
/// #ifndef INFPREC_H
/// #define INFPREC_H
///
/// #include <stdint.h> // ..and any other required headesr
/// ```
}

ffizz_header::snippet! {
#[ffizz(name="bottomatter", order=10000)]
/// ```c
/// #endif /* INFPREC_H */
/// ```
}

顶部内容可能还包括类型或宏的前向声明。

剩余的声明将用于类型和导出函数,使用 item。为每个源文件定义一系列 order 值可能会有帮助,以保持生成的头文件中相关声明的连续性。

#[ffizz_header::item]
#[ffizz(order = 900)]
/// ***** infprec_t *****
///
/// An infinite-precision integer.
/// ```c
/// typedef struct infprec_t infprec_t
/// ```
pub struct InfPrec { /* .. */ }
# type infprec_t = ();
#[ffizz_header::item]
#[ffizz(order = 901)]
/// Add two infinite-precision numbers.
#[no_mangle]
pub unsafe extern "C" fn infprec_add(a: infprec_t,  b: infprec_t) -> infprec_t { todo!() }

extern "C"

对于旨在用于 C 和 C++ 的头文件,定义一个 EXTERN_C 宏可能很有帮助

/// #ifdef __cplusplus
/// #define EXTERN_C extern "C"
/// #else
/// #define EXTERN_C
/// #endif // __cplusplus

这可以在以下声明中使用

EXTERN_C infprec_t infprec_add(infprec_t a, infprec_t b);

依赖项

~2MB
~44K SLoC