[Перевод] 15. Nix в пилюлях: Поисковые пути Nix
Добро пожаловать на пятнадцатую пилюлю 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
, и остальные, но, конечно, более проработанные. Сообщество постоянно улучшает эти утилиты, добавляя новые функции, которые помогают обрабатывать больше сценариев более общим способом.