Добро пожаловать на десятую пилюлю Nix.
В предыдущей девятой пилюле мы познакомились с одной из мощных возможностей Nix: автоматическим обнаружения зависимостей времени выполнения.
Заодно мы завершили разработку пакета GNU hello.
В этой пилюле мы познакомимся с утилитой nix-shell и попробуем с её помощью взломать программу hello.
Мы узнаем, что nix-shell создаёт для нас изолированную среду с возможностью редактирования исходных файлов проекта, точно также, как nix-build создаёт изолированную среду во время сборки деривации.
В конце концов мы сделаем наш скрипт сборки более эргономичным, ориентируясь на возможности nix-shell
Что такое nix-shell?
Утилита nix-shell помещает нас в командную оболочку с настроенными переменными окружения, необходимыми для сборки деривации.
Она не запускает сборку; она готовит плацдарм для пошаговой сборки проекта.
Давайте вспомним, что в Nix у нас нет доступа к библиотекам или программам до тех пор, пока они не установлены с помощью nix-env.
Впрочем, установка библиотек через nix-env не считается хорошей практикой.
Вместо этого мы предпочитаем изолированное окружение для разработки, доступное благодаря утилите nix-shell.
Мы можем передать в nix-shell любое выражение Nix, возвращающее деривацию, но в переменной PATH, которая в конечном итоге попадёт в bash, не будет нужных нам утилит.
$ nix-shell hello.nix [nix-shell]$ make bash: make: command not found [nix-shell]$ echo $baseInputs /nix/store/jff4a6zqi0yrladx3kwy4v6844s3swpc-gnutar-1.27.1 [...]
Такая оболочка в лучшем случае бесполезна.
Было бы разумно ожидать, что программы, описанные в $buildInputs, попадают в PATH (в том числе и программа GNU make), но в нашем случае это не так.
Однако, у нас есть переменные окружения, которые мы установили в деривации, в частности $baseInputs, $buildInputs, $src и др.
Это значит, что мы можем запустить source с параметром builder.sh и она построит деривацию.
На этапе установки у вас может возникнуть ошибка, потому что ваш пользователь не имеет прав записи в /nix/store:
[nix-shell]$ source builder.sh ...
Деривация не установилась, но она была построена. Обратите внимание вот на что:
-
Мы запустили
builder.shи он выполнил все шаги сборки, включая настройкуPATH. -
Рабочий каталог — больше не временный каталог, созданный
nix-build, а каталог, в котором мы запустили оболочку. -
Таким образом,
hello-2.10был распакован в текущий каталог.
Мы можем войти в каталог hello-2.10 и запустить make, поскольку make теперь доступен.
Это подтверждает, что nix-shell помещает нас в оболочку с тем же (или очень похожим) окружением, что и во время сборки.
Сборщик для nix-shell
Предыдущие шаги требуют ручного запуска команд и не оптимизированы для работы с nix-shell.
Сейчас мы сделаем наш сборщик более дружественным по отношению к nix-shell.
Вот несколько пунктов, которые нам надо изменить.
Во-первых, когда мы загружаем builder.sh, мы загружаем его в текущий каталог.
Что мы действительно хотим, так это поместить builder.sh в хранилище Nix, поскольку утилита nix-build использует именно этот файл.
Корректный способ в том, чтобы передать в деривацию правильную переменную окружения.
(Обратите внимание, что переменная $builder уже определена, но она указывает на исполняемый файл bash вместо builder.sh.
Наш builder.sh передаётся в bash как аргумент.)
Во-вторых, мы не хотим запускать сборку полностью, мы собираемся всего лишь настроить окружение, нужное для ручной сборки проекта.
Так что мы можем разбить builder.sh на два файла: setup.sh для настройки окружения и настоящий builder.sh, который отправится в nix-build.
В процессе рефакторинга мы завернём этапы сборки в функции, чтобы придать больше структуры нашему дизайну.
Дополнительно, мы перенесём set -e из файла настройки в файл сборки.
Команда set -e в nix-shell раздражает, так как она завершает работу оболочки при возникновении ошибки.
Вот наш исправленный autotools.nix.
Примечательным является атрибут setup = ./setup.sh в деривации, который добавляет setup.sh в хранилище Nix и соответственно инициализирует переменную окружения $setup в сборщике.
pkgs: attrs: let defaultAttrs = { builder = "${pkgs.bash}/bin/bash"; args = [ ./builder.sh ]; setup = ./setup.sh; baseInputs = with pkgs; [ gnutar gzip gnumake gcc coreutils gawk gnused gnugrep binutils.bintools patchelf findutils ]; buildInputs = [ ]; system = builtins.currentSystem; }; in derivation (defaultAttrs // attrs)
Благодаря этому мы можем разделить builder.sh на setup.sh и builder.sh.
Задача builder.sh заключается в том, чтобы загрузить $setup и вызвать функцию genericBuild.
Всё остальное — небольшие изменения в скрипте bash.
Вот исправленная версия builder.sh:
set -e source $setup genericBuild
Вот новая добавленная версия setup.sh:
unset PATH for p in $baseInputs $buildInputs; do export PATH=$p/bin${PATH:+:}$PATH done function unpackPhase() { tar -xzf $src for d in *; do if [ -d "$d" ]; then cd "$d" break fi done } function configurePhase() { ./configure --prefix=$out } function buildPhase() { make } function installPhase() { make install } function fixupPhase() { find $out -type f -exec patchelf --shrink-rpath '{}' \; -exec strip '{}' \; 2>/dev/null } function genericBuild() { unpackPhase configurePhase buildPhase installPhase fixupPhase }
Наконец, вот hello.nix:
let pkgs = import <nixpkgs> { }; mkDerivation = import ./autotools.nix pkgs; in mkDerivation { name = "hello"; src = ./hello-2.12.1.tar.gz; }
Возвращаемся в nix-shell:
$ nix-shell hello.nix [nix-shell]$ source $setup [nix-shell]$
Теперь, скажем, вы можете запустить unpackPhase, которая распакует $src и зайдёт в каталог.
И вы можете запускать такие команды, как ./configure, make и т.д. вручную, или запускать фазы с помощью соответствующих функций.
Всё правильно: процесс настолько прост, насколько вам кажется. nix-shell собирает файл .drv и все его входные зависимости, и затем запускает командную оболочку с настроенными переменными окружения, необходимыми для сборки .drv.
В частности, переменные окружения в оболочке совпадают с теми, которые передаются в функцию derivation.
Заключение
С помощью nix-shell мы можем запустить изолированное окружение, подходящее для разработки проекта.
Это окружение предоставляет необходимые зависимости для оболочки разработчика, подобно тому, как nix-build предоставляет необходимые зависимости сборщику.
Дополнительно, мы можем собирать и отлаживать проект вручную, выполняя его пошагово, как мы делали бы в любой другой среде разработки.
Заметьте, что мы никогда не устанавливаем такие инструменты, как gcc или make в систему; эти инструменты и библиотеки изолированы и доступны попроектно.
В следующей пилюле
В следующей пилюле мы займёмся чисткой хранилища Nix.
Мы написали и построили деривации, которые затем добавили в хранилище Nix, но до сего момента мы не беспокоились о том, чтобы удалять неиспользуемые деривации из хранилища.
ссылка на оригинал статьи https://habr.com/ru/articles/839092/
Добавить комментарий