[Перевод] 15. Nix в пилюлях: Поисковые пути Nix

525eebc2703c48f2ce5d5dfdd0644a42.jpg

Добро пожаловать на пятнадцатую пилюлю Nix. В предыдущей четырнадцатой пилюле мы познакомились с паттерном «переопределение», позволяющим создавать разные варианты дериваций с помощью параметров.

Полагая, что вы усвоили материал предыдущих постов, я надеюсь, вы готовы разобраться с тем, как устроен nixpkgs. На сначала мы должны найти nixpkgs в нашей системе! Так что первый шаг: узнаем о некоторых опциях и переменных окружения, которые используют утилиты Nix.

NIX_PATH

Переменная окружения NIX_PATH — одна из самых важных. Она очень похожа на переменную окружения PATH. Тот же синтаксис — несколько путей, разделённых символом :. Nix что-то ищет в этих каталогах, перебирая их слева направо.

Где нужен NIX_PATH? В выражениях Nix! Да, NIX_PATH нужен не столько для инструментария Nix, как такового, сколько для написания выражений Nix.

Например, в командной строке, когда вы запускаете ping, оболочка ищет программу в каталогах, перечисленных в PATH. Запущен будет первый найденный файл.

В Nix всё точно также, при небольшом отличии синтаксиса. Вместо того, чтобы ввести ping, вы вводите . Да, я знаю… вы уже подумали про . Что же, читайте дальше, скоро во всём разберёмся.

Для чего нужна переменная NIX_PATH? Выражения Nix могут ссылаться на «абстрактный» путь, такой как , и этот путь можно переопределить из командной строки.

Запуск nix-instantiate --eval поможет нам провести пару тестов, чтобы разобраться, как всё работает. Напоминаю, что nix-instantiate используется для выполнения выражений и генерации файлов .drv. Сейчас нам не надо собирать деривации, так что обычного выполнения будет достаточно.
Программу можно использовать для запуска одноразовых выражений.

Небольшая подделка

То, что мы сейчас сделаем, бессмысленно с точки зрения Nix, но поможет нам разобраться. Возьмём переменную PATH вместо NIX_PATH и попытаемся найти ping (или любую другую утилиту, если этой у вас нет).

$ nix-instantiate --eval -E ''
error: file `ping' was not found in the Nix search path (add it using $NIX_PATH or -I)
$ NIX_PATH=$PATH nix-instantiate --eval -E ''
/bin/ping
$ nix-instantiate -I /bin --eval -E ''
/bin/ping

Великолепно! При первой попытке Nix однозначно заявил, что не смог найти программу ни в одном из поисковых путей. Обратите внимание, что опция -I принимает в качестве параметра один каталог. Пути, добавленные через -I имеют приоритет на путями из NIX_PATH.

Помимо путей, в NIX_PATH может встретиться синтаксис »somename=somepath», которого нет в PATH. Он позволяет указать точное место нахождения программы и избавиться от поиска.

$ NIX_PATH="ping=/bin/ping" nix-instantiate --eval -E ''
/bin/ping
$ NIX_PATH="ping=/bin/foo" nix-instantiate --eval -E ''
error: file `ping' was not found in the Nix search path (add it using $NIX_PATH or -I)

Обратите внимание, что во втором случае Nix проверяет существование пути.

Путь к репозиторию

Вам ведь любопытно, да?

$ nix-instantiate --eval -E ''
/home/nix/.nix-defexpr/channels/nixpkgs
$ echo $NIX_PATH
nixpkgs=/home/nix/.nix-defexpr/channels/nixpkgs

У вас может быть другой путь, в зависимости от того, как вы добавляли каналы, и т. д. В этом, в общем-то, и суть. Переменная ссылается на каталог в файловой системе, определённый в переменной NIX_PATH.

Вы можете просмотреть этот каталог и убедиться, что там находится один из коммитов репозитория nixpkgs (подсказка: .version-suffix).

Переменная NIX_PATH экспортируется скриптом nix.sh. Именно поэтому я просил вас выполнить команду source nix.sh в предыдущих постах.

Возможно, вы задаётесь вопросом: могу ли я указать другой путь к nixpkgs, выполнив git checkout из репозитория nixpkgs? Да, вы можете, более того, я рекомендую это сделать. Займёмся этим в следующей пилюле.

Теперь попробуем определить путь к нашему репозиторию! Пусть все пакеты default.nix, graphviz.nix и прочие будут в каталоге /home/nix/mypkgs:

