From ff35468af6f43ae19a3c87b394a423d43709003d Mon Sep 17 00:00:00 2001 From: sunface Date: Mon, 4 Apr 2022 23:22:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AB=A0=20[Cookbook=20-=20?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E5=B7=A5=E5=85=B7]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cookbook/devtools/build-tools.md | 191 +++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/src/cookbook/devtools/build-tools.md b/src/cookbook/devtools/build-tools.md index d5694492..b3bdc6ad 100644 --- a/src/cookbook/devtools/build-tools.md +++ b/src/cookbook/devtools/build-tools.md @@ -1 +1,192 @@ # 构建时工具 +本章节的内容是关于构建工具的,如果大家没有听说过 `build.rs` 文件,强烈建议先看看[这里](https://course.rs/cargo/reference/build-script/intro.html)了解下何为构建工具。 + +### 编译并静态链接一个 C 库 + +[cc](https://docs.rs/cc/latest/cc/) 包能帮助我们更好地跟 C/C++/汇编进行交互:它提供了简单的 API 可以将外部的库编译成静态库( .a ),然后通过 `rustc` 进行静态链接。 + +下面的例子中,我们将在 Rust 代码中使用 C 的代码: *src/hello.c*。在开始编译 Rust 的项目代码前,`build.rs` 构建脚本将先被执行。通过 cc 包,一个静态的库可以被生成( *libhello.a* ),然后该库将被 Rust的代码所使用:通过 `extern` 声明外部函数签名的方式来使用。 + +由于例子中的 C 代码很简单,因此只需要将一个文件传递给 [cc::Build](https://docs.rs/cc/*/cc/struct.Build.html)。如果大家需要更复杂的构建,`cc::Build` 还提供了通过 [include](https://docs.rs/cc/*/cc/struct.Build.html#method.include) 来包含路径的方式,以及额外的编译标志( [flags](https://docs.rs/cc/1.0.73/cc/struct.Build.html#method.flag) )。 + +*Cargo.toml* + +```toml +[package] +... +build = "build.rs" + +[build-dependencies] +cc = "1" + +[dependencies] +error-chain = "0.11" +``` + +*build.rs* + +```rust +fn main() { + cc::Build::new() + .file("src/hello.c") + .compile("hello"); // outputs `libhello.a` +} +``` + +*src/hello.c* + +```C +#include + + +void hello() { + printf("Hello from C!\n"); +} + +void greet(const char* name) { + printf("Hello, %s!\n", name); +} +``` + +*src/main.rs* + +```rust +use error_chain::error_chain; +use std::ffi::CString; +use std::os::raw::c_char; + +error_chain! { + foreign_links { + NulError(::std::ffi::NulError); + Io(::std::io::Error); + } +} +fn prompt(s: &str) -> Result { + use std::io::Write; + print!("{}", s); + std::io::stdout().flush()?; + let mut input = String::new(); + std::io::stdin().read_line(&mut input)?; + Ok(input.trim().to_string()) +} + +extern { + fn hello(); + fn greet(name: *const c_char); +} + +fn main() -> Result<()> { + unsafe { hello() } + let name = prompt("What's your name? ")?; + let c_name = CString::new(name)?; + unsafe { greet(c_name.as_ptr()) } + Ok(()) +} +``` + +### 编译并静态链接一个 C++ 库 +链接到 C++ 库跟之前的方式非常相似。主要的区别在于链接到 C++ 库时,你需要通过构建方法 [cpp(true)](https://docs.rs/cc/*/cc/struct.Build.html#method.cpp) 来指定一个 C++ 编译器,然后在 C++ 的代码顶部添加 `extern "C"` 来阻止 C++ 编译器对库名进行名称重整( name mangling )。 + +*Cargo.toml* + +```toml +[package] +... +build = "build.rs" + +[build-dependencies] +cc = "1" +``` + +*build.rs* + +```rust +fn main() { + cc::Build::new() + .cpp(true) + .file("src/foo.cpp") + .compile("foo"); +} +``` + +*src/foo.cpp* + +```c++ +extern "C" { + int multiply(int x, int y); +} + +int multiply(int x, int y) { + return x*y; +} +``` + +*src/main.rs* + +```rust +extern { + fn multiply(x : i32, y : i32) -> i32; +} + +fn main(){ + unsafe { + println!("{}", multiply(5,7)); + } +} +``` + +### 为 C 库创建自定义的 define + +[cc::Build::define](https://docs.rs/cc/*/cc/struct.Build.html#method.define) 可以让我们使用自定义的 define 来构建 C 库。 + +以下示例在构建脚本 `build.rs` 中动态定义了一个 define,然后在运行时打印出 **Welcome to foo - version 1.0.2**。Cargo 会设置一些[环境变量](https://doc.rust-lang.org/cargo/reference/environment-variables.html),它们对于自定义的 define 会有所帮助。 + +*Cargo.toml* + +```toml +[package] +... +version = "1.0.2" +build = "build.rs" + +[build-dependencies] +cc = "1" +``` + +*build.rs* +```rust +fn main() { + cc::Build::new() + .define("APP_NAME", "\"foo\"") + .define("VERSION", format!("\"{}\"", env!("CARGO_PKG_VERSION")).as_str()) + .define("WELCOME", None) + .file("src/foo.c") + .compile("foo"); +} +``` + +*src/foo.c* +```C +#include + +void print_app_info() { +#ifdef WELCOME + printf("Welcome to "); +#endif + printf("%s - version %s\n", APP_NAME, VERSION); +} +``` + +*src/main.rs* +```rust +extern { + fn print_app_info(); +} + +fn main(){ + unsafe { + print_app_info(); + } +} +``` +