cargo buildで「Missing dependency: cmake」というエラーになったときの対処法


## はじめに

(この記事では、プロジェクトの依存関係（Cargo.tomlやCargo.lock）で`cmake`クレートが直接または間接的に使用されていることを前提とします。）

WindowsでRustの環境構築を進めていると、`cargo build` を実行した際に `error: failed to run custom build command for <crate-name>` のようなエラーと共に、「`Missing dependency: cmake`」というメッセージに遭遇することがあります。
これは、プロジェクトが依存しているクレートが内部でC/C++のコードを利用しており、そのビルドに `CMake` というツールが必要にもかかわらず、開発環境に `cmake` がインストールされていないか、`cmake` が内部で利用する C/C++ コンパイラ (gcc や [MSVC](https://visualstudio.microsoft.com/ja/vs/features/cplusplus/) など) が適切に設定されていない場合に発生します。


以下の記事でも、WindowsではCMakeがデフォルトでインストールされていないことが指摘されています。

https://users.rust-lang.org/t/cmake-as-a-common-external-dependency-for-rust-crates/18701

そこで、この記事では、このエラーの原因と、特に **VS Code の Dev Containers** を活用した解決策について紹介します。

## 想定読者

*   Rust 開発初心者
*   WindowsでのRustの環境構築がうまくいかない人
*   C/C++ライブラリに依存するクレート（例: `*-sys` クレート[1]）を利用していて `cargo build` 時に "Missing dependency: cmake" エラーに遭遇した方

（VS Code と Docker が使えることは前提にしています）

## TL;DR

*   Windows環境で `cargo build` を実行した際に "Missing dependency: cmake" と表示される場合、ビルドプロセスにCMakeを必要とするRustの依存クレートが存在し、C/C++のコンパイラなどの必要なツールがインストールされていないことが原因であると考えられる
*   ホストOSに直接 `cmake` をインストールするのはプロジェクト間の依存関係の衝突のリスクがあるため、**VS Code の Dev Containers** を使うのが個人的におすすめ

## なぜ "Missing dependency: cmake" エラーが発生するのか？

Rustプロジェクトの中には、既存のC言語やC++言語で書かれたライブラリ（例: OpenSSL, SQLiteなど）の機能を利用するために、それらをRustから呼び出すための「バインディング」を提供しているクレートがあります（`openssl-sys` や `sqlite3-sys` などが典型例）。
より詳細には、「[FFI (Foreign Function Interface)](https://qiita.com/termoshtt/items/0fa9959f9eb64b0907e2)」や「FFIバインディング」で調べてみることをお勧めします。

これらの `-sys` クレートの多くは、元のC/C++ライブラリをソースコードからビルドする機能を持っています。C/C++では、プロジェクトのビルド手順を管理するツールとして `CMake` が広く使われています。

`cargo build` を実行すると、Cargoは依存クレートのビルドスクリプトを実行します。このスクリプトが、依存するC/C++ライブラリのビルドを試みる過程で、以下のような問題が起きていると考えられます。

* 開発環境に `cmake` がインストールされていない
* CMakeが内部的に必要なC/C++コンパイラ(gcc, clangなど)がない

結果として、「Missing dependency: cmake」エラーが発生し、ビルドプロセス全体が失敗してしまいます。

### （補足）Rustが cmake クレートを使用して、C言語で書かれたコードをコンパイルするための手順 
（以下は、RustによるC言語のコンパイルの手順を、自分なりにまとめてみました。厳密には多少違うかもしれません。）

1. C言語で記述されたパッケージをビルドする際に、CMakeを使用してビルドするよう明示的に指定される
2. cmake クレートを使用することで、CMakeを利用するための[Config](https://docs.rs/cmake/latest/cmake/struct.Config.html) が作成される
3. CMake が内部的に、gccなどのCコンパイラ（ここは[target-triple](https://ryochack.hatenablog.com/entry/2017/10/01/163334)ごとに異なる）を用いて、コンパイルを試みる
4. この際に、実行環境のtarget-triple によって、C言語に対するABI（Application Binary Interface）が決定され、C言語がバイナリへとコンパイルされる[2]
5. Rust上で上記が行われているため、全体として、Rustのビルド時にCのコードもコンパイルできているように見える

## 解決策: Dev Containersの活用

この問題を解決する最も確実な方法は、VSCodeの **Dev Containers** 機能を利用することです。

### Dev Containers とは

[Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)は Visual Studio Code の拡張で、コンテナ内のファイル操作やコマンド実行を、ローカル環境と同じ使い心地で利用できるようにする拡張です。実行する Dockerfile や docker-compose.yml などをリポジトリの設定ファイルとして含めることで、同じ開発環境を再現・利用することがより容易になります。

### （今回のケースにおける）Dev Containers のメリット

1.  **Rust用のDocker公式イメージが使用できる**
    *   公式イメージ（例えば、[rust:1.85.1-bookworm](https://hub.docker.com/layers/library/rust/1.85.1-bookworm/images/sha256-bf7d87666c4da6eace19e06d21bc4859c6e2a5c97a21ac273b0e082112753cf0)）は、linux 上でRustやC/C++ コンパイラであるgcc がプリインストールされている環境を構築できます
    *   コンパイラがプリインストールされているのは、Windowsでの環境構築と比較して、最大のメリットといえます
1.  **Dockerに関する操作を必要としない**
    *   仮想環境の立ち上げであれば、Dockerコンテナにボリュームをマウントすることで、実現できます。しかし、Dockerコンテナの起動などのコマンドに関する知識が必要になります
    *   一方で、Dev Containers であれば、VS Code上での操作で仮想環境を完結させることができます。これにより、開発環境を再現性高く構築することができます

### Devcontainerの設定

**ディレクトリ構成**
```
root/
    └ .devcontainer/　← DevContainer 設定ディレクトリ
    |   ├ docker-compose.yml
    |	└ devcontainer.json
    └ src/ ← 作業ディレクトリ
```

**docker-compose.yml**
(src部分はPathに応じて変更してください)
```yaml
services:
  rust_devcontainer:
    image: rust:1.85.1-bookworm
    restart: always
    tty: true
    volumes:
      - ../src:/workspaces/src
    working_dir: /workspaces/src
```

**devcontainer.json**
```json
{
    "dockerComposeFile": [
        "docker-compose.yml"
    ],
    "service": "rust_devcontainer",
    "workspaceFolder": "/workspaces/src",
}
```

（Dev Containers の詳しい使い方は本題ではないので、以下の記事に譲ります）

https://zenn.dev/tom_tan/articles/9af1818fcde7d2

## まとめ

**VS Code の Dev Containers を利用して、プロジェクトごとに隔離された環境内にCMakeをインストールする** ことで、再利用性の高い形でのRustの環境構築ができます。また、C/C++コンパイラ などのOS依存性の高い部分を回避できるのも魅力的な部分だと感じています。

## 補足

[1]  Rustのクレートには、C言語で記述されたインターフェースを利用する必要のクレートがあり、それの接尾辞は`-sys` にする慣習があります
[2]  Rustコンパイラ（rustc）やCargoは、target-triple（例：`x86_64-unknown-linux-gnu`）を使用して、対象とするプラットフォームのABIやバイナリ形式に合わせたコードを生成することにより、クロスコンパイル時も適切なABIに従った関数呼び出しが可能にしています