$ export NIX_PATH=mypkgs=/home/nix/mypkgs:$NIX_PATH
$ nix-instantiate --eval ''
{ graphviz = ; graphvizCore = ; hello = ; mkDerivation = ; }

Да, и nix-build принимает пути в угловых скобках. Сначала мы выполняем выражение для всего репозитория (из файла default.nix), а затем можем выбрать атрибут, в данном случае graphviz.

Несколько слов о nix-env

Команда nix-env отличается от nix-instantiate и nix-build. В то время, как nix-instantiate и nix-build требуют вычисления выражения Nix, nix-env — не требует.

Эта концепция может сбить с толку. Вам может показаться что nix-env использует NIX_PATH, чтобы найти репозиторий nixpkgs. Но нет.

Команда nix-env использует ~/.nix-defexpr, который по умолчанию также является частью NIX_PATH, что, на самом деле, всего лишь совпадение. Если вы очистите NIX_PATH, nix-env всё равно сможет искать деривации из-за ~/.nix-defexpr.

Так что если вы запустите nix-env -i graphviz в каталоге вашего репозитория, утилита установит пакет из nixpkgs. То же самое случится, если NIX_PATH будет указывать на ваш репозиторий.

Чтобы выбрать альтернативный путь вместо ~/.nix-defexpr, используйте опцию -f:

$ nix-env -f '' -i graphviz
warning: there are multiple derivations named `graphviz'; using the first one
replacing old `graphviz'
installing `graphviz'

Почему утилита вывела сообщение о другой деривации с именем graphviz? Потому что у обоих атрибутов graphviz и graphvizCore из вашего репозитория, имя деривации — «graphviz»:

$ nix-env -f '' -qaP
graphviz      graphviz
graphvizCore  graphviz
hello         hello

Обычно nix-env разбирает все деривации и использует имена дериваций, чтобы интерпретировать командную строку. Так что здесь «graphviz» соответствует двум деривациям.
Как и в случае с nix-build, вы можете использовать -A, чтобы программа искала имена атрибутов вместо имён дериваций:

$ nix-env -f '' -i -A graphviz
replacing old `graphviz'
installing `graphviz'

Эта форма, не только точнее, но и быстрее, поскольку nix-env не нужно проверять все деривации.

Для полноты картины: вы должны устанавливать graphvizCore с опцией -A, поскольку без неё выбор деривации неоднозначен.

Подведём итог. Бывает, что nix-env находит деривацию, отличную от той, которую находит nix-build. Даже если вы указали правильный путь в NIX_PATH, nix-env ищет деривации в ~/.nix-defexpr.

Почему nix-evn ведёт себя иначе? Я, на самом деле, не знаю. Думаю, что верен один из этих ответов:

  • nix-env пытается быть универсальной утилитой, поэтому не зависит от NIX_PATH, а ищет деривации в ~/.nix-defexpr.

  • nix-env позволяет слить несколько репозиторией в один файл ~/.nix-defexpr, благодаря чему можно получить доступ ко всем деривации на вашей машине.

Впрочем, дело может быть и в том, что разработчики nix-env постарались сделать утилиту дружелюбнее при обычных настройках пользователя. Речь об ошибке нельзя сопоставить имя деривации при установке (you cannot match a derivation name when installing), возникающей из-за неоднозначного имени деривации, как было описано выше.

Возможно, есть и другие причины, или дело в том, что так сложилось исторически. В любом случае, сейчас всё работает так, как работает.

Заключение

Переменная NIX_PATH содержит поисковые пути. Утилиты Nix используют их, когда встречают имя пакета в угловых скобках. Это позволяет использовать «абстрактные» пути в выражениях Nix и опредлять «конкретный» путь с помощью NIX_PATH или флага -I.

Утилита nix-env работает не так, как другие. Для поиска пакетов она использует не переменную NIX_PATH, а файл ~/.nix-defexpr. Будьте внимательны!

В целом, постарайтесь не злоупотреблять NIX_PATH. При написании собственных выражений Nix по возможности используйте относительные пути. Впрочем, при ссылке на  из собственного репозитория использовать NIX_PATH совершенно нормально.
А вот внутри репозитория используйте относительные пути, например, ./hello.nix.

В следующей пилюле

…мы, наконец, погрузимся в nixpkgs. Нам пригодятся все техники, с которыми мы познакомились в этом цикле. Это и mkDerivation, и callPackage, и override, и остальные, но, конечно, более проработанные. Сообщество постоянно улучшает эти утилиты, добавляя новые функции, которые помогают обрабатывать больше сценариев более общим способом.

© Habrahabr.ru