构建时工具
本节介绍在编译箱子源代码之前,运行的“构建时”工具或代码。通常,构建时代码,位于build.rs文件,通常称为“构建脚本”。常见的用例,包括:Rust 代码生成,和捆绑的 C/C++/ASM 代码的编译。查看 crates.io 的 主分支文档,了解更多信息。
编译,并静态链接到捆绑的 C 库
为了适应项目,需要附加 C、C++ 或 ASM 的场景,cc箱子,提供了一个简单的 API,用于将捆绑的 C/C++/ASM 代码编译成静态库(.a),这样就可以静态链接到rustc.
下面的示例,包含一些绑定的 C 代码(src/hello.c),会用在 Rust 上。在编译 rust 源代码之前,“构建”文件(build.rs)可通过Cargo.toml指定运行。若使用cc箱子,一个静态库文件将被生成(在这情况下,就是libhello.a,若想知道更多,请看compile
文档)然后,在 Rust 中想使用该库,可以通过在extern
块中,定义外部函数签名。
由于绑定的 C 非常简单,因此只需要将一个源文件,传递给cc::Build
。 对于更复杂的构建需求,cc::Build
为指定的include
的路径,和额外的编译器flag
标志们,提供了一整套构建器方法。
Cargo.toml
[package]
...
build = "build.rs"
[build-dependencies]
cc = "1"
[dependencies]
error-chain = "0.11"
build.rs
extern crate cc; fn main() { cc::Build::new() .file("src/hello.c") .compile("hello"); // outputs `libhello.a` }
src/hello.c
#include <stdio.h>
void hello() {
printf("Hello from C!\n");
}
void greet(const char* name) {
printf("Hello, %s!\n", name);
}
src/main.rs
# #[macro_use] extern crate 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 run() -> Result<()> {
unsafe { hello() }
let name = prompt("What's your name? ")?;
let c_name = CString::new(name)?;
unsafe { greet(c_name.as_ptr()) }
Ok(())
}
#
# quick_main!(run);
编译,并静态链接到捆绑的 C++库
链接捆绑的 C++库,非常类似于链接捆绑的 C 库。编译和静态链接捆绑的 C++库的两个核心区别,是通过构建器方法cpp(true)
指定 C++编译器。通过在我们的 C++源文件的顶部,添加extern "C"
部分,防止 C++编译器的名称篡改。
Cargo.toml
[package]
...
build = "build.rs"
[build-dependencies]
cc = "1"
build.rs
extern crate cc; fn main() { cc::Build::new() .cpp(true) .file("src/foo.cpp") .compile("foo"); }
src/foo.cpp
extern "C" {
int multiply(int x, int y);
}
int multiply(int x, int y) {
return x*y;
}
src/main.rs
extern {
fn multiply(x : i32, y : i32) -> i32;
}
fn main(){
unsafe {
println!("{}", multiply(5,7));
}
}
带自定义设置,编译 C 库
使用cc::Build::define
自定义构建,捆绑的 C 代码很简单。 该方法采用Option
值,因此可以创建如下定义:#define APP_NAME "foo"
,以及#define WELCOME
(传递 None
作为一个缺乏值的定义)。这个例子构建了一个动态定义集的捆绑 C 文件,而定义集在build.rs
中,并会在运行使,打印欢迎使用 foo-版本 1.0.2“。Cargo 会设置一些环境变量,这对于某些自定义设置可能很有用。
Cargo.toml
[package]
...
version = "1.0.2"
build = "build.rs"
[build-dependencies]
cc = "1"
build.rs
extern crate cc; 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
#include <stdio.h>
void print_app_info() {
#ifdef WELCOME
printf("Welcome to ");
#endif
printf("%s - version %s\n", APP_NAME, VERSION);
}
src/main.rs
extern {
fn print_app_info();
}
fn main(){
unsafe {
print_app_info();
}
}