如何在编译到Rust二进制文件期间编译在命令行上提供的字符串?

Fre*_*nan 2 rust rust-cargo

我想这样做,以便当我的程序启动时,它打印到stderr:

This is Program X v. 0.1.0 compiled on 20180110. Now listening on stdin,
quit with SIGINT (^C). EOF is ignored. For licensing  information,  read 
LICENSE. To suppress this message, supply --quiet or --suppress-greeting
Run Code Online (Sandbox Code Playgroud)

在C/C++中,我将使用Makefile实现此目的,例如:

VERSION = 0.1.0
FLAGS = -Wall -pipe -O3 -funroll-loops -Wall -DVERSION="\"$(VERSION)\"" -DCOMPILED_AT="\"`date +%Y%m%d`\""
Run Code Online (Sandbox Code Playgroud)

然后,在源代码中,我会使用这些常量,因为我很高兴,也许是在调用fprintf.#ifdef当然,在检查它们是否确实存在之后.

如何在Rust中实现这一目标?我需要使用程序宏吗?我可以用cargo某种方式吗?

我知道env!("CARGO_PKG_VERSION")可以作为替代品VERSION,但是怎么样COMPILED_AT

Fre*_*nan 5

有两种方法可以做到这一点.

使用build.rs脚本

使用build.rs脚本的好处是编译程序的其他用户不必以cargo特殊方式调用或设置其环境.这是如何做到这一点的最小例子.

build.rs

use std::process::{Command, exit};
use std::str;

static CARGOENV: &str = "cargo:rustc-env=";

fn main() {
    let time_c = Command::new("date").args(&["+%Y%m%d"]).output();

    match time_c {
        Ok(t) => {
            let time;
            unsafe {
                time = str::from_utf8_unchecked( &t.stdout );
            }   
            println!("{}COMPILED_AT={}", CARGOENV, time);
        }   
        Err(_) => exit(1)
    }   
}
Run Code Online (Sandbox Code Playgroud)

src/main.rs

fn main() {
    println!("This is Example Program {} compiled at {}", env!("CARGO_PKG_VERSION"), env!("COMPILED_AT"));
}
Run Code Online (Sandbox Code Playgroud)

Cargo.toml

[package]
name = "compiled_at"
version = "0.1.0"
authors = ["Fredrick Brennan <copypaste@kittens.ph>"]
build = "build.rs"

[dependencies]
Run Code Online (Sandbox Code Playgroud)

显然,这可以调整,以使其在其他没有/bin/date或编译其他东西的平台上工作,如Git版本号.此脚本基于Jmb提供的示例,该示例演示了如何在编译时将Mercurial信息添加到程序中.

这可以保证COMPILED_AT将设置或构建失败.这样其他Rust程序员就可以通过这样做来构建cargo build.

[osboxes@osboxes compiled_at]$ cargo run
   Compiling compiled_at v0.1.0 (file:///home/osboxes/Workspace/rust/compiled_at)
    Finished dev [unoptimized + debuginfo] target(s) in 1.27 secs
     Running `target/debug/compiled_at`
This is Example Program 0.1.0 compiled at 20180202
Run Code Online (Sandbox Code Playgroud)

使用env!(),然后要求用户在构建之前设置其环境

在我问这个问题后尝试以下内容时,它发生了,它确实有效(vim正在使用,因此转义%s),cargo确实将我的环境传递给rustc:

COMPILED_AT=`date +\%Y\%m\%d` cargo run
Run Code Online (Sandbox Code Playgroud)

然后,在Rust:

fn main() {
    eprintln!("{}", env!("COMPILED_AT"));
}
Run Code Online (Sandbox Code Playgroud)

如果我不提供环境变量,Rust编译器拒绝编译代码,这是一个很好的接触.

error: environment variable `COMPILED_AT` not defined
   --> src/main.rs:147:21
    |
147 |     eprintln!("{}", env!("COMPILED_AT"));
    |                     ^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error
Run Code Online (Sandbox Code Playgroud)

这种方式非常hacky,并且保证会惹恼其他想要构建的Rust程序员cargo build,但它确实有效.如果可以的话,建议build.rs改用.

  • 我真的认为,不应该查看"货物"或货物脚本的自定义调用,而应该查看`build.rs`.`build.rs`的意图是专门用于"编写"构建,以便创建一个Rust项目始终是相同的命令(`cargo build`).这种一致性使第三方依赖的集成变得如此轻松,如果没有充分的理由就不应该丢弃它:) (3认同)