├── .gitignore ├── Nix-Pills-Compact-Edition.org ├── README.org └── amend.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | \#* 3 | .\#* 4 | *.o 5 | *.o_p 6 | *.hi 7 | dist 8 | *.swp 9 | cabal-dev/ 10 | .hsenv/ 11 | .cabal-sandbox/ 12 | cabal.sandbox.config 13 | nohup.out 14 | -------------------------------------------------------------------------------- /Nix-Pills-Compact-Edition.org: -------------------------------------------------------------------------------- 1 | # -*- indent-tabs-mode: nil -*- 2 | #+startup: hidestars odd 3 | 4 | * About this document 5 | 6 | [[https://github.com/deepfire/nix-pills-compact-edition#index][Nix Pills Compact Edition]] is an on-going attempt to reformulate the excellent 7 | series of articles by [[http://lethalman.blogspot.com/][Luca Bruno]], known as [[http://lethalman.blogspot.ru/2014/07/nix-pill-1-why-you-should-give-it-try.html][Nix Pills]], with the following 8 | differences in mind: 9 | 10 | - a more compact, outline-based format 11 | - an attempt at a more definition-based approach 12 | - ..while trying to give a more complete picture 13 | 14 | Mostly I have been outright plagiarizing Luca through liberal cut'n'paste, so 15 | this is very much a derived work, and so, as usual -- all the greatness is due 16 | to Luca, all the mistakes were added by me. 17 | 18 | *** The pie-in-the-sky 19 | 20 | - import the rest of /Nix Pills/ 21 | - review the imported material, try to distill further 22 | - review the distillation, use the outline to recombine things, breaking 23 | inter-/Pill/ boundaries 24 | - review, research and add /Proposed Extras/: 25 | - generic things that didn't get into original /Nix Pills/ 26 | - Haskell-specific things 27 | - Emacs-specific things 28 | 29 | *** The preferred way to edit this document 30 | 31 | ..is through the excellent Org Mode: http://orgmode.org/ -- a richer, but 32 | still light-weight, alternative to Markdown. 33 | 34 | * Index 35 | 36 | - [[https://github.com/deepfire/nix-pills-compact-edition#nix-pill-1-why-you-should-give-it-a-try][Nix Pill #1: why you should give it a try]] 37 | - [[https://github.com/deepfire/nix-pills-compact-edition#nix-pill-2-install-on-your-running-system][Nix Pill #2: install on your running system]] (incomplete) 38 | - Nix Pill #3: enter the environment 39 | - [[https://github.com/deepfire/nix-pills-compact-edition#nix-pill-4-the-basics-of-the-language][Nix Pill #4: the basics of the language]] 40 | - Nix Pill #5: functions and imports 41 | - Nix Pill #6: our first derivation 42 | - Nix Pill #7: a working derivation 43 | - Nix Pill #8: generic builders 44 | - Nix Pill #9: automatic runtime dependencies 45 | - Nix Pill #10: developing with nix-shell 46 | - Nix Pill #11: the garbage collector 47 | - [[https://github.com/deepfire/nix-pills-compact-edition#nix-pill-12-the-inputs-design-pattern][Nix Pill #12: the inputs design pattern]] 48 | - [[https://github.com/deepfire/nix-pills-compact-edition#nix-pill-13-the-callpackage-design-pattern][Nix Pill #13: the callPackage design pattern]] 49 | - [[https://github.com/deepfire/nix-pills-compact-edition#nix-pill-14-the-override-design-pattern][Nix Pill #14: the override design pattern]] 50 | - Nix Pill #15: nix search paths 51 | - Nix Pill #16: nixpkgs, the parameters 52 | - [[https://github.com/deepfire/nix-pills-compact-edition#nix-pill-17-nixpkgs-overriding-packages][Nix Pill #17: nixpkgs, overriding packages]] 53 | - Nix Pill #18: nix store paths 54 | 55 | *** Proposed extras 56 | 57 | - Generic: 58 | - =callPackageWith= -- the way callPackage is actually implemented 59 | - =callPackageWithScope= 60 | - =build-support/replace-dependency= 61 | - =build-support/autonix/= 62 | - KDE only 63 | - mkPackage 64 | - resolveDeps 65 | - renameDeps 66 | - propagateDeps 67 | - userEnvDeps 68 | - extendDerivation 69 | - mostly KDE, but also: zed, tessel, airfield, nixui 70 | - nativeDeps 71 | - Haskell, KDE 72 | - overrideScope 73 | - overrideDerivation 74 | 75 | =overrideDerivation drv f= takes a derivation (i.e., the result of a 76 | call to the builtin function =derivation=) and returns a new derivation 77 | in which the attributes of the original are overriden according to the 78 | function =f=. The function =f= is called with the original derivation 79 | attributes. 80 | 81 | =overrideDerivation= allows certain "ad-hoc" customisation scenarios 82 | (e.g. in =~/.nixpkgs/config.nix=). For instance, if you want to "patch" 83 | the derivation returned by a package function in /Nixpkgs/ to build 84 | another version than what the function itself provides, you can do 85 | something like this: 86 | 87 | #+BEGIN_SRC nix 88 | mySed = overrideDerivation pkgs.gnused (oldAttrs: { 89 | name = "sed-4.2.2-pre"; 90 | patches = []; 91 | src = fetchurl { 92 | url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2; 93 | sha256 = "11nq06d131y4wmf3drm0yk502d2xc6n5qy82cg88rb9nqd2lj41k"; 94 | }; 95 | }); 96 | #+END_SRC 97 | 98 | For another application, see =build-support/vm=, where this function is 99 | used to build arbitrary derivations inside a QEMU virtual machine. 100 | 101 | - =build-support/source-from-head-fun/= 102 | - Haskell: 103 | - mkScope 104 | - ghcWithPackages wrapper 105 | - overrideCabal 106 | 107 | * Nix Pill #1: why you should give it a try 108 | 109 | *** Motivation: problems with other package managers 110 | 111 | - not being purely functional 112 | - multiple versions are possible, but take a lot of effort 113 | - changing installation paths means breaking implicit assumptions about the file system hierarchy 114 | - containers, as a blunt solution, have their serious drawbacks 115 | - nearly useless without: orchestration, shared package cache 116 | - per-language sandbox-like solutions: virtualenv, cabal sandbox 117 | - scope that is too narrow -- compiler, C libraries are not taken into account 118 | - a bunch of specific solutions with different flaws 119 | 120 | *** Nix way: purely functional, universal solution at the right level of abstraction 121 | 122 | - no assumption about the global state of the system 123 | - system composed of elements (called /derivations/), that are explicitly 124 | /derived/ from each other 125 | - global, /hash/-addressed, immutable =/nix/store=, storing /derivation outputs/ -- results of 126 | building packages from source, where, during build: 127 | - all gratuitious variables are set to their mathematical equivalent of zero (or /identity/, if you will) 128 | - time/date 129 | - environment 130 | - non-essential file system contents -- the build is chrooted 131 | - all essential variables (called /build inputs/) count into the /hash/ 132 | of the store elements, and are fixed forever: 133 | - build toolchain -- compiler / linker / etc. 134 | - other build time dependencies -- kernel headers, libc, libraries, make, automake, coreutils 135 | - runtime dependencies 136 | - ..which means that nearly identical subsets of packages can peacefully 137 | coexist along each other, with their only difference, for example, being 138 | the compiler they were built with 139 | - which means painless, entirely separate upgrades -- at the cost of disc space, of course 140 | - (indirectly) composing =PATH= from elements stored in =/nix/store= 141 | 142 | *** Nix way: immutability 143 | 144 | - Other package managers mutate global state by replacing packages in-place: 145 | - the library namespace granularity is mostly just the /soname/ 146 | - new library gets used by all the current packages, linking to that 147 | /soname/, mostly available at a well-known, standard, shared, global location 148 | - In /Nix/, the namespace granularity for libraries (and other /inputs/, build or runtime) 149 | is its /hash/: 150 | - a new version of =glibc= becomes available at a location based on its /hash/, 151 | which will be known only to the packages that will be newly built with 152 | that particular /derivation/ as a specific build input -- passed as if 153 | by a function call 154 | - Ergo, the /Nix/ equivalent of the traditional glibc "upgrade" is recompiling /everything/! 155 | - In practice, build farms provide you with the pre-built /derivations/, 156 | through an opt-out mechanism known as /binary cache/ 157 | - Security updates is a separate story, a story of compromise with the 158 | principle of purity 159 | - Another problem is that it's hard to compose at runtime applications 160 | that don't have in mind a pure functional model, and that can't be 161 | adapted to: 162 | - example: Firefox looks for the Flash plugin binary at a global (albeit per-user) path 163 | - which breaks the /Nix/ model, entirely 164 | - the solution (known as /wrapping/) is to produce another Firefox 165 | derivation, that will at runtime feed unmodified Firefox the plugin path 166 | we desire, allowing us to put the Flash binary into =/nix/store= 167 | - Yet another question is upgrading data: 168 | - typically done in-place by other package managers, in best tradition of mutation 169 | - /Nix/ makes it your own responsibility 170 | 171 | *** Conclusion 172 | 173 | - /Nix/ ([[http://nixos.org/nix/manual/][manual]]) maximises both /flexibility/ and /reproducibility/ of system composition 174 | - /[[http://nixos.org/nixops/][NixOps]]/ (based on /Nix/, /Nixpkgs/ and /NixOS/) makes cloud deployment 175 | easy, consistent and reliable, effectively deprecating all existing 176 | containment and orchestration solutions, such as Puppet, Vagrant, you name 177 | it 178 | - Weak spots are: 179 | - dynamic composition at runtime -- solutions exist, but are one-off 180 | - massive rebuilds due to fundamental component upgrades (kernel, compilers, 181 | base libraries) -- mostly made irrelevant by build farms 182 | - However, reality shows that /Nix/ is an eminently livable environment, and 183 | progress in solving the above problems is being steadily made 184 | - /[[https://github.com/NixOS/nixpkgs][Nixpkgs]]/ ([[http://nixos.org/nixos/packages.html][search]]) is a completely new repository of all existing software 185 | - fresh concept 186 | - growing contribution 187 | - the current state is far beyond the experimental stage 188 | 189 | * Nix Pill #2: install on your running system 190 | 191 | *** Origin: http://lethalman.blogspot.ru/2014/07/nix-pill-2-install-on-your-running.html 192 | 193 | *** Download 194 | 195 | - /Hydra/, the /Nix/-based CI system, hosts the builds of /Nix/: 196 | http://hydra.nixos.org/project/nix#tabs-releases 197 | - The /Nix/ manual contains a [[http://nixos.org/nix/manual/#chap-installation][chapter on installation]] 198 | 199 | *** Installation 200 | 201 | - =/nix/store= and a separate user, to isolate the store and build processes: 202 | 203 | #+BEGIN_SRC sh 204 | adduser nix 205 | mkdir -m 0755 /nix && chown nix /nix 206 | #+END_SRC 207 | 208 | - From now on, all the operations we do on the shell are done from this =nix= 209 | user: 210 | 211 | #+BEGIN_SRC sh 212 | su - nix 213 | tar -xf nix-1.9-x86_64-linux.tar.bz2 214 | cd nix-1.9-x86_64-linux 215 | ./install 216 | #+END_SRC 217 | 218 | *** INCOMPLETE *** 219 | 220 | * Nix Pill #3: enter the environment 221 | 222 | *** Origin: http://lethalman.blogspot.ru/2014/07/nix-pill-3-enter-environment.html 223 | 224 | *** INCOMPLETE *** 225 | 226 | * Nix Pill #4: the basics of the language 227 | 228 | *** Introduction 229 | 230 | ***** Nix-repl 231 | 232 | This chapter makes a heavy use of =nix-repl=. To install it, issue =nix-env 233 | -i nix-repl=. 234 | 235 | *CAVEAT*: the =nix-repl= syntax is slightly different than nix syntax when it 236 | comes to assigning variables. 237 | 238 | *** Value types 239 | 240 | ***** Simple types 241 | 242 | - /integer/ 243 | - /string/ 244 | - /path/ 245 | - /boolean/ 246 | - /null/ 247 | 248 | ***** Complex types 249 | 250 | - /list/ 251 | - /attribute set/ 252 | - /function/ -- yes, a first-class value that can be passed to and 253 | returned from functions 254 | 255 | *** Operators 256 | 257 | ***** Basic arithmetic: 258 | 259 | - =+=, =-=, =*= and integer division as =builtins.div=: 260 | 261 | #+BEGIN_SRC nix 262 | nix-repl> 1+3 263 | 4 264 | nix-repl> builtins.div 6 3 265 | 2 266 | #+END_SRC 267 | 268 | - *CAVEAT*: =/= is =path= concatenation instead: 269 | 270 | #+BEGIN_SRC nix 271 | nix-repl> 2/3 272 | /home/nix/2/3 273 | #+END_SRC 274 | 275 | - /Nix/ parsed =2/3= as a /relative path/ to the current directory. 276 | - /Paths/ are parsed as long as there's a slash. 277 | - Therefore to specify the current directory, use =./=. 278 | - In addition, /Nix/ also parses urls. 279 | - Not all /urls/ or /paths/ can be parsed this way. If a syntax error 280 | occurs, it's still possible to fallback to plain /strings/. 281 | 282 | - *NOTE*: =builtins.div= is not being used in the whole of /Nixpkgs/ 283 | repository, hence its second-class syntax status. 284 | 285 | ***** Boolean expressions 286 | 287 | - =||=, =&&=, =!= 288 | - =!==, *==* 289 | - less used tests: =<=, =>=, *>=*, *<=* 290 | 291 | ***** Other operators 292 | 293 | - http://nixos.org/nix/manual/#table-operators 294 | 295 | *** Identifiers 296 | 297 | Dash (=-=) is allowed in identifiers: 298 | 299 | #+BEGIN_SRC nix 300 | nix-repl> a-b 301 | error: undefined variable `a-b' at (string):1:1 302 | nix-repl> a - b 303 | error: undefined variable `a' at (string):1:1 304 | #+END_SRC 305 | 306 | *** Strings 307 | 308 | - String literals :: ..are enclosed by double-quotes ("), or two single-quotes 309 | (''), with =\=-based escaping: 310 | 311 | #+BEGIN_SRC nix 312 | nix-repl> "''foo''" 313 | "''foo''" 314 | nix-repl> ''"foo"'' 315 | "\"foo\"" 316 | nix-repl> "\"foo\"" 317 | "\"foo\"" 318 | #+END_SRC 319 | 320 | - /String literal/ syntax provides means for [[http://nixos.org/nix/manual/#ssec-values][interpolation]] of expressions 321 | within =${...}=: 322 | 323 | #+BEGIN_SRC nix 324 | nix-repl> foo = "strval" 325 | nix-repl> "$foo" 326 | "$foo" 327 | nix-repl> "${foo}" 328 | "strval" 329 | nix-repl> "${2+3}" 330 | error: cannot coerce an integer to a string, at (string):1:2 331 | #+END_SRC 332 | 333 | - *NOTE*: ignore the foo = "strval" assignment, it's =nix-repl=-specific syntax. 334 | 335 | - Escaping =${...}= within double-quoted /string literals/ is done with the 336 | backslash. Within two single quotes, it's done with =''=: 337 | 338 | #+BEGIN_SRC nix 339 | nix-repl> "\${foo}" 340 | "${foo}" 341 | nix-repl> ''test ''${foo} test'' 342 | "test ${foo} test" 343 | #+END_SRC 344 | 345 | *** Lists 346 | 347 | - List :: an immutable sequence of /expressions/ delimited by space (not comma): 348 | 349 | #+BEGIN_SRC nix 350 | nix-repl> [ 2 "foo" true (2+3) ] 351 | [ 2 "foo" true 5 ] 352 | #+END_SRC 353 | 354 | - Adding or removing elements from a list is only possible through production 355 | of a new list. 356 | 357 | *** Attribute sets 358 | 359 | - Attribute set :: a set of associations between /keys/ and /values/, where: 360 | - /keys/ can be either/identifiers/ or /strings/, for the cases when desired 361 | key names aren't valid identifiers 362 | - /values/ can be arbitrary /Nix/ /expressions/ 363 | 364 | - Example value: 365 | 366 | #+BEGIN_SRC nix 367 | nix-repl> s = { foo = "bar"; a-b = "baz"; "123" = "num"; } 368 | nix-repl> s 369 | { 123 = "num"; a-b = "baz"; foo = "bar"; } 370 | #+END_SRC 371 | 372 | - The output from =nix-repl= is wrong, you can't write { 123 = "num"; } because 123 is not an identifier. 373 | - Semicolon (;) is required after every key-value assignment. 374 | - For those reading /Nix/ expressions from /Nixpkgs/: do not confuse 375 | /attribute sets/ (which are /values/) with /argument sets/ used in 376 | function definitions (which are /argument specifiers/). 377 | 378 | - Accessing elements: 379 | 380 | #+BEGIN_SRC nix 381 | nix-repl> s.a-b 382 | "baz" 383 | nix-repl> s."123" 384 | "num" 385 | #+END_SRC 386 | 387 | - Defining /recursive attribute sets/: 388 | - Exhibit of the problem: 389 | 390 | #+BEGIN_SRC nix 391 | nix-repl> { a = 3; b = a+4; } 392 | error: undefined variable `a' at (string):1:10 393 | #+END_SRC 394 | 395 | - Problem statement -- =a= isn't in scope for =b= 396 | - Solution: *INCOMPLETE*: URL 397 | 398 | #+BEGIN_SRC nix 399 | nix-repl> rec { a= 3; b = a+4; } 400 | { a = 3; b = 7; } 401 | #+END_SRC 402 | 403 | *** If expression 404 | 405 | #+BEGIN_SRC nix 406 | nix-repl> a = 3 407 | nix-repl> b = 4 408 | nix-repl> if a > b then "yes" else "no" 409 | "no" 410 | #+END_SRC 411 | 412 | - Both =then= and =else= must be available -- so the value of the expression 413 | is always defined. 414 | 415 | *** Let expression 416 | 417 | - Introducing variables into scope: 418 | 419 | #+BEGIN_SRC nix 420 | nix-repl> let a = 3; b = 4; in a + b 421 | 7 422 | #+END_SRC 423 | 424 | - ..with recursion: 425 | 426 | #+BEGIN_SRC nix 427 | nix-repl> let a = 4; b = a + 5; in b 428 | 9 429 | #+END_SRC 430 | 431 | - Variable scopes compose..: 432 | 433 | #+BEGIN_SRC nix 434 | nix-repl> let a = 3; in let b = 4; in a + b 435 | 7 436 | #+END_SRC 437 | 438 | - ..with shadowing: 439 | 440 | #+BEGIN_SRC nix 441 | nix-repl> let a = 3; in let a = 8; b = 4; in a + b 442 | 12 443 | #+END_SRC 444 | 445 | *** With expression 446 | 447 | - =with= allows "opening" /attribute sets/, binding names of its keys to their 448 | corresponding values: 449 | 450 | #+BEGIN_SRC nix 451 | nix-repl> longExpression = { a = 3; b = 4; "123" = 5; } 452 | nix-repl> longExpression.a + longExpression.b 453 | 7 454 | nix-repl> with longExpression; a + b 455 | 7 456 | #+END_SRC 457 | 458 | - *CAVEAT*: only valid identifiers from the set keys will be included 459 | 460 | #+BEGIN_SRC nix 461 | nix-repl> let a = 10; in with longExpression; a + b + longExpression."123" 462 | 19 463 | #+END_SRC 464 | 465 | - *CAVEAT*: if an identifier is bound in the outer scope and is also present 466 | in the attribute set of =with=, it will *not* be shadowed 467 | 468 | #+BEGIN_SRC nix 469 | nix-repl> let a = 10; in with longExpression; a + b 470 | 14 471 | nix-repl> let a = 10; in with longExpression; longExpression.a + b 472 | 7 473 | #+END_SRC 474 | 475 | *** Laziness 476 | 477 | /Nix/ evaluates expressions only [[http://en.wikipedia.org/wiki/Lazy_evaluation][when needed]]. This allows easy definition of 478 | mutually referencing entities and efficient handling of large package 479 | repository definitions. 480 | 481 | *** INCOMPLETE *** 482 | 483 | * Nix Pill #5: functions and imports 484 | 485 | *** Origin: http://lethalman.blogspot.ru/2014/07/nix-pill-5-functions-and-imports.html 486 | 487 | *** INCOMPLETE *** 488 | 489 | * Nix Pill #6: our first derivation 490 | 491 | *** Origin: http://lethalman.blogspot.ru/2014/07/nix-pill-6-our-first-derivation.html 492 | 493 | *** INCOMPLETE *** 494 | 495 | * Nix Pill #7: a working derivation 496 | 497 | *** Origin: http://lethalman.blogspot.ru/2014/07/nix-pill-7-working-derivation.html 498 | 499 | *** INCOMPLETE *** 500 | 501 | * Nix Pill #8: generic builders 502 | 503 | *** Origin: http://lethalman.blogspot.ru/2014/08/nix-pill-8-generic-builders.html 504 | 505 | *** INCOMPLETE *** 506 | 507 | * Nix Pill #9: automatic runtime dependencies 508 | 509 | *** Origin: http://lethalman.blogspot.ru/2014/08/nix-pill-9-automatic-runtime.html 510 | 511 | *** INCOMPLETE *** 512 | 513 | * Nix Pill #10: developing with nix-shell 514 | 515 | *** Origin: http://lethalman.blogspot.ru/2014/08/nix-pill-10-developing-with-nix-shell.html 516 | 517 | *** INCOMPLETE *** 518 | 519 | * Nix Pill #11: the garbage collector 520 | 521 | *** Origin: http://lethalman.blogspot.ru/2014/08/nix-pill-11-garbage-collector.html 522 | 523 | *** INCOMPLETE *** 524 | 525 | * Nix Pill #12: the inputs design pattern 526 | 527 | *** Composing package definitions: repositories in Nix 528 | 529 | - We only packaged a single program so far -- but how do we compose package definitions? 530 | 531 | - As we have already seen, from the point of view of a single /package X/, 532 | /Nix/ is a language for describing: 533 | - /names/ of the /externalities/ that are required to build (and run) /package X/ 534 | - how to use these /externalities/, given their /names/ 535 | - ..which looks suspiciously like function definition -- which it is! 536 | 537 | - However, function definition at package level isn't enough for whole-system description: 538 | - ..functions need arguments supplied, which is the /Nix/ way of saying that: 539 | - ..packages need to have their dependencies supplied 540 | - ..which means that particular versions and build configurations of the 541 | dependencies need to be decided upon /somewhere/ 542 | - Traditionally, this /somewhere/ is called a /package repository/ 543 | 544 | - /Nix/, by itself, doesn't enforce a /package repository/ structure, as the 545 | only inherent requirement of its functional decomposition approach is that 546 | all the /functions/ that define packages must be supplied proper arguments. 547 | 548 | - /Nix/, however has a particular /package repository/, with a particular 549 | structure -- /[[https://github.com/NixOS/nixpkgs][Nixpkgs]]/ 550 | - essentially, a single, giant expression in the /Nix/ language: 551 | - mostly organized across individual, per-package files, 552 | - the root =import=-ing the nodes and leaves 553 | - evaluates to a giant attribute set with /name -> package/ pairs 554 | - ..which works efficiently, due to the /lazy evaluation/ property of /Nix/, 555 | meaning it only evaluates parts of the expression that are actually needed 556 | - ..which contrasts with, for example Debian and Fedora, which pull package 557 | definitions from /several/ repositories (through indexes like =/etc/apt/sources.list=) 558 | - ..but coincides, for example, with Gentoo 559 | 560 | - The structure of /Nixpkgs/ has /patterns/ (like the above -- pulling 561 | everything into a /single coherent definition/) that aren't /enforced/ by 562 | /Nix/, but are nonetheless present, codifying a distillation of successful 563 | practices of describing the world 564 | 565 | *** Packaging graphviz 566 | 567 | - Graphviz: 568 | - uses the standard autotools build system 569 | - requires no patching 570 | - dependencies are optional 571 | - source: http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.38.0.tar.gz 572 | 573 | - Expression: 574 | 575 | #+BEGIN_SRC nix 576 | let 577 | pkgs = import {}; 578 | mkDerivation = import ./autotools.nix pkgs; 579 | in mkDerivation { 580 | name = "graphviz"; 581 | src = ./graphviz-2.38.0.tar.gz; 582 | } 583 | #+END_SRC 584 | 585 | - reuses =autotools.nix= from =hello.nix= 586 | 587 | - Build, producing runnable binaries under =result/bin=: 588 | 589 | : nix-build graphviz.nix 590 | 591 | - Let's create a simple png: 592 | 593 | #+BEGIN_SRC sh 594 | $ echo 'graph test { a -- b }' | result/bin/dot -Tpng -o test.png 595 | Format: "png" not recognized. Use one of: canon cmap [...] 596 | #+END_SRC 597 | 598 | - ..meaning that only the output formats graphviz supports natively, without 599 | using any extra library, were built. 600 | 601 | - in =autotools.nix= there's a =buildInputs= variable, which gets concatenated 602 | to =baseInputs=. That would be the perfect place to add a build 603 | dependency. We created that variable exactly for this reason to be 604 | overridable from package expressions. 605 | 606 | *** Digression about gcc and ld wrappers 607 | 608 | - build systems for =gd=, =jpeg=, =fontconfig= and =bzip2= libraries 609 | (dependencies of =gd=) don't use =pkg-config= to specify which flags to pass 610 | to the compiler, and so rely, instead, on the traditional, system-global 611 | locations, such as =/usr/lib= and =/usr/include= to find dependency headers 612 | and binaries -- which are exactly absent in the Nix model. 613 | 614 | - =gcc= and =binutils= package definitions provided by =Nixpkgs= include 615 | [[http://nixos.org/nixpkgs/manual/#ssec-setup-hooks][wrappers]], that allow passing extra arguments to =gcc= and =ld= binaries -- 616 | bypassing and overriding the project build systems we call into, and 617 | effectively providing us with a project-independent way of supplying 618 | tool flags and dependencies: 619 | 620 | - =NIX_CFLAGS_COMPILE= :: extra flags to gcc at compile time 621 | - =NIX_LDFLAGS= :: extra flags to ld 622 | 623 | - These variables can be filled from /derivation inputs/ the same way as was 624 | previously done for =PATH= -- here is the relevant snippet of =setup.sh=: 625 | 626 | #+BEGIN_SRC sh 627 | for p in $baseInputs $buildInputs; do 628 | if [ -d $p/bin ]; then 629 | export PATH="$p/bin${PATH:+:}$PATH" 630 | fi 631 | if [ -d $p/include ]; then 632 | export NIX_CFLAGS_COMPILE="-I $p/include${NIX_CFLAGS_COMPILE:+ }$NIX_CFLAGS_COMPILE" 633 | fi 634 | if [ -d $p/lib ]; then 635 | export NIX_LDFLAGS="-rpath $p/lib -L $p/lib${NIX_LDFLAGS:+ }$NIX_LDFLAGS" 636 | fi 637 | done 638 | #+END_SRC 639 | 640 | - The =-rpath= flag in =ld= is needed because at runtime, the executable 641 | must use exactly that version of the library. 642 | - If unneeded paths are specified, the fixup phase will automatically shrink 643 | the =rpath=. 644 | 645 | *** Completing graphviz with gd 646 | 647 | Building upon the results above, we now can transparently supply the graphviz 648 | build system with more libraries -- which it will find without any =configure= 649 | parameters, thanks to the =gcc= and =ld= wrappers: 650 | 651 | #+BEGIN_SRC nix 652 | # graphviz.nix 653 | let 654 | pkgs = import {}; 655 | mkDerivation = import ./autotools.nix pkgs; 656 | in mkDerivation { 657 | name = "graphviz"; 658 | src = ./graphviz-2.38.0.tar.gz; 659 | buildInputs = with pkgs; [ gd fontconfig libjpeg bzip2 ]; 660 | } 661 | #+END_SRC 662 | 663 | *** Composing package definitions: the repository expression 664 | 665 | - It's nice to be able to abstract out the file-level repository structure, 666 | replacing the file paths with names, and that's what /Nixpkgs/ does -- the 667 | top level expression imports the file names and provides the results as 668 | elements of the attribute set: 669 | 670 | #+BEGIN_SRC nix 671 | # default.nix: 672 | { 673 | hello = import ./hello.nix; 674 | graphviz = import ./graphviz.nix; 675 | } 676 | #+END_SRC 677 | 678 | - Trying it: 679 | 680 | #+BEGIN_SRC sh 681 | $ nix-repl 682 | nix-repl> :l default.nix 683 | Added 2 variables. 684 | nix-repl> hello 685 | «derivation /nix/store/dkib02g54fpdqgpskswgp6m7bd7mgx89-hello.drv» 686 | nix-repl> graphviz 687 | «derivation /nix/store/zqv520v9mk13is0w980c91z7q1vkhhil-graphviz.drv» 688 | #+END_SRC 689 | 690 | - With =nix-build=: 691 | 692 | #+BEGIN_SRC sh 693 | $ nix-build default.nix -A hello 694 | [...] 695 | $ result/bin/hello 696 | Hello, world! 697 | #+END_SRC 698 | 699 | - The =-A= argument is used to access an /attribute/ of the set from the 700 | given .nix expression. 701 | - When a directory (by default the current directory) has a =default.nix=, 702 | it will be used by default, so the following will work as well: 703 | 704 | : nix-build -A hello 705 | 706 | - Install the package in your user environment: 707 | 708 | #+BEGIN_SRC sh 709 | $ nix-env -f . -iA graphviz 710 | [...] 711 | $ dot -V 712 | #+END_SRC 713 | 714 | - =-f= is used to specify the expression to use, in this case the current 715 | directory, therefore ./default.nix. 716 | - =-i= stands for installation 717 | - =-A= is the same as above for nix-build 718 | 719 | - ..which concludes an exhibit of the essence of nixpkgs -- a collection of 720 | package definitions. 721 | 722 | *** The inputs pattern 723 | 724 | - Three problems with =hello.nix= and =graphviz.nix= definitions, rooting, 725 | essentially in their dependence on =Nixpkgs= structure: 726 | - They =import= /Nixpkgs/ directly. In =autotools.nix= instead we pass /Nixpkgs/ as 727 | an argument. That's a much better approach. 728 | - No way to define =graphviz= without =libgd= support 729 | - No way to vary =libgd= version in =graphviz= definition 730 | 731 | - So far, the answer was to edit the /callee/ 732 | - The essence of the /inputs pattern/ is to actually use the functional abstraction, 733 | shifting these high-level decisions where they belong -- to the /caller/ 734 | 735 | - Inputs of an expression :: the set of /derivations/ needed to build that 736 | expression. In this case: 737 | - =mkDerivation= from =autotools=. Recall that =mkDerivation= has an 738 | /implicit dependency/ on the toolchain. 739 | - =libgd= and its dependencies. 740 | 741 | - *NOTE*: =src= is also an input but it's pointless to change the source 742 | from the caller. For version bumps, in /Nixpkgs/ we prefer to write another 743 | expression (e.g. because patches are needed or different inputs are needed). 744 | 745 | *** Leveraging functional abstraction for package expression independence 746 | 747 | - for =graphviz.nix=: 748 | 749 | #+BEGIN_SRC nix 750 | { mkDerivation, gdSupport ? true, gd, fontconfig, libjpeg, bzip2 }: 751 | 752 | mkDerivation { 753 | name = "graphviz"; 754 | src = ./graphviz-2.38.0.tar.gz; 755 | buildInputs = if gdSupport then [ gd fontconfig libjpeg bzip2 ] else []; 756 | } 757 | #+END_SRC 758 | 759 | - ={...}: ...= is syntax for defining functions accepting an attribute set 760 | as argument. *INCOMPLETE*: URL 761 | - when omitted by the caller, =gdSupport= defaults to =true= 762 | 763 | - for =default.nix=: 764 | 765 | #+BEGIN_SRC nix 766 | let 767 | pkgs = (import ) {}; 768 | mkDerivation = (import ./autotools.nix) pkgs; 769 | in with pkgs; { 770 | hello = (import ./hello.nix) { inherit mkDerivation; }; 771 | graphviz = (import ./graphviz.nix) { inherit mkDerivation gd fontconfig libjpeg bzip2; }; 772 | graphvizCore = (import ./graphviz.nix) { inherit mkDerivation gd fontconfig libjpeg bzip2; 773 | gdSupport = false; }; 774 | } 775 | #+END_SRC 776 | 777 | - =let= binds convenience variables 778 | - for pedagogical purposes we cheat, by using a ==, which already 779 | contains everything one might want -- defining contents of == 780 | from ground up would have obscured the subject matter of this exhibition. 781 | - =with pkgs= "opens" the /Nixpkgs/, binding =gd=, =fontconfig=, =libjpeg= and =bzip2= 782 | - the toolchain is captured in the particular value of =mkDerivation= 783 | - =inherit a b c;= is syntactic sugar for =a = a; b = b; c = c;= -- 784 | essentially capturing a part of the variable scope in an /attribute set/ 785 | - =import= reads the =.nix= files, which contain functions -- which are 786 | subsequently called with the /attribute sets/ arguments -- which is 787 | clarified by the added parentheses (which are otherwise unnecessary) 788 | 789 | *** Conclusion 790 | 791 | The /inputs pattern/ is another name for leveraging functional abstraction to 792 | separate the repository in two conceptual parts: 793 | 794 | - Package definitions :: =import=-ed leaves, containing flexible function 795 | expressions, that are free from policy decision-making, such as: 796 | - repository structure 797 | - specific versions of build inputs 798 | - other choices that the author of a particular /package function/ 799 | expression chose to abstract out 800 | - High-level structure :: the part that: 801 | - maintains knowledge of the file structure of /repository/, at the points 802 | of =import= expressions 803 | - provides a global namespace of /package names/, associating them to 804 | attribute sets, which are produced by: 805 | - instantiation of the =import=-ed /package functions/ 806 | - note that the same /package function/ can be instantiated several 807 | times, resulting in several /packages/, bound to different /package names/ 808 | - ..while making decisions about their arguments -- whose interpretation is 809 | determined by the /package functions/ 810 | 811 | * Nix Pill #13: the callPackage design pattern 812 | 813 | *** Origin: http://lethalman.blogspot.ru/2014/09/nix-pill-13-callpackage-design-pattern.html 814 | 815 | *** The callPackage convenience 816 | 817 | - Problem: duplicate listing of /package function/ arguments -- /explicit 818 | argument passing/: 819 | - In /package function/: 820 | 821 | #+BEGIN_SRC nix 822 | # package1.nix 823 | { input1, input2, ... }: 824 | ... 825 | #+END_SRC 826 | 827 | - In /package derivation expressions/: 828 | 829 | #+BEGIN_SRC nix 830 | rec { 831 | lib1 = import package1.nix { inherit input1 input2 ...; }; 832 | program2 = import package2.nix { inherit inputX inputY lib1 ...; }; 833 | } 834 | #+END_SRC 835 | 836 | - *NOTE*: this "problem" wouldn't have arised, had we not strategically 837 | chosen the /package function/ /parameter names/ to coincide with the 838 | /derivation names/ in the /global derivation namespace/. To be clear: this 839 | is a simplification opportunity, not a problem. 840 | 841 | - So, what form do we choose for the lucky /derivation expressions/ -- those 842 | enjoying the parameter/namespace coincidence? How about this: 843 | 844 | #+BEGIN_SRC nix 845 | { 846 | lib1 = callPackage package1.nix { }; 847 | program2 = callPackage package2.nix { someoverride = overriddenDerivation; }; 848 | } 849 | #+END_SRC 850 | 851 | - The exact semantics should this /implicit argument passing/ have? This: 852 | - Import the given /expression/ (which still must evaluate to a /function/). 853 | - Determine the name of its arguments. 854 | - Pass the values bound within /global derivation namespace/ to the names of 855 | those arguments, and let us override those arguments. 856 | 857 | *** Implementing callPackage 858 | 859 | - To implement /implicit argument passing/, we need a way to introspect at 860 | runtime the /argument names/ of a /function/, to know what names it needs: 861 | 862 | #+BEGIN_SRC nix 863 | nix-repl> add = { a ? 3, b }: a+b 864 | nix-repl> builtins.functionArgs add 865 | { a = true; b = false; } 866 | #+END_SRC 867 | 868 | - As a bonus, =builtins.functionArgs= returns not just the names, but also 869 | a boolean that determines whether the names have a default value or not. 870 | 871 | - We need access to the /global derivation namespace/, to know what is available 872 | 873 | - Given both of the above, the next step is to extract a subset from the 874 | /global derivation namespace/, determined by the chosen names: 875 | 876 | #+BEGIN_SRC nix 877 | # Mocking the /global derivation namespace/ here: 878 | nix-repl> values = { a = 3; b = 5; c = 10; } 879 | nix-repl> builtins.intersectAttrs values (builtins.functionArgs add) 880 | { a = true; b = false; } 881 | nix-repl> builtins.intersectAttrs (builtins.functionArgs add) values 882 | { a = 3; b = 5; } 883 | #+END_SRC 884 | 885 | - =builtins.intersectAttrs= is the function that does the extraction 886 | 887 | - This allows for a preliminary implementation of =callPackage= -- 888 | function calling with /implicit argument passing/, without overrides: 889 | 890 | #+BEGIN_SRC nix 891 | nix-repl> callPackage = set: f: f (builtins.intersectAttrs (builtins.functionArgs f) set) 892 | nix-repl> callPackage values add 893 | 8 894 | nix-repl> with values; add { inherit a b; } 895 | 8 896 | #+END_SRC 897 | 898 | - =callPackage= is a function of two arguments: 899 | - the /attribute set/ from which to take arguments for.. 900 | - ..the function that is to be called 901 | - the second expression shows the original function call expression, 902 | with /explicit argument passing/ that we sought to avoid 903 | 904 | - What if the /attribute set/ is missing a key/value pair for the /required 905 | argument/ of the function being called? Nothing special -- that's an error. 906 | 907 | - The remaining piece is being able to override the /attribute set/, for 908 | example if the /input derivation/ we want to supply is named with something 909 | else than the canonical /derivation name/ -- like =gcc-5.2=, instead of 910 | =gcc=. 911 | 912 | - This can be done through adding a third argument to =callPackage= -- the 913 | override /attribute set/: 914 | 915 | #+BEGIN_SRC nix 916 | nix-repl> callPackage = set: f: overrides: f ((builtins.intersectAttrs (builtins.functionArgs f) set) // overrides) 917 | nix-repl> callPackage values add { } 918 | 8 919 | nix-repl> callPackage values add { b = 12; } 920 | 15 921 | #+END_SRC 922 | 923 | - the =//= operator is an /attribute set/ union, with the /attribute set/ on 924 | the right taking precedence in case of key conflicts. 925 | 926 | *** Use callPackage to simplify the repository 927 | 928 | Given our brand new tool, we can simplify the repository expression 929 | (=default.nix=): 930 | 931 | #+BEGIN_SRC nix 932 | let 933 | nixpkgs = import {}; 934 | allPkgs = nixpkgs // pkgs; 935 | callPackage = path: overrides: 936 | let f = import path; 937 | in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) 938 | // overrides); 939 | pkgs = with nixpkgs; { 940 | mkDerivation = import ./autotools.nix nixpkgs; 941 | hello = callPackage ./hello.nix { }; 942 | graphviz = callPackage ./graphviz.nix { }; 943 | graphvizCore = callPackage ./graphviz.nix { gdSupport = false; }; 944 | }; 945 | in pkgs 946 | #+END_SRC 947 | 948 | - We renamed the old =pkgs= of the previous pill to =nixpkgs=. "Our" package 949 | /attribute set/ is now instead named =pkgs=. 950 | - For convenience, in =callPackage= we first =import= the file argument, 951 | instead of calling it directly. Otherwise each /derivation expression/ 952 | would have to perform the import itself. 953 | - Since our expressions use packages from /Nixpkgs/, in =callPackage= we use 954 | =allPkgs=, which is the union of /Nixpkgs/ and our packages. 955 | - We moved =mkDerivation= into =pkgs= itself, so that it gets also passed 956 | implicitly 957 | 958 | Note how easy is to override arguments in the case of =graphviz= without =gd=. 959 | But most importantly, how easy it was to merge two repositories: =nixpkgs= and our =pkgs=! 960 | 961 | *NOTE*: ..how deeply the new scheme depends on the ability of =let= to 962 | define mutually recursive structure -- which is only made possible by 963 | /lazy evaluation/ semantics of /Nix/: 964 | - =allPkgs= depends on =pkgs= 965 | - =callPackage= depends on =allPkgs= 966 | - =pkgs= depends on =callPackage= 967 | 968 | *** Conclusion 969 | 970 | - /Implicit argument passing/ enabled by callPackage, (aka the /callPackage 971 | pattern/) allow us to reduce maintenance burden: 972 | - whenever the set of /package function/ arguments change, we'll most likely 973 | need to change just one place -- the /package function/ itself 974 | - whenever the /derivation expression/ needs an override of the default 975 | inputs to the /package function/, it can easily do just this 976 | - =builtins.functionArgs= is undocumented in the [[http://nixos.org/nix/manual/][Nix Manual]], mostly because 977 | it's a function that only makes sense for the sort of plumbing we're going 978 | through. 979 | - Most of all, we get to see how /Nix/, the language, is a generic tool 980 | suitable for construction of arbitrary purpose-built abstractions, that can 981 | support any policy we choose. 982 | 983 | * Nix Pill #14: the override design pattern 984 | 985 | *** Prelude: about composability 986 | 987 | - Functional update pattern: update functions return a modified copy 988 | of the original structure. 989 | 990 | - Common type in Nix :: =a -> Derivation= 991 | 992 | - Example: 993 | - input derivation =drv= 994 | - with debug info 995 | - with patches applied 996 | 997 | #+BEGIN_SRC nix 998 | debugVersion (applyPatches [ ./patch1.patch ./patch2.patch ] drv) 999 | #+END_SRC 1000 | 1001 | *** The override pattern 1002 | 1003 | Let us recall the repository structure: 1004 | 1005 | - A recursive attribute set :: =Name -> Derivation= 1006 | 1007 | - ..that employs introspection to supply arguments -- that are its own 1008 | elements -- to the =Derivation= expressions 1009 | 1010 | - ..it has the following form: 1011 | 1012 | #+BEGIN_SRC nix 1013 | graphviz = callPackage ./graphviz.nix { }; 1014 | #+END_SRC 1015 | 1016 | - If we wanted to produce a derivation of =graphviz= with a customized =gd= 1017 | version, we would have to: 1018 | 1019 | #+BEGIN_SRC nix 1020 | mygraphviz = callPackage ./graphviz.nix { gd = customgd; }; 1021 | #+END_SRC 1022 | 1023 | - The problem here is a little unobvious -- our override is tightly coupled to 1024 | the original definition -- which forces us to: 1025 | 1026 | 1. find the original definition 1027 | 2. derive a modified version 1028 | 3. maintain the modification against the possible changes of the original 1029 | 1030 | - So, we would like to decouple the override from the form of the definition: 1031 | 1032 | #+BEGIN_SRC nix 1033 | mygraphviz = graphviz.override { gd = customgd; }; 1034 | #+END_SRC 1035 | 1036 | - *NOTE*: in this form, =.override= is just a plain element of an /attribute set/ 1037 | 1038 | *** The override implementation 1039 | 1040 | As a reminder, the =graphviz= attribute is the /derivation/ (which is an 1041 | /attribute set/) returned by the /derivation function/ =import=-ed from 1042 | =graphviz.nix=. 1043 | 1044 | So, to follow the form we preferred above, we would have to modify the 1045 | =import=-ed function to return its original /attribute set/ enriched with an 1046 | element named =override=. 1047 | 1048 | - First attempt: 1049 | 1050 | #+BEGIN_SRC nix 1051 | { 1052 | # makeOverridable :: (ASet Inputs -> Drv) -> ASet Inputs 1053 | makeOverridable = f: origArgs: 1054 | let 1055 | origRes = f origArgs; 1056 | in 1057 | origRes // { override = newArgs: f (origArgs // newArgs); }; 1058 | } 1059 | #+END_SRC 1060 | 1061 | - takes a /derivation function/ 1062 | - returns a /derivation function/, that returns a /derivation/ with an 1063 | enriched /attribute set/ -- the added =override= attribute 1064 | - the =override= attribute _too_ is a /derivation function/, but one, that 1065 | calls the _original_ derivation function with _original_ arguments 1066 | enriched by the /attribute set/ supplied to the /override function/ 1067 | 1068 | - How does it work? 1069 | 1070 | #+BEGIN_SRC nix 1071 | $ nix-repl 1072 | nix-repl> :l lib.nix 1073 | Added 1 variables. 1074 | nix-repl> f = { a, b }: { result = a+b; } 1075 | nix-repl> f { a = 3; b = 5; } 1076 | { result = 8; } 1077 | nix-repl> res = makeOverridable f { a = 3; b = 5; } 1078 | nix-repl> res 1079 | { override = «lambda»; result = 8; } 1080 | #+END_SRC 1081 | 1082 | - so far, so good.. 1083 | 1084 | - But, can we chain overrides? 1085 | 1086 | #+BEGIN_SRC nix 1087 | nix-repl> res.override { a = 10; } 1088 | { result = 15; } 1089 | #+END_SRC 1090 | 1091 | - ..the result of an override is missing the =override= attribute, 1092 | so no, we can't. 1093 | 1094 | - Try #2: 1095 | 1096 | #+BEGIN_SRC nix 1097 | rec { 1098 | makeOverridable = f: origArgs: 1099 | let 1100 | origRes = f origArgs; 1101 | in 1102 | origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); }; 1103 | } 1104 | #+END_SRC 1105 | 1106 | - the =rec= keyword allows us to refer to =makeOverridable= from its own 1107 | definition 1108 | - overrides can now be chained 1109 | 1110 | *** Conclusion 1111 | 1112 | - =makeOverridable= allows us to decouple expressions defining derivations, 1113 | from the points where we want to override them. 1114 | 1115 | - This allows a degree of separation between the package definitions, and the 1116 | package users. 1117 | 1118 | * Nix Pill #15: nix search paths 1119 | 1120 | *** Origin: http://lethalman.blogspot.ru/2014/09/nix-pill-15-nix-search-paths.html 1121 | 1122 | *** INCOMPLETE *** 1123 | 1124 | * Nix Pill #16: nixpkgs, the parameters 1125 | 1126 | *** Origin: http://lethalman.blogspot.ru/2014/11/nix-pill-16-nixpkgs-parameters.html 1127 | 1128 | *** INCOMPLETE *** 1129 | 1130 | * Nix Pill #17: nixpkgs, overriding packages 1131 | 1132 | *** Origin: http://lethalman.blogspot.ru/2014/11/nix-pill-17-nixpkgs-overriding-packages.html 1133 | 1134 | *** Overriding a package 1135 | 1136 | - In Nix Pill #14, to produce an overriden /derivation/ by producing a 1137 | modified /derivation expression/, in order to avoid coupling we have 1138 | built-in overridability into the /derivation function/, by putting an 1139 | /override function/ into the /attribute set/ returned by the /derivation 1140 | function/ under the name =override=. 1141 | 1142 | - As an example usage, here's how we override =graphviz= to build without X 1143 | support: 1144 | 1145 | #+BEGIN_SRC nix 1146 | $ nix-repl 1147 | nix-repl> :l 1148 | Added 4360 variables. 1149 | nix-repl> :b graphviz.override { xlibs = null; } 1150 | #+END_SRC 1151 | 1152 | - However, how do we make other packages refer to this overriden =graphviz=, 1153 | instead of the original one? 1154 | - There is no global, shared state to mutate -- our only option is to re-evaluate 1155 | everything in a modified context. 1156 | - Note also, how not only the new /derivations/ need to refer to the new 1157 | =graphviz=, but they also must refer to the new versions of each other. 1158 | - Hence the option of overriding everything manually would entail a 1159 | prohibitive amount of work -- basically amounting to repeating the whole 1160 | repository with a new, parallel set of names. 1161 | 1162 | *** Fixed point and beyond 1163 | 1164 | - Enter =fix=, a /fix-point/ combinator, that: 1165 | - takes a function 1166 | - call it with its own returned value made available to it as argument 1167 | 1168 | #+BEGIN_SRC nix 1169 | fix = f: let result = f result; in result; 1170 | #+END_SRC 1171 | 1172 | - The effective call structure is =f(f(f(....= 1173 | 1174 | - If =f= always used the /entirety/ of its argument, that would have been an 1175 | infinite loop. 1176 | 1177 | #+BEGIN_SRC nix 1178 | nix-repl> fix = f: let result = f result; in result 1179 | nix-repl> pkgs = self: { x = "abc"; y = self.x + "123"; } 1180 | nix-repl> fix pkgs 1181 | { x = "abc"; y = "abc123"; } 1182 | #+END_SRC 1183 | 1184 | Squinting a little, we can see how: 1185 | 1186 | 1. the subexpression to the right of =self= is our /repository declaration/, where: 1187 | - =x= and =y= correspond to our package names.. 1188 | - ="abc"=, =self.x + "123"= correspond to our /derivation expressions/ 1189 | 2. =fix pkgs= is the /repository expression/, yielding an attribute set 1190 | mapping names to derivations 1191 | 1192 | - Note, how =y= is determined in terms of =self= -- which isn't quite pretty. 1193 | Can we do without that, so we can squint less? Yes: 1194 | 1195 | #+BEGIN_SRC nix 1196 | nix-repl> fix = f: let result = f result; in result 1197 | nix-repl> pkgs = self: with self; { x = "abc"; y = x + "123"; } 1198 | nix-repl> fix pkgs 1199 | { x = "abc"; y = "abc123"; } 1200 | #+END_SRC 1201 | 1202 | - Notice how we didn't even start with the overriding part, having merely 1203 | established a correspondence between our toy example and Nixpkgs structure. 1204 | So, what about overriding? Let's note several details: 1205 | 1206 | 1. We want to intervene at the level of /repository expression/, separately 1207 | from the /repository declaration/, since =pkgs= has just the perfect 1208 | structure, and we don't want to mess with it. 1209 | 2. By the time we call =fix=, it's too late to intervene -- it seals the 1210 | value. 1211 | 3. Adding up #1 and #2, we see that we need to produce an function that, 1212 | given an overriding set, would transform =pkgs=, but without changing its 1213 | type -- the transformed value must be still something that we can pass to 1214 | =fix=. 1215 | 4. Given our goals, we can postulate the following about this function: 1216 | - it must take an overriding attribute set 1217 | - it must take the object being overriden (a concrete value of =pkgs=) 1218 | - it must return a function that would behave as =pkgs= does, which implies: 1219 | - the returned function must take an attribute set -- the value of =self= 1220 | - the returned function must return an attribute set -- with the 1221 | overrides applied 1222 | 1223 | Here's how this can be done: 1224 | 1225 | #+BEGIN_SRC nix 1226 | nix-repl> pkgs = self: with self; { x = "abc"; y = x + "123"; } 1227 | nix-repl> withOverride = overrides: f: self: f self // overrides 1228 | nix-repl> fix (withOverride { x = "def"; } pkgs) 1229 | { x = "def"; y = "def123"; } 1230 | #+END_SRC 1231 | 1232 | - Now, we would like to use the /package environment/ inside the /override 1233 | expression/, wouldn't we -- for example to be able to override =y=? The 1234 | only way to do that would be to change the /override expression/ from being 1235 | an attribute set to a function taking the /package environment/ and 1236 | returning the attribute set: 1237 | 1238 | #+BEGIN_SRC nix 1239 | nix-repl> pkgs = self: with self; { x = "abc"; y = x + "123"; z = y + "456"; } 1240 | nix-repl> withOverride = overrides: f: self: f self // overrides self 1241 | nix-repl> fix (withOverride (self: with self; { y = x + "---"; }) pkgs) 1242 | { x = "abc"; y = "abc---"; z = "abc---456"; } 1243 | #+END_SRC 1244 | 1245 | - The next step is to package the override method into the attribute set 1246 | itself, in a way similar to how we did it in /Nix Pill #14/: 1247 | 1248 | #+BEGIN_SRC nix 1249 | nix-repl> withOverride = overrides: f: self: f self // overrides self 1250 | nix-repl> virtual = let virt = f: fix f // { _override = overrides: virt (withOverride overrides f); }; in virt 1251 | nix-repl> pkgs = virtual (self: with self; { x = "abc"; y = x + "123"; z = y + "456"; }) 1252 | nix-repl> pkgs 1253 | { _override = «lambda»; x = "abc"; y = "abc123"; z = "abc123456"; } 1254 | nix-repl> pkgs._override (self: with self; { y = x + "---"; }) 1255 | { _override = «lambda»; x = "abc"; y = "abc---"; z = "abc---456"; } 1256 | #+END_SRC 1257 | 1258 | Finally, we have produced a way to define an attribute set, with the following 1259 | properties: 1260 | 1261 | - its definition is recursive -- its elements can be defined in terms of each other 1262 | - it ships with a method to perform a deep override of any element and, 1263 | recursively, all the elements that refer to it 1264 | 1265 | *** Overriding packages globally, the user-exposed way 1266 | 1267 | /Nixpkgs/ exposes the mechanism we examined above through the 1268 | =packageOverrides= attribute in =config= / =~/.nixpkgs/config.nix=: 1269 | 1270 | #+BEGIN_SRC nix 1271 | { 1272 | packageOverrides = pkgs: { 1273 | graphviz = pkgs.graphviz.override { xlibs = null; }; 1274 | }; 1275 | } 1276 | #+END_SRC 1277 | 1278 | Overriding =graphviz= like this will make the change propagate to all packages 1279 | that refer to it. 1280 | 1281 | *** TODO The =~/.nixpkgs/config.nix= file 1282 | 1283 | #+BEGIN_SRC nix 1284 | pkgs = import { config = import ./config.nix; } 1285 | #+END_SRC 1286 | 1287 | * Nix Pill #18: nix store paths 1288 | 1289 | *** Origin: http://lethalman.blogspot.ru/2015/01/nix-pill-18-nix-store-paths.html 1290 | 1291 | *** INCOMPLETE *** 1292 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | Nix-Pills-Compact-Edition.org -------------------------------------------------------------------------------- /amend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | git add Nix-Pills-Compact-Edition.org 5 | git commit --amend --no-edit 6 | --------------------------------------------------------------------------------