知っておこう: stdenv
#Nix日本語コミュニティゼミ 第4回
話す人: asa1984.icon
stdenv
stdenv.mkDerivation
誰もが使ったことのある Nixpkgs 収録の関数
builtins.derivation のラッパー
builtins.derivation
Nix 言語で一番大事な関数
store derivation (.drv ファイル) を生成する
instantiation
nuenv (ニューエンヴ)
DeterminateSystems/nuenv
NixCon2023 で紹介された 実験プロジェクト
archive 済み
stdenv の bash をnushell に置き換えたもの
nushell
イケてるシェル
https://scrapbox.io/files/697b3fcbfb63e6bbccf92c9c.png
実装を見る
code:nuenv.nix
{
# The Nuenv build function. Essentially a wrapper around Nix's core derivation function.
mkNushellDerivation =
nushell: # nixpkgs.nushell (from overlay)
sys: # nixpkgs.system (from overlay)
{ name # The name of the derivation
, src # The derivation's sources
, packages ? # Packages provided to the realisation process
, system ? sys # The build system
, build ? "" # The build script itself
, debug ? true # Run in debug mode
, outputs ? "out" # Outputs to provide
, envFile ? ../nuenv/user-env.nu # Nushell environment passed to build phases
, ... # Catch user-supplied env vars
}@attrs:
let
# Gather arbitrary user-supplied environment variables
reservedAttrs = [
"build"
"debug"
"envFile"
"name"
"outputs"
"packages"
"src"
"system"
"__nu_builder"
"__nu_debug"
"__nu_env"
"__nu_extra_attrs"
"__nu_nushell"
];
extraAttrs = removeAttrs attrs reservedAttrs;
in
derivation ({
# Core derivation info
inherit envFile name outputs packages src system;
# Realisation phases (just one for now)
inherit build;
# Build logic
builder = "${nushell}/bin/nu"; # Use Nushell instead of Bash
args = ../nuenv/bootstrap.nu ; # Run a bootstrap script that then runs the builder
# When this is set, Nix writes the environment to a JSON file at
# $NIX_BUILD_TOP/.attrs.json. Because Nushell can handle JSON natively, this approach
# is generally cleaner than parsing environment variables as strings.
__structuredAttrs = true;
# Attributes passed to the environment (prefaced with __nu_ to avoid naming collisions)
__nu_builder = ../nuenv/builder.nu;
__nu_debug = debug;
__nu_env = ../nuenv/env.nu ;
__nu_extra_attrs = extraAttrs;
__nu_nushell = "${nushell}/bin/nu";
} // extraAttrs);
# An analogue to writeScriptBin but for Nushell rather than Bash scripts.
mkNushellScript =
nushell: # nixpkgs.nushell (from overlay)
writeTextFile: # Utility function (from overlay)
{ name
, script
, bin ? name
}:
let
nu = "${nushell}/bin/nu";
in
writeTextFile {
inherit name;
destination = "/bin/${bin}";
text = ''
#!${nu}
${script}
'';
executable = true;
};
}
これだけ?
たった85行
「Nix 言語側」で行っていることは少ない
大部分はシェルスクリプトで実行される
実はめっちゃプリミティブ: derivation 関数
特別扱いされる attribute
name: ストアパスに含められる
builder: 実行可能ファイルのパス
args: builder に与えられる引数
system: x86-64-linux など
etc...
Q. 勘のいい人「src, buildInputs, nativeBuildInputs, その他…どこに行った?」
A. 環境変数
stdenv がやっていること: 色んなものを環境変数にぶち込んでシェルスクリプトでガチャガチャする
nuenv の builder
bootstrap.nu を nushell で実行している
code:nuenv.nix
builder = "${nushell}/bin/nu"; # Use Nushell instead of Bash
args = ../nuenv/bootstrap.nu ; # Run a bootstrap script that then runs the builder
bootstrap.nu
1. nushell 定義のヘルパー関数の導入
2. PATH に nuenv 関連の実行可能ファイル追加
3. $__nu_builder (中身は builder.nu) 実行
code:bootstrap.nu
## Bootstrap script
# This script performs any necessary setup before the builder.nu script is run.
# Discover and load the .attrs.json file, which supplies Nuenv with all the
# information it needs to realise the derivation.
let here = $env.NIX_BUILD_TOP
let attrsJsonFile = if ($env.NIX_ATTRS_JSON_FILE | path exists) {
$env.NIX_ATTRS_JSON_FILE
} else {
$"($here)/.attrs.json"
}
let attrs = (open $attrsJsonFile)
# Copy all .nu helper files into the sandbox
for file in $attrs.__nu_env {
let filename = ($file | parse "{__root}-{filename}" | get filename.0)
let target = $"($here)/($filename)"
cp $file $target
}
# Set the PATH so that Nushell itself is discoverable. The PATH will be
# overwritten later.
$env.PATH = ($attrs.__nu_nushell | parse "{root}/nu" | get root.0)
# Run the Nushell builder
nu --commands (open $attrs.__nu_builder)
builder.nu
src を sandbox 内にコピー
code:builder.nu
# Copy sources into sandbox
if $nix.debug { info "Copying sources" }
for src in $drv.src { cp -r -f $src $nix.sandbox }
packages で指定したパッケージ群の PATH への追加/
code:builder.nu
let packagesPath = (
$drv.packages # List of strings
| each { |pkg| $"($pkg)/bin" } # Append /bin to each package path
| str join (char esep) # Collect into a single colon-separated string
)
$env.PATH = $packagesPath
フェーズ の順次実行
フェーズ
mkDerivation の再利用性を高めるためにビルドに関わるステップを分割している
unpackPhase: tarball の展開などソースコードの準備
buildPhase: ビルド
installPhase: $out に成果物を配置する過程
など
nuenv の場合 build だけ対応
stdenv と C/C++
stdenv は C/C++ プロジェクトを自動でビルドするために最適化されている
フェーズの初期値
C 言語のパッケージビルド向けに調整されている
tarball の展開、make の実行など
例えばGNU 製のパッケージの場合、名前・バージョン・ソースコードさえ指定すれば、あとは追加でほぼ何も書かずにビルド可能
nuenv でやっていないこと
共有ライブラリをいい感じにする
buildInputs に追加された共有ライブラリは NIX_LDFLAGS に追加される
バイナリファイルに共有ライブラリのストアパスを埋め込む
該当バイナリ実行時、ローダーが RPATH から共有ライブラリを探して動的リンク
RPATH: バイナリ自身に共有ライブラリを探すためのパスを埋め込める
patchelf で不要な RPATH を削ったりする
bootstrap
stdenv.mkDerivation で使っている bash は誰がビルドしとんねん問題
Nixpkgs を見ると、builtins.derivation を直に使って stdenv に必要なパッケージをビルドしている箇所がある
asa1984.icon コンパイラの bootstrap と同様、いい感じになってるでしょう(調査放棄)