Nixでcargo-competeをwrap(パッケージング?)する
(この人またNixの記事書いてる…)(なんだかんだ日本語の情報が少ないから増やしたい)
始めにとりあえず成果物。
satler-git/sb-nix-cargo-compete – GitHub
packages.${system}.cargo-competepackages.${system}.cargo-compete-unwrappedpackages.${system}.wrapped-rustup
を含んでいる。
課題
普通のRustのソフトウェアなら、以下のようなNix式でビルドできる。
{
packages.cargo-compete-unwrapped = pkgs.rustPlatform.buildRustPackage {
pname = "cargo-compete";
version = "${cargo-compete-src.rev}";
buildInputs = [ pkgs.openssl ];
nativeBuildInputs = [ pkgs.pkg-config ];
src = cargo-compete-src;
cargoHash = "sha256-r5QjwexX7btgT31xn59vG91g8DSMoUKWbi+nQxIdTvo=";
doCheck = false; # tests in cargo-compete require network access
meta = {
description = "Unwrapped version of cargo-compete";
mainProgram = "cargo-compete";
};
};
}
ただなにが問題かというと cargo-compete は、 rustup を使用して以下のようにバージョンを指定するのだ。
rustup run 1.70.0 cargo run --bin a
しかし、Nixユーザーはrustupを入れていない場合があるから、下手に依存したくないということだ。
ただrustupに依存している問題を解決したい場合は buildInputs に含めればいい。しかし様々な問題が発生する。
- 上のコマンドを実行する場合、すでにtoolchainをインストールしていなければならないため失敗する。しかも
rustupはユーザーからは触れないから追加もできない rustupはデフォルトで~/.rustupフォルダを作る
解決策
rustupにする小細工
上のパッケージの wrapped-rustup で行っている小細工について。パッケージの定義は以下のようになっている。シェルスクリプトは普段書かないから、LLMと一緒に書いた。
{
packages.wrapped-rustup = pkgs.writeShellApplication {
name = "rustup";
runtimeInputs = with pkgs; [
rustup
];
# $2がrunの場合に--installを$2と$3の間に付ける
text = ''
if [ "''${1:-}" = "run" ]; then
# Shift the first argument (removing 'run')
command=$1
shift
# Ensure $2 and $3 exist
if [ $# -ge 3 ]; then
# Insert --install between $2 and $3
new_args=("$command" "$1" "--install" "$2" "$3" "''${@:4}")
rustup "''${new_args[@]}"
else
echo "Error: Missing required arguments for --install insertion."
exit 1
fi
else
rustup "$@"
fi
'';
};
}
やっていることはコメントの通りだが、 rustup run の時に 自動的に --install(toolchainを自動でインストールする) フラグをつけ、
それ以外のコマンドはそのまま通すようになっている。そしてこれを rustup という名前で保存している。
cargo-competeにする小細工
まず、 cargo-compete の定義は以下のようになっている(flake-parts を使っているから self')。
{
packages.cargo-compete = pkgs.stdenvNoCC.mkDerivation {
pname = "cargo-compete";
inherit (self'.packages.cargo-compete-unwrapped) version meta;
nativeBuildInputs = with pkgs; [
makeWrapper
];
src = null;
dontUnpack = true;
postFixup = ''
makeWrapper ${lib.getExe self'.packages.cargo-compete-unwrapped} $out/bin/cargo-compete \
--prefix PATH : ${
lib.makeBinPath (
with pkgs;
[
self'.packages.wrapped-rustup
gcc
]
)
} \
--run 'export PATH=$PATH:~/.cache/cargo-compete/rustup/bin' \
--run 'export RUSTUP_HOME=''${RUSTUP_HOME-~/.cache/cargo-compete/rustup}' \
--run 'export CARGO_HOME=''${CARGO_HOME-~/.cache/cargo-compete/rustup}' \
--run 'rustup default stable &> /dev/null'
'';
};
}
Nixのパッケージング向けの便利なパッケージ、 makeWrapper を使ってwrapした rustup と gcc(Rustのコンパイルの時に cc が必要)を PATH に加えている。さらに RUSTUP_HOME と CARGO_HOME を ~/.cache/cargo-compete/rustup にすることで ~/.rustup が作られないようにしている。また、cargo metadata が呼ばれるときのtoolchainを stable にしている。
makeWrapper はシェルスクリプトを書くまでもない簡単なwrapを作るのに便利。