Nix управљач пакетима

Слајдови у изради

Проф. др Игор Дејановић (igord на uns ac rs)

Креирано 2026-01-30 Fri 12:43, притисни ESC за мапу, Ctrl+Shift+F за претрагу, "?" за помоћ

Садржај

1. Шта је nix?

  • Управљач пакетима и систем за изградњу пакета
  • Али и језик специфичан за домен изградње пакета
  • Декларативна спецификација пакета
  • Чист функционални језик са лењом евалуацијом
  • Поновљива изградња
  • На основу зависности -> деривација - дефинише комплетно окружење за поновљиву изградњу пакета
  • Производ изградње смешта се у непромењиво складиште са путањом која садржи хеш целокупног графа зависности
  • Ово омогућава атомичко ажурирање, конкурентно инсталирање више верзија истог пакета, ефикасно кеширање, враћање на старе верзије итд.

2. Шта може са nix-ом да се уради?

  • Поновљива развојна окружења.
  • Инсталација и/или покретање софтвера преко URL-а
  • Лак пренос окружења између рачунара
  • Декларативна спецификација линукс рачунара
  • Поновљиво интеграционо тестирање употребом виртуалних машина
  • Избегавање конфликта са већ инсталираним софтвером
  • Инсталација софтвера из изворног кода
  • Транспарентно кеширање бинарних производа изградње
  • Јака подршка за ревизију софтвера
  • Подршка за кроскомпајлирање
  • Удаљена изградња
  • Удаљено распоређивање
  • Атомичко ажурирање и повратак на стару верзију

2.1. Поновљива развојна окружења

2.1.1. Креирање шкољке са инсталираним зависностима

$ cowsay no can do
The program ‘cowsay’ is currently not installed.

$ echo no chance | lolcat
The program ‘lolcat’ is currently not installed.
$ nix-shell -p cowsay lolcat
these 3 derivations will be built:
  /nix/store/zx1j8gchgwzfjn7sr4r8yxb7a0afkjdg-builder.pl.drv
  /nix/store/h9sbaa2k8ivnihw2czhl5b58k0f7fsfh-lolcat-100.0.1.drv
  ...

[nix-shell:~]$
[nix-shell:~]$ cowsay Hello, Nix! | lolcat
...
# сада програми cowsay и lolcat постоје у окружењу

[nix-shell:~]$ exit

# када напустимо шкољку програми више нису доступни

$ echo all gone | lolcat
The program ‘lolcat’ is currently not installed.

2.1.2. Покретање програма једном

Ако желимо да покренемо програм само једном можемо то урадити на следећи начин:

$ nix-shell -p cowsay --run "cowsay Nix"

или ако се команда састоји само од имена програма нису потребни знаци навода:

$ nix-shell -p hello --run hello

2.1.3. Покретање комбинације програма

$ nix-shell -p git neovim nodejs
these 9 derivations will be built:
  /nix/store/7gz8jyn99kw4k74bgm4qp6z487l5ap06-packdir-start.drv
  /nix/store/d6fkgxc3b04m85wrhg6j0l5y0ray82l7-packdir-opt.drv
  /nix/store/da6njv7r0zzc2n54n2j54g2a5sbi4a5i-manifest.vim.drv
  /nix/store/zs4jb2ybr4rcyzwq0dagg9rlhlc368h6-builder.pl.drv
  /nix/store/g8sl2xnsshfrz9f39ki94k8p15vp3xd7-vim-pack-dir.drv
  /nix/store/jmxkg8b1psk52awsvfziy9nq6dwmxmjp-luajit-2.1.0-2022-10-04-env.drv
  /nix/store/kn83q8yk6ds74zgyklrjhvv5wkv5wmch-python3-3.10.9-env.drv
  /nix/store/m445wn3vizcgg7syna2cdkkws3kk1gq8-neovim-ruby-env.drv
  /nix/store/r2wa882mw99c311a4my7hcis9lq3kp3v-neovim-0.8.1.drv
