Добро пожаловать на девятнадцатую пилюлю Nix. В предыдущей восемнадцатой пилюле мы изучили алгоритм, который Nix использует для генерации путей хранения и узнали, как он работает с фиксированными выходными путями.
Настало время исследовать nixpkgs
и, конкретно, одну из его основных дериваций — stdenv
.
Деривация stdenv
не считается какой-то особенной, но очень важна для репозитория nixpkgs
. Она служит базой для создания пакетов, поскольку подгружает зависимости для инструментария GCC, GNU make, утилит ядра, утилит patch
и diff
и т. д. Благодаря ей, нам доступны основные утилиты, необходимые для сборки огромной кучи программ, присутствующих в nixpkgs
в данный момент.
Что такое stdenv?
Прежде всего, stdenv
— это очень простая деривация:
$ nix-build '<nixpkgs>' -A stdenv
/nix/store/k4jklkcag4zq4xkqhkpy156mgfm34ipn-stdenv
$ ls -R result/
result/:
nix-support/ setup
result/nix-support:
propagated-user-env-packages
В ней всего лишь два файла: /setup
и /nix-support/propagated-user-env-packages
. Последний можно не принимать во внимание, поскольку, по факту, он пустой. Действительно важный файл — это /setup
.
Как такая простая деривация может включать в себя весь набор инструментов и базовых утилит для сборки пакетов? Взглянем на зависимости времени выполнения:
$ nix-store -q --references result
/nix/store/3a45nb37s0ndljp68228snsqr3qsyp96-bzip2-1.0.6
/nix/store/a457ywa1haa0sgr9g7a1pgldrg3s798d-coreutils-8.24
/nix/store/zmd4jk4db5lgxb8l93mhkvr3x92g2sx2-bash-4.3-p39
/nix/store/47sfpm2qclpqvrzijizimk4md1739b1b-gcc-wrapper-4.9.3
...
Как такое может быть? Пакет должен как-то ссылаться на другие пакеты. Оказывается, зависимости прописаны в файле /setup
:
$ head result/setup
export SHELL=/nix/store/zmd4jk4db5lgxb8l93mhkvr3x92g2sx2-bash-4.3-p39/bin/bash
initialPath="/nix/store/a457ywa1haa0sgr9g7a1pgldrg3s798d-coreutils-8.24 ..."
defaultNativeBuildInputs="/nix/store/sgwq15xg00xnm435gjicspm048rqg9y6-patchelf-0.8 ..."
Файл /setup
Помните наш обобщённый скрипт builder.sh
из восьмой пилюли? Он инициализировал переменную PATH
, распаковывал исходники и запускал для нас команды autotools
.
Файл /setup
из stdenv
— точно такой же. Он инициализирует несколько переменных окружения, таких как PATH
и создаёт несколько вспомогательных функций bash
для сборки пакетов. Давайте его прочитаем.
Чтобы было удобно запускать команды, в файле /setup
для наборов инструментов и утилит прописаны переменные окружения. Подобный трюк мы проворачивали с baseInputs
и buildInputs
, когда создавали универсальный скрипт сборки.
Сборка в stdenv
разбита на фазы: unpackPhase
, configurePhase
, buildPhase
, checkPhase
, installPhase
, fixupPhase
. Список фаз по умолчанию находится в функции genericBuild
.
Функция запускает эти фазы одну за другой. Все фазы — это, на самом деле, функции bash
, так что вы легко можете их прочитать.
У каждой фазы есть хуки для запуска команд до или после фазы. Фазы можно переопределить, переставить местами, да и в принципе сделать с ними что угодно, так как это просто код на bash
.
Как использовать этот файл? Как наш старый скрипт сборки. Чтобы проверить его, сделаем фиктивную пустую деривацию, запустим setup
из stdenv
с помощью source
, распакуем исходники hello
и скомпилируем их:
$ nix-shell -E 'derivation { name = "fake"; builder = "fake"; system = "x86_64-linux"; }'
nix-shell$ unset PATH
nix-shell$ source /nix/store/k4jklkcag4zq4xkqhkpy156mgfm34ipn-stdenv/setup
nix-shell$ tar -xf hello-2.10.tar.gz
nix-shell$ cd hello-2.10
nix-shell$ configurePhase
...
nix-shell$ buildPhase
...
Я очистил PATH
, чтобы ещё раз показать, что в stdenv
есть всё, что нужно для сборки пакетов autotools
, не имеющих других зависимостей.
Мы запустили функции configurePhase
и buildPhase
, и они отработали. Эти функции bash
имеют «говорящие» имена, их код вы можете найти в файле setup
.
Из чего состоит setup
До сих пор мы работали с обычными скриптами bash
. А что насчёт Nix? В репозитории nixpkgs
есть полезная функция, похожая на ту, которую мы написали в нашем старом скрипте. Она вызывает функцию деривации, подтягивая stdenv
и запуская genericBuild
.
Это stdenv.mkDerivation.
Обратите внимание, что stdenv
— это не только деривация, но и набор атрибутов, содержащий, в том числе, mkDerivation
. Это сделано для удобства.
Давайте напишем выражение hello.nix
, используя stdenv
:
with import <nixpkgs> { };
stdenv.mkDerivation {
name = "hello";
src = ./hello-2.10.tar.gz;
}
Не пугайтесь выражения with
. Оно подгружает репозиторий nixpkgs
в область видимости, чтобы мы могли напрямую использовать stdenv
. Это очень похоже на выражение hello
из пилюли 8.
Программа отлично собирается и работает:
$ nix-build hello.nix
...
/nix/store/6y0mzdarm5qxfafvn2zm9nr01d1j0a72-hello
$ result/bin/hello
Hello, world!
Сборщик stdenv.mkDerivation
Взглянем на скрипт сборки, используемый mkDerivation
. Вы можете найти код здесь в nixpkgs
:
{
# ...
builder = attrs.realBuilder or shell;
args =
attrs.args or [
"-e"
(attrs.builder or ./default-builder.sh)
];
stdenv = result;
# ...
}
Сравним его с нашей старой обёрткой над деривациями из предыдущих пилюль. Здесь пакет собирается с помощью bash
(переменная shell
), аргументом которого является default-builder.sh
. В набор атрибутов, уже присутствующему в деривации stdenv
, мы добавляем переменную окружения $stdenv
.
Вы можете открыть default-builder.sh и посмотреть, что он делает:
source $stdenv/setup
genericBuild
То же самое мы делали в десятой пилюлей чтобы из nix-shell
было удобно работать с деривациями. При входе в оболочку, файл setup
настраивает окружение, но не запускает сборку. А при запуске nix-build
он действительно запускает процесс сборки.
Чтобы получить ясное представление о переменных окружения, загляните в файл hello.drv
:
$ nix derivation show $(nix-instantiate hello.nix)
warning: you did not specify '--add-root'; the result might be removed by the garbage collector
{
"/nix/store/abwj50lycl0m515yblnrvwyydlhhqvj2-hello.drv": {
"outputs": {
"out": {
"path": "/nix/store/6y0mzdarm5qxfafvn2zm9nr01d1j0a72-hello"
}
},
"inputSrcs": [
"/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh",
"/nix/store/svc70mmzrlgq42m9acs0prsmci7ksh6h-hello-2.10.tar.gz"
],
"inputDrvs": {
"/nix/store/hcgwbx42mcxr7ksnv0i1fg7kw6jvxshb-bash-4.4-p19.drv": [
"out"
],
"/nix/store/sfxh3ybqh97cgl4s59nrpi78kgcc8f3d-stdenv-linux.drv": [
"out"
]
},
"platform": "x86_64-linux",
"builder": "/nix/store/q1g0rl8zfmz7r371fp5p42p4acmv297d-bash-4.4-p19/bin/bash",
"args": [
"-e",
"/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"
],
"env": {
"buildInputs": "",
"builder": "/nix/store/q1g0rl8zfmz7r371fp5p42p4acmv297d-bash-4.4-p19/bin/bash",
"configureFlags": "",
"depsBuildBuild": "",
"depsBuildBuildPropagated": "",
"depsBuildTarget": "",
"depsBuildTargetPropagated": "",
"depsHostBuild": "",
"depsHostBuildPropagated": "",
"depsTargetTarget": "",
"depsTargetTargetPropagated": "",
"name": "hello",
"nativeBuildInputs": "",
"out": "/nix/store/6y0mzdarm5qxfafvn2zm9nr01d1j0a72-hello",
"propagatedBuildInputs": "",
"propagatedNativeBuildInputs": "",
"src": "/nix/store/svc70mmzrlgq42m9acs0prsmci7ksh6h-hello-2.10.tar.gz",
"stdenv": "/nix/store/6kz2vbh98s2r1pfshidkzhiy2s2qdw0a-stdenv-linux",
"system": "x86_64-linux"
}
}
}
Он настолько короткий, что я решил вставить его полностью. Программа сборки — это bash
с аргументами -d default-builder.sh
. В файле вы видите переменные окружения src
и stdenv
.
Последняя фаза, с которой мы пока не сталкивались — это unpackPhase
. В setup
она используется для распаковки исходных кодов и перехода в каталог. Здесь снова всё точно также, как в наших старых скриптах сборки.
Заключение
Деривация stdenv
— это ядро репозитория nixpkgs
. Все пакеты используют обёртку stdenv.mkDerivation
вместо прямого вызова дериваций. Она выполняет разные операции и создаёт удобное окружение для сборки.
Процесс в целом прост:
nix-build
bash -e default-builder.sh
source $stdenv/setup
genericBuild
И это всё. То, что вам надо знать о фазах stdenv
есть в файле setup
.
Серьёзно, найдите время его прочитать. И не забывайте, что в руководстве по nixpkgs
тоже есть важные документы.
В следующей пилюле
Мы поговорим о том, как добавить зависимости в наши пакеты с помощью buildInputs
и propagatedBuildInputs
, и о том, как влиять на зависимые сборки с помощью хуков настройки и окружения. Эти концепции очень важны для понимания, как nixpkgs
собирает пакеты.