#header #c #ffi #cargo-toml #build-script

构建 moz-cheddar

一个从Rust源文件自动生成C头文件的库

3个版本

使用旧的Rust 2015

0.4.2 2017年11月8日
0.4.1 2017年4月11日
0.4.0 2017年4月4日

#674 in 构建工具

MIT许可证

69KB
1K SLoC

这是一个 rusty-cheddar 的分支,直到 rusty-binder 版本可用。

moz-cheddar

Build Status MIT licensed

moz-cheddar 是一个将Rust源文件转换为C头文件的库。

关于版本号的说明:尽管 moz-cheddar 仍然是预 v1.0.0 版本,但它可能会经历许多破坏性变化。我们试图遵循 semver,并在添加新功能或更改输出行为时提高次要版本号。

moz-cheddar 旨在支持 C99 或更高版本(用于单行注释,以及使用 stdint.hstdbool.h)。

使用 moz-cheddar 的最有效方式是在构建脚本中。为此,请将以下 build-dependencies 部分添加到您的 Cargo.toml(如果将其用作普通库,只需将 build-dependencies 替换为 dependencies 即可)

# Cargo.toml

[build-dependencies]
moz-cheddar = "0.4.0"

然后创建以下 build.rs

// build.rs

extern crate cheddar;

fn main() {
    cheddar::Cheddar::new().expect("could not read manifest")
        .run_build("include/my_header.h");
}

如果您的项目设置正确,则应按原样工作。 不要忘记将 build = ... 添加到您的 [package] 部分,有关更多信息,请参阅 cargo 文档

moz-cheddar 将在 include/ 中创建一个 my_header.h 文件。请注意,moz-cheddar 产生的警告非常少,编写可以正确从 C 调用的库是程序员的责任。

模块中的API

您还可以将您的API放入模块中,以帮助保持源代码整洁。为此,您必须向Cheddar提供模块名称,然后确保项在顶级作用域中可用。

// build.rs

extern crate cheddar;

fn main() {
    cheddar::Cheddar::new().expect("could not read manifest")
        .module("c_api").expect("malformed module path")
        .run_build("target/include/rusty.h");
}
// src/lib.rs

pub use c_api::*;

mod c_api {
    // api goes here ...
}

此外,还有用于更精细控制的.compile().compile_code()方法。

转换

以下示例中省略了头部的模板代码。

类型别名

moz-cheddar将pub type A = B转换为typedef B A;。包含泛型的类型将被忽略。

Rust

type UInt32 = u32;
pub type UInt64 = u64;
pub type MyOption<T> = Option<T>

头文件

// Some boilerplate omitted.
typedef uint64_t UInt64;
// Some more boilerplate omitted.

枚举

moz-cheddar将标记为#[repr(C)]的公共枚举转换为C语言。如果枚举是泛型或包含元组或结构体变体,则cheddar将失败。moz-cheddar应该能够正确处理显式的区分符。

Rust

#[repr(C)]
pub enum Colours {
    Red = -6,
    Blue,
    Green = 7,
    Yellow,
}

// This would fail if it was #[repr(C)].
pub enum Tastes<T> {
    Savoury(T),
    Sweet,
}

// This would fail if it was public.
#[repr(C)]
enum Units {
    Kg(f64),
    M(f64),
    S(f64),
    A(f64),
    K(f64),
    Mol(f64),
    Cd(f64),
}

头文件

// Some boilerplate omitted.
typedef enum Colours {
        Colours_Red = -6,
        Colours_Blue,
        Colours_Green = 7,
        Colours_Yellow,
} Colours;
// Some more boilerplate omitted.

结构体

结构体的处理方式与枚举非常相似,它们必须是公共的,标记为#[repr(C)],并且不能包含泛型。目前这仅在结构体级别进行检查。泛型字段不会被检查。

Rust

#[repr(C)]
pub struct Person {
    age: i32,
    height: f64,
    weight: f64,
}

头文件

// Some boilerplate omitted.
typedef struct Person {
        int32_t age;
        double height;
        double weight;
} Person;
// Some more boilerplate omitted.

不透明结构体

一种常见的C语言习惯是使用不透明结构体来隐藏结构体的实现,它只能通过指针使用。这在Rust-C接口中特别有用,因为它允许您在C中使用任何任意的Rust结构体。

要定义一个不透明结构体,您必须定义一个标记为#[repr(C)]的公共新类型。

Rust

struct Foo<T> {
    bar: i32,
    baz: Option<T>,
}

#[repr(C)]
pub struct MyCrate_Foo(Foo<PathBuf>);

头文件

// Some boilerplate omitted.
typedef struct MyCrate_Foo MyCrate_Foo;
// Some boilerplate omitted.

请注意,新类型必须不是泛型的,但包装的类型可以是任意的。

函数

为了让moz-cheddar能够识别函数声明,它必须是公共的,标记为#[no_mangle],并具有以下之一ABIs

  • C
  • Cdecl
  • Stdcall
  • Fastcall
  • System

如果您认为其中之一被错误地包含,或者如果遗漏了一个,请在仓库中提交一个issue。

moz-cheddar会在标记为发散的函数上失败(-> !)。

Rust

use std::ops::Add;

#[no_mangle]
pub extern fn hello() {
    println!("Hello!");
}

fn add<O, R, L: Add<R, Output=O>>(l: L, r: R) -> O {
    l + r
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn MyAdd_add_u8(l: u8, r: u8) -> u8 {
    add(l, r)
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn MyAdd_add_u16(l: u16, r: u16) -> u16 {
    add(l, r)
}

头文件

// Some boilerplate omitted.
void hello();

uint8_t MyAdd_add_u8(uint8_t l, uint8_t r);

uint16_t MyAdd_add_u16(uint16_t l, uint16_t r);
// Some more boilerplate omitted.

路径

您不应该在不将其隐藏在不透明结构体之后的情况下,在其他模块中定义的类型放在导出的类型签名中。这是因为C编译器必须知道类型的布局,而moz-cheddar目前还不能搜索其他模块。

本规则的非常重要的例外是定义在 libc crate 和 std::os::raw 中的 C ABI 类型。这两个模块中的类型 必须 完全限定(例如 libc::c_voidstd::os::raw::c_longlong),以便正确转换。使用 use 语句导入它们将不起作用。

贡献

欢迎对 moz-cheddar 的贡献。

错误

如果你发现错误或有功能请求,请创建一个问题。

如果你在源代码中发现任何不清楚的地方,我认为那是一个错误。我试图使我的源代码尽可能清晰,因此任何这方面的帮助都将受到赞赏。

当然也欢迎拉取请求。

测试

测试需要你安装一个版本的 CppHeaderParser(> v2.7.2),用于与作为 python 安装的 Python 版本(通常是 Python 2)匹配。此外,由于测试是一个庞大的垃圾代码堆,你必须处于 moz-cheddar 的 Cargo.toml 相同目录中才能成功运行它们。

如果你没有这个,尝试

pip install CppHeaderParser

依赖关系

~0.9–1.7MB
~27K SLoC