these 151 paths will be fetched (186.43 MiB download, 1018.20 MiB unpacked):
  /nix/store/046zxlxhq4srm3ggafkymx794bn1jksc-bzip2-1.0.8
  /nix/store/0p1jxcb7b4p8jhhlf8qnjc4cqwy89460-unibilium-2.1.1
  /nix/store/0q4fpnqmg8liqraj7zidylcyd062f6z0-perl5.36.0-URI-5.05
  ...

# ова шкољка има доступан git, neovim и nodejs
[nix-shell:~]$

2.1.4. Провера верзија пакета

[nix-shell:~]$ which git
/nix/store/3cdi52xh6lk3h1fb51jkxs3p561p37wg-git-2.38.3/bin/git

[nix-shell:~]$ git --version
git version 2.38.3

[nix-shell:~]$ which nvim
/nix/store/ynskzgkf07lmrrs3cl2kzr9ah487lwab-neovim-0.8.1/bin/nvim

[nix-shell:~]$ nvim --version | head -1
NVIM v0.8.1

[nix-shell:~]$ which npm
/nix/store/q12w83z0i5pi1y0m6am7qmw1r73228sh-nodejs-18.12.1/bin/npm

[nix-shell:~]$ npm --version
8.19.2

2.1.5. Угњеждене шкољке

Ако приметимо да нам недостаје неки програм док смо у nix шкољци можемо доинсталирати потребан пакет употребом угњеждене шкољке:

[nix-shell:~]$ nix-shell -p python3
this path will be fetched (11.42 MiB download, 62.64 MiB unpacked):
  /nix/store/pwy30a7siqrkki9r7xd1lksyv9fg4l1r-python3-3.10.11
copying path '/nix/store/pwy30a7siqrkki9r7xd1lksyv9fg4l1r-python3-3.10.11' from 'https://cache.nixos.org'...

[nix-shell:~]$ python --version
Python 3.10.11

2.1.6. Поновљивост

  • Претходне команде нису поновљиве јер исте команде на разичитим рачунарима и у различито време позива могу креирати окружење са различитим верзијама пакета.
  • Да добијемо поновљиво окружење можемо урадити следеће:
nix-shell -p git --run "git --version" --pure -I nixpkgs=https://github.com/NixOS/nixpkgs/tarball/2a601aafdc5605a5133a2ca506a34a3a73377247

3. Nix складиште (Nix store)

  • Централно, глобално складиште свих изграђених пакета, деривација и извора.
  • Сваки елемент је јединствено идентификован криптографским хешом садржаја (content addressable). Путања: /nix/store/ХЕШ-имепакета.
  • Гарантује непроменљивост (immutability) и поновљивост (reproducibility). Промена зависности = нови хеш = нова путања.
  • Омогућава истовремену инсталацију различитих верзија истог пакета (side-by-side).
  • Garbage Collector (GC) уклања пакете који више нису референцирани.

3.1. Пример путање у /nix/store

/nix/store/jbz6j4iwnrvki1zl34hwcyj2i0m6l2y1-coreutils-9.8
  • jbz6j4iwnrvki1zl34hwcyj2i0m6l2y1 – 160-битни SHA-256 хеш (32 карактера у base32) изведен из свих улаза за изградњу пакета
  • coreutils-9.8 – људски-читљиво име пакета и верзија

Садржај директоријума:

bin/
├── ls
├── cp
├── rm
└── ... (остали coreutils алати)
libexec/...

Кључна својства:

  • Ако поново изградите исти пакет са истим улазима, добићете потпуно исту путању (исти хеш)
  • Два различита пакета могу делити датотеке преко хард линкова ако су идентичне
  • Потпуно изолован – нема општих апсолутних путања попут /lib64/libc.so.6 већ пакети користе специфичне путање за из Nix складишта.

4. Деривације

  • Рецепти који садрже све потребне информације за изградњу пакета:
    • Изворни код
    • Зависности (у општем случају друге деривације)
    • Заставице за изградњу (build flags)
    • Кораке за изградњу и инсталацију
    • Тестове
    • Променљиве окружења
  • Скуп свих директних и индиректних зависности назива се "затворење изградње" (build closure).
  • Користи се криптографски хеш свих информација за креирање јединственог имена и путање у Nix складишту.

