Инструменты времени компиляции
Этот раздел охватывает инструменты времени компиляции, или код который запускается до момента компиляции исходного кода крейта. По соглашению, код исполняемый во время сборки находится в файле build.rs и он часто называется "скриптом сборки". Распространённые случаи использования включают генерацию Rust кода и компиляцию связанного C/C++/asm кода. Смотрите документацию по этому вопросу на crates.io для дополнительной информации.
Компиляция и статическая компоновка с идущей в комплекте библиотекой на C
Чтобы покрыть сценарии, где требуется использовать дополнительный код на C, C++ или ассемблере, есть крейт cc, который предлагает простой API для компиляции включённого в проект кода на C/C++/asm в статические библиотеки (.a), которые затем могут быть статически скомпонованы rustc.
Следующий пример содержит некоторый код на C (src/hello.c) который будет использован в Rust
коде. Перед компиляцией кода на Rust файл запускается специальный "build" файл (build.rs),
определённый в Cargo.toml. Используя крейт cc будет создана статическая библиотека
(в данном случае libhello.a, смотрите compile
docs), которая затем может быть использована из кода на 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 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. Самые главные два отличия это когда вам нужно компилировать и компоновать библиотеку на C++ вы должны определить компилятор C++ с помощью специального метода cpp(true)
и также нужно предотвратить искажение имён компилятором 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 с использованием особых директив
На самом деле довольно просто собрать код на C с особыми директивами используя cc::Build::define
.
Этот метод принимает значение Option
, таким образом делая возможным определить такие директивы,
как, скажем, #define APP_NAME "foo"
или там #define WELCOME
(можно передать None
для определения
директивы без значения). Этот пример собирает файл на C с динамически определёнными директивами,
установленными в build.rs
и печатает "Welcome to foo - version 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();
}
}