Aug 28, 2017

Сборка пакета для Nix

Nix - замечательный пакетный менеджер, который работает на MacOS и любом дистрибутиве Linux, причем из-за своей функциональной чистоты и изоляции зависимостей, гарантировано выдает всегда одинаковый результат.

На сегодняшний день Nix имеет огромную базу пакетов, которая ежедневно пополняется. Для сборки своего пакета потребуется знание языка Nix expression. Это чистый функциональный язык созданный специально для пакетного менеджера Nix и операционной системы NixOS.

Примечание

Для примера создадим сборку пакета языка программирования Red. Будем следовать оф. документации https://nixos.org/nixpkgs/manual/#chap-quick-start.

1. Скачиваем исходники

$ git clone git://github.com/NixOS/nixpkgs.git
$ cd nixpkgs

2. Добавляем директорию с новым пакетом

См.также

Правила именования файлов https://nixos.org/nixpkgs/manual/#sec-organisation

$ mkdir pkgs/development/interpreters/red/

3. Создаем файл описания пакета default.nix

Заготовка для нашего пакета будет выглядеть так:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{ stdenv, fetchFromGitHub }:

stdenv.mkDerivation rec {
  src = fetchFromGitHub {
    rev = "6a43c767fa2e85d668b83f749158a18e62c30f70";
    owner = "red";
    repo = "red";
    sha256 = "1zh6xc728bs7r4v5jz1jjrdk0xd838xsxmvy9gfg75a3zffm0slr";
  };

  configurePhase = ''
  '';

  buildPhase = ''
  '';

  installPhase = ''
  '';

  meta = with stdenv.lib; {
    description = ''
      New programming language strongly inspired by Rebol, but with a
      broader field of usage thanks to its native-code compiler, from system
      programming to high-level scripting, while providing modern support for
      concurrency and multi-core CPUs
    '';
    maintainers = with maintainers; [ uralbash ];
    platforms = [ "i686-linux" "x86_64-linux" ];
    license = licenses.bsd3;
    homepage = http://www.red-lang.org/;
  };
}

В первой строке указан импорт необходимых нам функций.

{ stdenv, fetchFromGitHub }:

Далее забираем исходники с GitHub при помощи функции fetchFromGitHub.

src = fetchFromGitHub {
  rev = "6a43c767fa2e85d668b83f749158a18e62c30f70";
  owner = "red";
  repo = "red";
  sha256 = "1zh6xc728bs7r4v5jz1jjrdk0xd838xsxmvy9gfg75a3zffm0slr";
};

Что бы посчитать хэш (sha256) нам потребуется утилита nix-prefetch-git.