4.1. Типови деривација

  • Fixed Output Derivations (FODs)
    • Дефинисани само својим садржајем. Немају зависности. Представљају листове затворења изградње.
    • Креирају се са fetch* функцијама у nixpkgs и builtins.
  • Input-Addressed Derivations
    • "Нормалне" деривације. Имају завиности и дефинисане кораке изградње и инсталације.
    • Креирају се са stdenv.mkDerivation и повезаним build* помоћним функцијама.
  • Content-Addressable Derivations (CA)
    • Експерименталне деривације
    • Хибридне. Имају особине претходне две.
    • Намена: оптимизација изградње графа зависности када дође до промене зависних деривација

4.2. Креирање деривација

  • Процес креирања деривација назива се "инстанцирање" или уопштеније "евалуација"
  • Сваки пакет у колекцији Nix пакета nixpkgs има одговарајућу деривацију.
  • Значи да можемо креирати и извршити инспекцију деривације свега што је извезено из nixpkgs.

    Пример:

    $ nix-instantiate '<nixpkgs>' -A hello
    /nix/store/rn30zy2bj4zka8axq0ipknfp1pbgnnd1-hello-2.12.1.drv
    
    # или са flakes, nix>=2.4
    
    $ nix eval nixpkgs#hello.drvPath
    "/nix/store/9vkdb4vqqyqjkm2825vkm74gdxcr0m46-hello-2.12.1.drv"
    

Обратите пажњу да ове две команде не морају бити еквивалентне јер <nixpkgs> не мора на исти начин да се разреши.

4.3. Садржај деривације

~> nix derivation show /nix/store/9vkdb4vqqyqjkm2825vkm74gdxcr0m46-hello-2.12.1.drv
{
  "/nix/store/9vkdb4vqqyqjkm2825vkm74gdxcr0m46-hello-2.12.1.drv": {
    "args": [
      "-e",
      "/nix/store/v6x3cs394jgqfbi0a42pam708flxaphh-default-builder.sh"
    ],
    "builder": "/nix/store/p6k7xp1lsfmbdd731mlglrdj2d66mr82-bash-5.2p37/bin/bash",
    "env": {
      "__structuredAttrs": "",
      "buildInputs": "",
      "builder": "/nix/store/p6k7xp1lsfmbdd731mlglrdj2d66mr82-bash-5.2p37/bin/bash",
      ...
      "src": "/nix/store/pa10z4ngm0g83kx9mssrqzz30s84vq7k-hello-2.12.1.tar.gz",
      "stdenv": "/nix/store/m1p78gqlc0pw3sdbz3rdhklzm0g26g96-stdenv-linux",
      "strictDeps": "",
      "system": "x86_64-linux",
      "version": "2.12.1"
    },
    "inputDrvs": {
      "/nix/store/bfhj6w6zvy1v7l6dm6ppsk2hrlcrkyfl-hello-2.12.1.tar.gz.drv": {
        "dynamicOutputs": {},
        "outputs": [
          "out"
        ]
      },
      "/nix/store/s63zivn27i8qv5cqiy8r5hf48r323qwa-bash-5.2p37.drv": {
        "dynamicOutputs": {},
        "outputs": [
          "out"
        ]
      },
      ...
    },
    "inputSrcs": [
      "/nix/store/v6x3cs394jgqfbi0a42pam708flxaphh-default-builder.sh"
    ],
    "name": "hello-2.12.1",
    "outputs": {
      "out": {
        "path": "/nix/store/1q8w6gl1ll0mwfkqc3c2yx005s6wwfrl-hello-2.12.1"
      }
    },
    "system": "x86_64-linux"
  }
}

4.4. Особине деривација

  • Опис како изградити пакет из изворног кода
  • Пуне путање излаза су дефинисане пре почетка процеса изградње
  • Све зависности су разрешене приликом инстанцирања
  • Све додатне заставице су експлицитне
  • Нема неодређености. Систем, архитектура и друге опције су разрешене
  • Деривација је непромењива (immutable). Ако се деривација промени то је нова деривација.
  • Путања деривација је јединствена. Ако се две деривације подударају са путањама онда су једнаке у сваком погледу (исте зависности, исте заставице…)

