构建时工具

本节介绍在编译箱子源代码之前,运行的“构建时”工具或代码。通常,构建时代码,位于build.rs文件,通常称为“构建脚本”。常见的用例,包括:Rust 代码生成,和捆绑的 C/C++/ASM 代码的编译。查看 crates.io 的 主分支文档,了解更多信息。

编译,并静态链接到捆绑的 C 库

cc-badge cat-development-tools-badge

为了适应项目,需要附加 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++库

cc-badge cat-development-tools-badge

链接捆绑的 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-badge cat-development-tools-badge

使用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();
    }
}