5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Nix:Rustのビルドの仕組みと改善

Last updated at Posted at 2024-12-06

この記事はNix Advent Calendar 2024の6日目の記事です。

概要

NixでRustアプリケーションのパッケージ化時に遭遇したエラーとその解決方法を紹介します。そのために、NixにおけるRustのビルドのフローを確認し、エラーの発生箇所を明らかにし、修正方法を説明します。

今回の問題を解決するために、パッチを作成しました。

導入

Rustでは、同じパッケージ名で異なるバージョンや、同一のバージョンで異なるGitリポジトリを持つことが可能です。たとえば、次のようにCargo.tomlに記述できますが、Nixのビルドではエラーが発生します。

[dependencies]
rand = "0.8"
rand_07 = { package = "rand", version = "0.7" }
rand_08 = { package = "rand", version = "0.8", git = "https://github.com/rand/xxx", rev="xx" }

Nixでのビルドでは、RustのCargoはインターネットに直接アクセスできません。これは、Nixのビルド環境がインターネットアクセスを制限しているためです。そのため、cargo vendorというコマンドを使用してローカルに関連パッケージをダウンロードし、それをもとにビルドを行います。しかし、上記のような重複したパッケージを使うプロジェクトにおいて、cargo vendorを使用してビルドすると、通常のcargo buildでは成功するプロジェクトが失敗することがあります。

そのような場合、Nixでのパッケージ化は困難を伴います。具体的には、重複したパッケージの除去や、cargo vendorが生成したファイルを手動で修正する必要が出てきます。

次に、なぜNixのビルドフローが通常のビルドフローと異なるのかを説明し、NixにおけるRustのビルドフローとその問題点を紹介したうえで、修正方法について解説します。

なぜNixにおけるRustのビルドで問題が起こるのか?

通常のビルドフロー(cargo build)では、Cargoが必要なパッケージをその都度ダウンロードします。一方、Nixではcargo vendorコマンドを使用してローカルにパッケージをダウンロードします。この際、パッケージはパッケージ名+バージョンのディレクトリに配置されます。しかし、Gitのハッシュが異なるパッケージであっても、同じパッケージ名+バージョンのディレクトリが使用されるため、衝突が発生し、パッケージの配置に失敗します。

cargo vendorがCargoに組み込まれる前のcargo-vendorツールには、Gitハッシュによる衝突を回避するオプション(--no-merge-sources)がありましたが、Cargoに組み込まれた際にその機能は削除されました。そのため、Nixでのビルドが成功したり失敗したりする状況が続いていました。

この問題は現在のcargo vendorに起因しており、NixのRustビルドフレームワークが変更されても、本質的な解決には至っていないと考えられます。

NixにおけるRustのビルドフロー

nixpkgsのpkgs/build-support/rust/build-rust-packageには、buildRustPackageが定義されており、Rustプロジェクトのビルドが管理されています。

NixにおけるRustのビルドフローは、以下のような手順で行われます。フローの簡略化のため、Cargo.lockを使う場合で説明します。

  1. cargo-vendor-dirの作成: importCargoLockを使ってcargo-vendor-dirを作成します。
  2. importCargoLockの中でCargoのパッケージ(Crate)をcrates.ioから取り出します。そこにないものはoutputHashesから検索し、Gitからダウンロードしようとしますが、Gitのハッシュが見つからないパッケージがあればエラーで失敗します。
  3. importCargoLockの中でCargoのパッケージをダウンロードしたあと、ワークスペースの設定のための親のCargo.tomlと子のCargo.tomlの融合をreplace-workspace-values.pyで行います。この融合では、ワークスペース全体で共通の依存関係やバージョンを管理し、各サブプロジェクトで整合性のあるビルドができるようにする目的があります。融合したパッケージをcargo-vendor-dirの中に配置します。
  4. importCargoLockの中でオフラインでビルドするための.cargo/config.tomlファイルを生成します。
  5. ビルドの実行: buildRustPackage関数のnativeBuildInputsに設定されているcargoBuildHookを使ってcargo buildを実行し、ビルドします。
  6. 成果物の生成: ビルドが成功すると、バイナリやその他の成果物がNixストアに配置され、再利用可能な形で保存されます。

問題の修正方法

cargo-vendorのGitハッシュによる衝突を回避するオプション(--no-merge-sources)の振る舞いを再現するスクリプトを用意することで、この問題を解決しました。Gitハッシュによる衝突を回避するように、Gitハッシュを含めたディレクトリ名にパッケージ名+バージョンを配置し、重複による衝突を避けることで、すべての依存パッケージを正しく配置できるようにしました。

outputHashesも従来はパッケージ名+バージョンに対するハッシュ値しか設定できませんでしたが、パッケージ名+バージョン+Gitハッシュに対するハッシュ値を設定できるようにしました。

outputHashes = {
  "quantization-0.1.0" = "sha256-ggVqJiftu0nvyEM0dzsH0JqIc/Z1XILyUSKiJHeuuZs=";
  "tonic-0.9.2" = "sha256-ZlcDUZy/FhxcgZE7DtYhAubOq8DMSO17T+TCmXar1jE=";
  "wal-0.1.2-acaf1b2ebd5de3a871f4d2c48e13fc8788ffa43b" = "sha256-CeHQWHUVsHZvIy/7ftDWzbJ7BTARjsKvWHinEjhgL10=";
  "wal-0.1.2-fad0e7c48be58d8e7db4cc739acd9b1cf6735de0" = "sha256-nBGwpphtj+WBwL9TmWk7qXiEqlIWkgh/2V9uProqhMk=";
};

suiqdrantといったパッケージで修正を確認しました。

まとめ

NixにおけるRustのビルドで遭遇するパッケージの衝突問題について、その原因と解決方法を紹介しました。今回のパッチにより、より安定したビルド環境を提供することが可能になります。この知見が、NixでRustのアプリケーションをパッケージ化する際の助けになれば幸いです。

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?