4.5. Реализација деривације

  • Изградња деривације се назива и "реализација" (realization).
  • Деривација представља план изградње. Њеном реализацијом настаје циљни објекат изградње.

    ~> nix-store --realize  /nix/store/9vkdb4vqqyqjkm2825vkm74gdxcr0m46-hello-2.12.1.drv
    warning: you did not specify '--add-root'; the result might
             be removed by the garbage collector
    /nix/store/1q8w6gl1ll0mwfkqc3c2yx005s6wwfrl-hello-2.12.1
    

5. Nix језик

  • Чисто функционалан, декларативан језик са лењом евалуацијом.
  • Специјализован за дефинисање пакета и конфигурација система.
  • Нема петље, имена/идентификатори су непромењиве (immutable).
  • Главни циљ: описати како се нешто гради.

5.1. Основни типови података

  • String: "Hello, Nix" или ''multi-line string''
  • Integer: 42
  • Boolean: true, false
  • Path: /putanja/do/fajla или ./relativna/putanja (аутоматски се копира у Nix складиште)
  • Null: null
"Ovo je string."
''
  Ovo je
  string u više
  redova.
''
/etc/passwd
./default.nix

5.2. Листе (Lists)

  • Низ вредности било ког типа.
  • Дефинишу се у угластим заградама, елементи су одвојени размаком.
[ 1 2 3 ]
[ "hello" "world" ]
[ 1 "dva" true null ]
[ [ "ugnjezdena" ] "lista" ]

5.3. Скупови атрибута (Attribute Sets)

  • Кључна структура у Nix-у, слична мапама или објектима у другим језицима.
  • Колекција парова кључ-вредност.
  • Кључеви су стрингови, вредности могу бити било ког типа.
{
  name = "moj-paket";
  version = "1.0";
  enableTests = true;
  dependencies = [ "openssl" "zlib" ];
}

5.4. let ... in изрази

  • Користе се за дефинисање локалних, непроменљивих имена (везивања - bindings).
  • Све што је дефинисано у let делу је доступно у in делу.
let
  ime = "Свет";
  pozdrav = "Здраво, ${ime}!";
in
  pozdrav
# Резултат: "Здраво, Свет!"

5.5. Функције (Ламбде)

  • Функције су грађани првог реда.
  • Примају један аргумент и враћају једну вредност.
  • Синтакса: аргумент: тело_функције
# Функција која прима један број и враћа његов квадрат
x: x * x

# Позив функције
(x: x * x) 5
# Резултат: 25

5.6. Функције са скуповима атрибута

  • Најчешћи начин за прослеђивање више аргумената.
  • Омогућава именоване и опционе параметре.
{ ime, verzija, sistem ? "x86_64-linux" }:

let
  fullName = "${ime}-${verzija}";
in
  "Paket ${fullName} za sistem ${sistem}"

# Позив:
# ( { ime, ... }: ... ) { ime = "openssl"; verzija = "3.0"; }
# Резултат: "Paket openssl-3.0 за sistem x86_64-linux"

5.7. import

  • Учитава, парсира и евалуира други .nix фајл.
  • Враћа вредност коју тај фајл дефинише.
  • Омогућава модуларизацију кода.

Садржај podaci.nix:

{ verzija = "2.12.1"; }

Садржај default.nix:

let
  podaci = import ./podaci.nix;
in
  "Verzija programa je: ${podaci.verzija}"
# Резултат: "Verzija programa je: 2.12.1"

5.8. Уграђене функције (builtins)

  • Nix језик има скуп моћних уграђених функција.
  • Доступне су преко builtins скупа атрибута.
  • Примери: builtins.readFile, builtins.toJSON, builtins.fetchGit.
let
  sadrzaj = builtins.readFile ./README.md;
