cargoの条件付きコンパイルを試す

cargoの条件付きコンパイルを簡単なケースで試してみました。

cargoは`/src/bin`または`/examples`配下のファイルを実行することができますが、クレート本体のコンパイルエラーで実行できない場合がありました。
コンパイルエラーは修正しないままに`/src/bin`または`/examples`のファイルを実行したく、本記事の内容を調べました。[^1]

本記事で利用するRustのバージョンは1.82.0です。

```bash
$ rustc --version
rustc 1.82.0 (f6e511eec 2024-10-15)
$ cargo --version
cargo 1.82.0 (8f40fc59f 2024-08-21)
```

## 方法１：featuresを利用する

Cargo.tomlに下記を追記します。
```toml
[features]
wip = []
```

これで条件付きコンパイルに利用できるfeature`wip`を作成できましたので、下記のように利用できます。

```rust
fn main() {
    #[cfg(feature = "wip")]
    println!("something in wip feature.");
    #[cfg(not(feature = "wip"))]
    println!("something without wip feature.");
}
```

実行するときに、`--features`フラグで指定します。

```bash
cargo run --features wip
# something in wip feature.

cargo run
# something without wip feature.
```

参考：[Features - The Cargo Book](https://doc.rust-lang.org/nightly/cargo/reference/features.html)


ちなみにfeaturesは下記のような目的で利用されているようでしたので、目的に沿ったものであれば違和感なく利用できると思います。

- ビルド時間とファイルサイズを最適化する
- ライブラリの機能を拡張する
- [`no_std`属性](https://doc.rust-lang.org/nightly/reference/names/preludes.html#the-no_std-attribute)を補助する
- 依存関係を再公開する
- Cライブラリの依存先選択肢を提供する
- ライブラリの機能に優先度付けする
- 手続きマクロを分離する
- 実験的機能を分離する

参考：[Features Examples - The Cargo Book](https://doc.rust-lang.org/nightly/cargo/reference/features-examples.html)

## 方法２：定義済みの環境変数を利用する

cargoプロジェクトのルートディレクトリに配置する`build.rs`を利用します。
`build.rs`はBuild Scriptsと呼ばれ、パッケージビルドが開始する前に実行されます。


`build.rs`を作成し、下記を記述します。

```rust
fn main() {
  // lintの`#[warn(unexpected_cfgs)]`を抑制する
    println!("cargo::rustc-check-cfg=cfg(CUSTOM_VAR)");
    // 変数が変更された場合のみBuild Scriptsを実行する
    println!("cargo::rerun-if-env-changed=CUSTOM_VAR");

    if std::env::var("CUSTOM_VAR").is_ok() {
        println!("cargo::warning=Succeeded."); // 動作確認用
        println!("cargo::rustc-cfg=CUSTOM_VAR");
    } else {
        println!("cargo::warning=Something is wrong."); //動作確認用
    }
    let mut file = File::create("example.txt").unwrap(); // 動作確認用
    file.write_all(b"some text").unwrap(); // 動作確認用
}
```
参考：[Build Scripts - The Cargo Book](https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html)

これで環境変数`CUSTOM_VAR`を下記のように利用できます。

```rust
fn main() {
    #[cfg(CUSTOM_VAR)]
    println!("CUSTOM_VAR is valid.");
    #[cfg(not(CUSTOM_VAR))]
    println!("CUSTOM_VAR is invalid.");
}
```

```bash
cargo run               # CUSTOM_VAR is invalid.

# cargoコマンドの前に記述すると、その実行時のみ変数を設定できます
CUSTOM_VAR=1 cargo run  # CUSTOM_VAR is valid.
cargo run               # CUSTOM_VAR is invalid.

# シェル上で環境変数を宣言する
export CUSTOM_VAR=1
cargo run               # CUSTOM_VAR is valid.
# 環境変数の値が同じなのでBuild Scriptsが再実行されません
cargo run               # CUSTOM_VAR is valid.
unset CUSTOM_VAR
cargo run               # CUSTOM_VAR is invalid.
```



ちなみに環境変数の宣言は`.cargo/config.toml`を使っても可能でした。
cargoプロジェクトのルートディレクトリに`.cargo/config.toml`を作成します。
下記でcargoが認識する形で環境変数を定義できます。ただしキャッシュが効いており、値を更新してもBuild Scriptsへは反映されませんでした。

```toml
[env]
CUSTOM_VAR="1"
```
参考：[Configuration - The Cargo Book](https://doc.rust-lang.org/nightly/cargo/reference/config.html#env)

## あとがき


cargoで利用できる環境変数を調べるきっかけになりました。
ちなみに`CARGO_BIN_NAME`は`build.rs`実行時に未設定のため利用することができませんでした。代わりに[`build.rs`で利用できる環境変数](https://doc.rust-lang.org/nightly/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts)があります。
何かの参考になれば幸いです。

[^1]: 簡単なコードなら[`todo!()`](https://doc.rust-lang.org/std/macro.todo.html)で実現できます。