Open1

rustでビルドするbinaryごとに--versionで出力する値を変える

yunayuna

以下のようなディレクトリ構造でビルドし、各バイナリごとにbinary_name --versionで実行時、各バイナリに指定した別のバージョンを取得できるようにしたい。

sample_module
 - bin
   - binary1.rs
   - binary2.rs

その実装の一例です。

clapを使った前提

clap crateを使って、--versionでの出力情報を設定することができる。
最もシンプルな実装としては、以下の通り

binary1.rs
use clap::{Parser};

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
    #[arg(short, long)]
    mode: Option<String>,
}

fn main() {
    let args = Args::parse();
}

この実装だと、
binary_name --versionで、
親モジュールのCargo.toml内で指定した[package]内のversion=xxx 情報を自動的に取得し、表示される。

が、binary毎に別のバージョンを指定することはできない。

各binaryに別のバージョンを設定する

  • clapでは、versionに具体的な値を指定することができる
  • rustでは、ビルド時の環境変数から、値を取得することができる
  • rsutでは、ビルド時にbuild.rs で前処理を行い、環境変数に値をセットすることができる

以上を利用して、以下の方針を立てた

  • 各バイナリのバージョン情報を記録する version.toml を作成する
  • build.rs で、version.tomlから情報を取得し、環境変数に値をセット
  • clapのversion情報に、std::env!を使ってビルド時の環境変数から値をセットする

実装

[versions]
binary1 = "0.0.1"
binary2 = "0.0.2"
build.rs
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use serde::Deserialize;
use toml;

#[derive(Deserialize)]
struct Versions {
    versions: HashMap<String, String>,
}

fn main() {
    let mut file = File::open("version.toml").expect("Failed to open version.toml");
    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .expect("Failed to read version.toml");

    let versions: Versions = toml::from_str(&contents).expect("Failed to parse ver.toml");
    
    for (key, value) in versions.versions.iter() {
        //ビルドスクリプト内では、cargo:という接頭辞を持つprintln!はCargoによって特別に解釈されます。
        //rustc-envは、ビルド実行時に有効な環境変数としてセットされます。
        println!("cargo:rustc-env=VERSION_{}={}", key.to_uppercase(), value);
    }
}
binary1.rs
use clap::{Parser};

#[derive(Parser, Debug)]
#[command(author, version=std::env!("VERSION_BINARY1"), about, long_about = None)]
//--versionで出力される名称も、パッケージ名でなくバイナリ名にしたい場合、nameも指定する
//#[command(author, name="binary1", version=std::env!("VERSION_BINARY1"), about, long_about = None)]
struct Args {
    #[arg(short, long)]
    mode: Option<String>,
}

fn main() {
    let args = Args::parse();
}