in
  "Dužina README fajla je: ${toString (builtins.stringLength sadrzaj)}"

5.9. Дефиниција деривације (.nix фајл)

Пример дефиниције пакета hello коришћењем функције stdenv.mkDerivation.

# default.nix
{ lib, stdenv, fetchurl }:

stdenv.mkDerivation rec {
  pname = "hello";
  version = "2.12.1";

  src = fetchurl {
    url = "mirror://gnu/hello/${pname}-${version}.tar.gz";
    sha256 = "sha256-jZkUKv2SV28wsM18tCqNxoCZmLxdYH2Idh9RLibH2yA=";
  };

  # 'hello' нема build зависности, али овако би изгледало:
  # buildInputs = [ pkgs.nekaZavisnost ];

  meta = {
    description = "A program that produces a familiar, friendly greeting";
    homepage = "https://www.gnu.org/software/hello/";
    license = lib.licenses.gpl3Plus;
  };
}

Кључни делови:

  • { lib, stdenv, fetchurl }: - Функција која прима зависности. Ово је стандардни образац у nixpkgs.
  • stdenv.mkDerivation - Основна функција за изградњу која аутоматизује ./configure && make && make install.
  • pname, version - Име и верзија пакета. rec омогућава да их користимо унутар истог скупа.
  • src - Изворни код. fetchurl га преузима и верификује помоћу sha256 хеша, чиме се гарантује поновљивост.
  • buildInputs - Листа зависности потребних за изградњу пакета.
  • meta - Метаподаци о пакету (опис, лиценца, веб страница…).

5.10. Изградња деривације - класичан приступ (без flakes)

Најједноставније је направити још један .nix фајл који ће учитати nixpkgs, а затим позвати деривацију.

Направити release.nix фајл у истом директоријуму:

let
  pkgs = import <nixpkgs> {};
in
  # Позива функцију и аутоматски јој прослеђује
  # аргументе (stdenv, fetchurl, lib...) из pkgs скупа.
  pkgs.callPackage ./default.nix {}

Изградња програма (build):

Користи се команда nix-build. Она ће креирати симболички линк ./result који показује на изграђени пакет у /nix/store.

$ nix-build release.nix
... (output изградње) ...

$ ls -l result
lrwxrwxrwx 1 user user 56 Dec  3 16:00 result -> /nix/store/1q8w6gl1ll0mwfkqc3c2yx005s6wwfrl-hello-2.12.1

Извршна датотека се налази унутар bin директоријума унутар result линка.

$ ./result/bin/hello
Hello, world!

6. Nix пахуљице (flakes)

  • Нови, експериментални начин дефиниције деривација и изградње

      ~> cat .config/nix/nix.conf
      experimental-features = nix-command flakes
    
  • Пахуљица представља стабло фајлова у чијем корену се налази flake.nix
  • Нови CLI - nix <команда>
  • Није потпуно компатибилна са старим командама типа nix-<команда> али већина команди има еквивалент

6.1. Текућа конфигурација

nix config show

6.2. Регистар пахуљица

