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
67KB
973 行
rusty-cheddar
rusty-cheddar 是一个将 Rust 源文件转换为 C 头文件的库。
关于版本的信息:虽然 rusty-cheddar 还处于重大变化之中(即预 v1.0.0
),但它可能经历多次重大更改。然而,直到 v1.0.0
,每次进行重大更改时,将增加次要版本号;每次添加新功能时,将增加路径版本号。
rusty-cheddar 面向 C99 或更高版本(对于合理的单行注释以及使用 stdint.h
和 stdbool.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_void
或 std::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