Основы stdenv
Добро пожаловать на девятнадцатую пилюлю 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-buildbash -e default-builder.shsource $stdenv/setupgenericBuild
И это всё.
То, что вам надо знать о фазах stdenv есть в файле setup.
Серьёзно, найдите время его прочитать.
И не забывайте, что в руководстве по nixpkgs тоже есть важные документы.
В следующей пилюле
Мы поговорим о том, как добавить зависимости в наши пакеты с помощью buildInputs и propagatedBuildInputs, и о том, как влиять на зависимые сборки с помощью хуков настройки и окружения.
Эти концепции очень важны для понимания, как nixpkgs собирает пакеты.