~> nix config show | grep -i registry
flake-registry = https://channels.nixos.org/flake-registry.json
~> wget 'https://channels.nixos.org/flake-registry.json' -O - 2&>/dev/null | head -20
{
  "flakes": [
    {
      "from": {
        "id": "agda",
        "type": "indirect"
      },
      "to": {
        "owner": "agda",
        "repo": "agda",
        "type": "github"
      }
    },
    {
      "from": {
        "id": "arion",
        "type": "indirect"
      },
      "to": {
        "owner": "hercules-ci",

6.3. Листање регистра пахуљица

~> nix registry list
global flake:agda github:agda/agda
global flake:arion github:hercules-ci/arion
global flake:blender-bin github:edolstra/nix-warez?dir=blender
global flake:bundlers github:NixOS/bundlers
global flake:cachix github:cachix/cachix
...
global flake:nix github:NixOS/nix
global flake:nixpkgs github:NixOS/nixpkgs/nixpkgs-unstable
...

6.4. Коришћење тачне верзије пахуљице

nix registry pin nixpkgs --url github:NixOS/nixpkgs/<commit-hash>

6.5. Додавање и уклањање пахуљице из регистра

~> nix registry add crane github:ipetkov/crane
~> cat ~/.config/nix/registry.json
{
  "flakes": [
    {
      "from": {
        "id": "crane",
        "type": "indirect"
      },
      "to": {
        "owner": "ipetkov",
        "repo": "crane",
        "type": "github"
      }
    }
  ],
  "version": 2
}

~> nix registry remove crane

6.6. Референцирање израза за евалуацију

nix build nixpkgs#hello

6.7. Структура flake.nix фајла

Сваки flake.nix има следећу основну структуру:

{
  description = "Опис пахуљице";

  inputs = {
    # Зависности пахуљице
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs, ... }: {
    # Излази пахуљице: пакети, развојна окружења, NixOS конфигурације...
  };
}

6.8. Типови излаза пахуљице

  • packages.<систем>.<име> - Пакети за изградњу
  • devShells.<систем>.default - Развојно окружење
  • apps.<систем>.<име> - Апликације за покретање
  • nixosConfigurations.<име> - NixOS системске конфигурације
  • overlays.default - Преклопи за nixpkgs
  • templates.<име> - Шаблони за нове пројекте

6.9. Пример: Једноставна пахуљица за пројекат

# flake.nix
{
  description = "Мој Python пројекат";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in {
        # Развојно окружење
        devShells.default = pkgs.mkShell {
          buildInputs = with pkgs; [
            python311
            python311Packages.pip
            python311Packages.pytest
          ];
        };
      }
    );
}

6.10. Пример: Пахуљица са пакетом и развојним окружењем

{
  description = "Hello World C пројекат";

  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

  outputs = { self, nixpkgs }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in {
      packages.${system}.default = pkgs.stdenv.mkDerivation {
        pname = "moj-hello";
        version = "1.0.0";
        src = ./.;
        buildPhase = "gcc -o hello hello.c";
        installPhase = "mkdir -p $out/bin && cp hello $out/bin/";
      };

      devShells.${system}.default = pkgs.mkShell {
        buildInputs = [ pkgs.gcc pkgs.gdb ];
      };
    };
}

6.11. Употреба пахуљице

# Иницијализација пахуљице у постојећем пројекту
nix flake init

# Креирање на основу шаблона
nix flake init -t templates#c-hello

# Улазак у развојно окружење
nix develop

# Изградња пакета
nix build

# Покретање без инсталације
nix run

# Ажурирање зависности (flake.lock)
nix flake update

# Провера пахуљице
nix flake check

6.12. flake.lock - Закључавање верзија

  • Аутоматски генерисан фајл који садржи тачне верзије свих зависности
  • Гарантује поновљивост изградње
  • Треба га чувати у систему за контролу верзија (Git)
{
  "nodes": {
    "nixpkgs": {
      "locked": {
        "lastModified": 1703013332,
        "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6",
        "type": "github"
      }
    }
  }
}

6.13. Пахуљица за Rust пројекат (са crane)

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    crane.url = "github:ipetkov/crane";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, crane, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
        craneLib = crane.mkLib pkgs;
      in {
        packages.default = craneLib.buildPackage {
          src = craneLib.cleanCargoSource ./.;
        };

        devShells.default = craneLib.devShell {
          packages = [ pkgs.rust-analyzer ];
        };
      }
    );
}

6.14. Креирање пахуљице из шаблона

$ mkdir moj_projekat
$ cd moj_projekat
$ nix flake init --template "https://flakehub.com/f/the-nix-way/dev-templates/*#python"

6.15. Удаљено покретање пахуљица

# Покретање програма директно са GitHub-а
nix run github:NixOS/nixpkgs#hello

# Развојно окружење из удаљене пахуљице
nix develop github:korisnik/repo

# Изградња пакета из удаљене пахуљице
nix build github:korisnik/repo#paket

# Са специфичном граном или комитом
nix run github:korisnik/repo/grana#app
nix run github:korisnik/repo/abc123#app

7. Литература