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

构建 rusty-cheddar

一个库,可以从 Rust 源文件自动生成 C 头文件

6 个版本

使用旧版 Rust 2015

0.3.3 2016 年 5 月 3 日
0.3.2 2016 年 3 月 2 日
0.3.1 2016 年 1 月 20 日
0.2.0 2015 年 12 月 28 日
0.1.0 2015 年 12 月 7 日

#387构建工具

每月 31 次下载
用于 3 crates

MIT 许可证

67KB
973

rusty-cheddar

Build Status MIT licensed

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

关于版本的信息:虽然 rusty-cheddar 还处于重大变化之中(即预 v1.0.0),但它可能经历多次重大更改。然而,直到 v1.0.0,每次进行重大更改时,将增加次要版本号;每次添加新功能时,将增加路径版本号。

rusty-cheddar 面向 C99 或更高版本(对于合理的单行注释以及使用 stdint.hstdbool.h),如果您真的真的真的真的真的需要使用较旧的标准,请在 repo 中创建一个问题,我会不情愿地想出如何实现对该标准的支持(在和你争论很多之后)。

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

# Cargo.toml

[build-dependencies]
rusty-cheddar = "0.3.0"

然后创建以下 build.rs

// build.rs

extern crate cheddar;

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

这应该会按预期工作,前提是您已正确设置项目。 别忘了在 [package] 部分(在 cargo 文档 中了解更多信息)中添加 build = ...

rusty-cheddar 将在 include/ 中创建一个 my_header.h 文件。请注意,rusty-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() 方法以提供更精细的控制。

转换

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

类型定义

rusty-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.

枚举

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

Rust

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

// This would fail is 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 {
        Red = -6,
        Blue,
        Green = 7,
        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.

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

函数

为了使 rusty-cheddar 能够检测到函数声明,它必须是公共的,标记为 #[no_mangle],并且具有以下之一 ABIs

  • C
  • Cdecl
  • Stdcall
  • Fastcall
  • System

我对调用约定不是很了解,所以如果您认为其中之一被错误地包含,或者如果遗漏了其中一个,请在该 仓库 上提交一个 issue。

rusty-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 编译器必须知道类型的布局,而 rusty-cheddar 目前还不能搜索其他模块。

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

贡献

欢迎向 rusty-cheddar 贡献。

错误

如果您发现错误或有功能请求,请打开一个问题。我无法保证我会修复它,但我会尽力而为。

如果您以任何方式发现源代码不清楚,那么我认为这是一个错误。我尽量使我的源代码尽可能清晰,但我不太擅长,所以任何这方面的帮助都将受到欢迎。

PRs

我喜欢 pull requests,它们使我的工作变得容易得多,所以如果您想自己修复错误或实现功能,那就太好了。如果您对任何事情感到困惑或需要一些指导,请随时打开一个问题,以便我可以帮助您,否则 这些文档 是一个好的开始。

测试

测试需要您安装最近版本的 CppHeaderParser(> v2.7.2),用于已安装的 Python 版本(通常是 Python 2)。此外,由于测试是一堆垃圾黑客,您必须位于 rusty-cheddar 的 Cargo.toml 相同目录中才能成功运行它们。

依赖关系

~2.5MB
~50K SLoC