新增章 [Cookbook - 构建工具]

pull/737/head
sunface 3 years ago
parent 327e7c42f0
commit ff35468af6

@ -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 <stdio.h>
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<String> {
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 <stdio.h>
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();
}
}
```

Loading…
Cancel
Save