$ nix-prefetch-git https://github.com/red/red 6a43c767fa2e85d668b83f749158a18e62c30f70
Initialized empty Git repository in /tmp/git-checkout-tmp-Jzca0REG/red-6a43c76/.git/
fatal: Invalid refspec '+refs/tags/v0.6.3^{}'
remote: Counting objects: 52473, done.
remote: Total 52473 (delta 0), reused 0 (delta 0), pack-reused 52473
Receiving objects: 100% (52473/52473), 20.39 MiB | 1.50 MiB/s, done.
Resolving deltas: 100% (35793/35793), done.
From https://github.com/red/red
Switched to a new branch 'fetchgit'
removing `.git'...

git revision is 6a43c767fa2e85d668b83f749158a18e62c30f70
path is /nix/store/qp10dy2plvskbv0zjpcg9mmrsszd1a6i-red-6a43c76
git human-readable version is v0.6.3
Commit date is 2017-07-17 21:25:27 +0800
hash is 1zh6xc728bs7r4v5jz1jjrdk0xd838xsxmvy9gfg75a3zffm0slr
{
  "url": "https://github.com/red/red",
  "rev": "6a43c767fa2e85d668b83f749158a18e62c30f70",
  "date": "2017-07-17T21:25:27+08:00",
  "sha256": "1zh6xc728bs7r4v5jz1jjrdk0xd838xsxmvy9gfg75a3zffm0slr",
  "fetchSubmodules": true
}

В конце файла указывается мета информация:

meta = with stdenv.lib; {
  description = ''
    New programming language strongly inspired by Rebol, but with a
    broader field of usage thanks to its native-code compiler, from system
    programming to high-level scripting, while providing modern support for
    concurrency and multi-core CPUs
  '';
  maintainers = with maintainers; [ uralbash ];
  platforms = [ "i686-linux" "x86_64-linux" ];
  license = licenses.bsd3;
  homepage = http://www.red-lang.org/;
};

Сама сборка происходит в 3 директивах configurePhase, buildPhase, installPhase. Если их не указывать, то по умолчанию будут выполняться ./configure, make, make install соответственно. Т.к. Red не использует autotools для сборки, то нам придется вручную прописать все необходимые шаги. Что бы не изобретать велосипед возьмем за основу пакет из Arch Linux https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=red.

Red требует 32х битный curl, добавим его в окружение сборки:

{ stdenv, stdenv_32bit, pkgsi686Linux, fetchFromGitHub, fetchurl }:

...

buildInputs = [ pkgsi686Linux.curl stdenv_32bit ];

Скачаем Rebol. При помощи него собирается интерпретатор Red.

...

rebol = fetchurl {
  url = "http://www.rebol.com/downloads/v278/rebol-core-278-4-2.tar.gz";
  sha256 = "1c1v0pyhf3d8z98qc93a5zmx0bbl0qq5lr8mbkdgygqsq2bv2xbz";
};

r2 = "./rebol/releases/rebol-core/rebol";

...

В нашем случае, что добавлять в фазы установки имеет очень условных характер. Поделим следующим образом - в фазу конфигурации добавим распаковку Rebol:

configurePhase = ''
  # Download rebol
  mkdir rebol/
  tar -xzvf ${rebol} -C rebol/
  patchelf --set-interpreter \
      ${stdenv_32bit.cc.libc.out}/lib/32/ld-linux.so.2 \
      ${r2}
'';

Заметьте r2 это переменная, которую мы объявили выше. А PatchELF это мега крутая утилита, которой можно подменять динамические зависимости в бинарниках, не пересобирая их. Мы изменили путь до ld-linux.so.2 файла, т.к. в NixOS он имеет нестандартный путь.

Примечание

При этом если такой пакет собирать в Ubuntu то патчить rebol нет необходимости, он подхватит зависимость из стандартного пути. Но для большей универсальности это необходимо.

После условной конфигурации приступим к сборке интерпретатора Red. Все шаги выполняются относительно директории с исходными кодами с GitHub:

buildPhase = ''
  # Do tests
  #${r2} -qw run-all.r

  # Build test
  ${r2} -qw red.r tests/hello.red

  # Compiling the Red console...
  ${r2} -qw red.r -r environment/console/console.red

  # Generating docs...
  cd docs
  ../${r2} -qw makedoc2.r red-system-specs.txt
  ../${r2} -qw makedoc2.r red-system-quick-test.txt
  cd ../
'';

Установка:

installPhase = ''
  mkdir $out

  # Install
  install -d $out/opt/red
  find quick-test -type f -executable -print0 | xargs -0 rm
  cp -R * $out/opt/red/
  rm -rf $out/opt/red/rebol
  install -Dm755 console $out/bin/red
  install -Dm644 BSD-3-License.txt                          \
      $out/share/licenses/${name}/BSD-3-License.txt
  install -Dm644 BSL-License.txt                            \
      $out/share/licenses/${name}/BSL-License.txt
  install -Dm644 docs/red-system-quick-test.html            \
      $out/share/doc/${name}/red-system-quick-test.html
  install -Dm644 docs/red-system-specs.html                 \
      $out/share/doc/${name}/red-system-specs.html

  # PathElf
  patchelf --set-interpreter                            \
      ${stdenv_32bit.cc.libc.out}/lib/32/ld-linux.so.2  \
      $out/opt/red/console
  patchelf --set-rpath ${pkgsi686Linux.curl.out}/lib \
      $out/opt/red/console
  patchelf --set-interpreter                            \
      ${stdenv_32bit.cc.libc.out}/lib/32/ld-linux.so.2  \
      $out/bin/red
  patchelf --set-rpath ${pkgsi686Linux.curl.out}/lib \
      $out/bin/red

'';

4. Добавление в общий список пакетов

Почти все готово, осталось добавить новый пакет в общий список и собрать.

$ vim pkgs/top-level/all-packages.nix
...

rascal = callPackage ../development/interpreters/rascal { };

red = callPackage ../development/interpreters/red { };

regina = callPackage ../development/interpreters/regina { };

...

5. Сборка

Сборка пакета выполняется командой nix-build:

$ nix-build -A red

Или если необходимо его сразу установить в окружение:

$ nix-build -f . -iA red

Полный исходник выглядит так:

default.nix
{ stdenv, stdenv_32bit, pkgsi686Linux, fetchFromGitHub, fetchurl }:

stdenv.mkDerivation rec {
  name = "red-v${version}";
  version = "0.6.3";
  src = fetchFromGitHub {
    rev = "6a43c767fa2e85d668b83f749158a18e62c30f70";
    owner = "red";
    repo = "red";
    sha256 = "1zh6xc728bs7r4v5jz1jjrdk0xd838xsxmvy9gfg75a3zffm0slr";
  };

  rebol = fetchurl {
    url = "http://www.rebol.com/downloads/v278/rebol-core-278-4-2.tar.gz";
    sha256 = "1c1v0pyhf3d8z98qc93a5zmx0bbl0qq5lr8mbkdgygqsq2bv2xbz";
  };

  buildInputs = [ pkgsi686Linux.curl stdenv_32bit ];

  r2 = "./rebol/releases/rebol-core/rebol";

  configurePhase = ''
    # Download rebol
    mkdir rebol/
    tar -xzvf ${rebol} -C rebol/
    patchelf --set-interpreter \
        ${stdenv_32bit.cc.libc.out}/lib/32/ld-linux.so.2 \
        ${r2}
  '';

  buildPhase = ''
    # Do tests
    #${r2} -qw run-all.r

    # Build test
    ${r2} -qw red.r tests/hello.red

    # Compiling the Red console...
    ${r2} -qw red.r -r environment/console/console.red

    # Generating docs...
    cd docs
    ../${r2} -qw makedoc2.r red-system-specs.txt
    ../${r2} -qw makedoc2.r red-system-quick-test.txt
    cd ../
  '';

  installPhase = ''
    mkdir $out

    # Install
    install -d $out/opt/red
    find quick-test -type f -executable -print0 | xargs -0 rm
    cp -R * $out/opt/red/
    rm -rf $out/opt/red/rebol
    install -Dm755 console $out/bin/red
    install -Dm644 BSD-3-License.txt                          \
        $out/share/licenses/${name}/BSD-3-License.txt
    install -Dm644 BSL-License.txt                            \
        $out/share/licenses/${name}/BSL-License.txt
    install -Dm644 docs/red-system-quick-test.html            \
        $out/share/doc/${name}/red-system-quick-test.html
    install -Dm644 docs/red-system-specs.html                 \
        $out/share/doc/${name}/red-system-specs.html

    # PathElf
    patchelf --set-interpreter                            \
        ${stdenv_32bit.cc.libc.out}/lib/32/ld-linux.so.2  \
        $out/opt/red/console
    patchelf --set-rpath ${pkgsi686Linux.curl.out}/lib \
        $out/opt/red/console
    patchelf --set-interpreter                            \
        ${stdenv_32bit.cc.libc.out}/lib/32/ld-linux.so.2  \
        $out/bin/red
    patchelf --set-rpath ${pkgsi686Linux.curl.out}/lib \
        $out/bin/red

  '';

  meta = with stdenv.lib; {
    description = ''
      New programming language strongly inspired by Rebol, but with a
      broader field of usage thanks to its native-code compiler, from system
      programming to high-level scripting, while providing modern support for
      concurrency and multi-core CPUs
    '';
    maintainers = with maintainers; [ uralbash ];
    platforms = [ "i686-linux" "x86_64-linux" ];
    license = licenses.bsd3;
    homepage = http://www.red-lang.org/;
  };
}

Готовый пакет можно отправить в основную ветку nixpkgs. Перед этим лучше почитать как правильно делать pull request:

В следующей статье я постараюсь описать как собирать свой собственный NixOS дистрибутив с шахматами и балеринами.


Comments

comments powered by Disqus