Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1# Dhall {#sec-language-dhall} 2 3The Nixpkgs support for Dhall assumes some familiarity with Dhall's language 4support for importing Dhall expressions, which is documented here: 5 6* [`dhall-lang.org` - Installing packages](https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages) 7 8## Remote imports {#ssec-dhall-remote-imports} 9 10Nixpkgs bypasses Dhall's support for remote imports using Dhall's 11semantic integrity checks. Specifically, any Dhall import can be protected by 12an integrity check like: 13 14```dhall 15https://prelude.dhall-lang.org/v20.1.0/package.dhall 16 sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 17``` 18 19… and if the import is cached then the interpreter will load the import from 20cache instead of fetching the URL. 21 22Nixpkgs uses this trick to add all of a Dhall expression's dependencies into the 23cache so that the Dhall interpreter never needs to resolve any remote URLs. In 24fact, Nixpkgs uses a Dhall interpreter with remote imports disabled when 25packaging Dhall expressions to enforce that the interpreter never resolves a 26remote import. This means that Nixpkgs only supports building Dhall expressions 27if all of their remote imports are protected by semantic integrity checks. 28 29Instead of remote imports, Nixpkgs uses Nix to fetch remote Dhall code. For 30example, the Prelude Dhall package uses `pkgs.fetchFromGitHub` to fetch the 31`dhall-lang` repository containing the Prelude. Relying exclusively on Nix 32to fetch Dhall code ensures that Dhall packages built using Nix remain pure and 33also behave well when built within a sandbox. 34 35## Packaging a Dhall expression from scratch {#ssec-dhall-packaging-expression} 36 37We can illustrate how Nixpkgs integrates Dhall by beginning from the following 38trivial Dhall expression with one dependency (the Prelude): 39 40```dhall 41-- ./true.dhall 42 43let Prelude = https://prelude.dhall-lang.org/v20.1.0/package.dhall 44 45in Prelude.Bool.not False 46``` 47 48As written, this expression cannot be built using Nixpkgs because the 49expression does not protect the Prelude import with a semantic integrity 50check, so the first step is to freeze the expression using `dhall freeze`, 51like this: 52 53```ShellSession 54$ dhall freeze --inplace ./true.dhall 55``` 56 57… which gives us: 58 59```dhall 60-- ./true.dhall 61 62let Prelude = 63 https://prelude.dhall-lang.org/v20.1.0/package.dhall 64 sha256:26b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 65 66in Prelude.Bool.not False 67``` 68 69To package that expression, we create a `./true.nix` file containing the 70following specification for the Dhall package: 71 72```nix 73# ./true.nix 74 75{ buildDhallPackage, Prelude }: 76 77buildDhallPackage { 78 name = "true"; 79 code = ./true.dhall; 80 dependencies = [ Prelude ]; 81 source = true; 82} 83``` 84 85… and we complete the build by incorporating that Dhall package into the 86`pkgs.dhallPackages` hierarchy using an overlay, like this: 87 88```nix 89# ./example.nix 90 91let 92 nixpkgs = builtins.fetchTarball { 93 url = "https://github.com/NixOS/nixpkgs/archive/94b2848559b12a8ed1fe433084686b2a81123c99.tar.gz"; 94 hash = "sha256-B4Q3c6IvTLg3Q92qYa8y+i4uTaphtFdjp+Ir3QQjdN0="; 95 }; 96 97 dhallOverlay = self: super: { 98 true = self.callPackage ./true.nix { }; 99 }; 100 101 overlay = self: super: { 102 dhallPackages = super.dhallPackages.override (old: { 103 overrides = self.lib.composeExtensions (old.overrides or (_: _: { })) dhallOverlay; 104 }); 105 }; 106 107 pkgs = import nixpkgs { 108 config = { }; 109 overlays = [ overlay ]; 110 }; 111 112in 113pkgs 114``` 115 116… which we can then build using this command: 117 118```ShellSession 119$ nix build --file ./example.nix dhallPackages.true 120``` 121 122## Contents of a Dhall package {#ssec-dhall-package-contents} 123 124The above package produces the following directory tree: 125 126```ShellSession 127$ tree -a ./result 128result 129├── .cache 130│   └── dhall 131│   └── 122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 132├── binary.dhall 133└── source.dhall 134``` 135 136… where: 137 138* `source.dhall` contains the result of interpreting our Dhall package: 139 140 ```ShellSession 141 $ cat ./result/source.dhall 142 True 143 ``` 144 145* The `.cache` subdirectory contains one binary cache product encoding the 146 same result as `source.dhall`: 147 148 ```ShellSession 149 $ dhall decode < ./result/.cache/dhall/122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 150 True 151 ``` 152 153* `binary.dhall` contains a Dhall expression which handles fetching and decoding 154 the same cache product: 155 156 ```ShellSession 157 $ cat ./result/binary.dhall 158 missing sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 159 $ cp -r ./result/.cache .cache 160 161 $ chmod -R u+w .cache 162 163 $ XDG_CACHE_HOME=.cache dhall --file ./result/binary.dhall 164 True 165 ``` 166 167The `source.dhall` file is only present for packages that specify 168`source = true;`. By default, Dhall packages omit the `source.dhall` in order 169to conserve disk space when they are used exclusively as dependencies. For 170example, if we build the Prelude package it will only contain the binary 171encoding of the expression: 172 173```ShellSession 174$ nix build --file ./example.nix dhallPackages.Prelude 175 176$ tree -a result 177result 178├── .cache 179│   └── dhall 180│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 181└── binary.dhall 182 1832 directories, 2 files 184``` 185 186Typically, you only specify `source = true;` for the top-level Dhall expression 187of interest (such as our example `true.nix` Dhall package). However, if you 188wish to specify `source = true` for all Dhall packages, then you can amend the 189Dhall overlay like this: 190 191```nix 192{ 193 dhallOverrides = self: super: { 194 # Enable source for all Dhall packages 195 buildDhallPackage = args: super.buildDhallPackage (args // { source = true; }); 196 197 true = self.callPackage ./true.nix { }; 198 }; 199} 200``` 201 202… and now the Prelude will contain the fully decoded result of interpreting 203the Prelude: 204 205```ShellSession 206$ nix build --file ./example.nix dhallPackages.Prelude 207 208$ tree -a result 209result 210├── .cache 211│   └── dhall 212│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 213├── binary.dhall 214└── source.dhall 215 216$ cat ./result/source.dhall 217{ Bool = 218 { and = 219 \(_ : List Bool) -> 220 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 && _) True 221 , build = \(_ : Type -> _ -> _@1 -> _@2) -> _ Bool True False 222 , even = 223 \(_ : List Bool) -> 224 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 == _) True 225 , fold = 226 \(_ : Bool) -> 227228``` 229 230## Packaging functions {#ssec-dhall-packaging-functions} 231 232We already saw an example of using `buildDhallPackage` to create a Dhall 233package from a single file, but most Dhall packages consist of more than one 234file and there are two derived utilities that you may find more useful when 235packaging multiple files: 236 237* `buildDhallDirectoryPackage` - build a Dhall package from a local directory 238 239* `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository 240 241The `buildDhallPackage` is the lowest-level function and accepts the following 242arguments: 243 244* `name`: The name of the derivation 245 246* `dependencies`: Dhall dependencies to build and cache ahead of time 247 248* `code`: The top-level expression to build for this package 249 250 Note that the `code` field accepts an arbitrary Dhall expression. You're 251 not limited to just a file. 252 253* `source`: Set to `true` to include the decoded result as `source.dhall` in the 254 build product, at the expense of requiring more disk space 255 256* `documentationRoot`: Set to the root directory of the package if you want 257 `dhall-docs` to generate documentation underneath the `docs` subdirectory of 258 the build product 259 260The `buildDhallDirectoryPackage` is a higher-level function implemented in terms 261of `buildDhallPackage` that accepts the following arguments: 262 263* `name`: Same as `buildDhallPackage` 264 265* `dependencies`: Same as `buildDhallPackage` 266 267* `source`: Same as `buildDhallPackage` 268 269* `src`: The directory containing Dhall code that you want to turn into a Dhall 270 package 271 272* `file`: The top-level file (`package.dhall` by default) that is the entrypoint 273 to the rest of the package 274 275* `document`: Set to `true` to generate documentation for the package 276 277The `buildDhallGitHubPackage` is another higher-level function implemented in 278terms of `buildDhallPackage` that accepts the following arguments: 279 280* `name`: Same as `buildDhallPackage` 281 282* `dependencies`: Same as `buildDhallPackage` 283 284* `source`: Same as `buildDhallPackage` 285 286* `owner`: The owner of the repository 287 288* `repo`: The repository name 289 290* `rev`: The desired revision (or branch, or tag) 291 292* `directory`: The subdirectory of the Git repository to package (if a 293 directory other than the root of the repository) 294 295* `file`: The top-level file (`${directory}/package.dhall` by default) that is 296 the entrypoint to the rest of the package 297 298* `document`: Set to `true` to generate documentation for the package 299 300Additionally, `buildDhallGitHubPackage` accepts the same arguments as 301`fetchFromGitHub`, such as `hash` or `fetchSubmodules`. 302 303## `dhall-to-nixpkgs` {#ssec-dhall-dhall-to-nixpkgs} 304 305You can use the `dhall-to-nixpkgs` command-line utility to automate 306packaging Dhall code. For example: 307 308```ShellSession 309$ nix-shell -p haskellPackages.dhall-nixpkgs nix-prefetch-git 310[nix-shell]$ dhall-to-nixpkgs github https://github.com/Gabriella439/dhall-semver.git 311{ buildDhallGitHubPackage, Prelude }: 312 buildDhallGitHubPackage { 313 name = "dhall-semver"; 314 githubBase = "github.com"; 315 owner = "Gabriella439"; 316 repo = "dhall-semver"; 317 rev = "2d44ae605302ce5dc6c657a1216887fbb96392a4"; 318 fetchSubmodules = false; 319 hash = "sha256-n0nQtswVapWi/x7or0O3MEYmAkt/a1uvlOtnje6GGnk="; 320 directory = ""; 321 file = "package.dhall"; 322 source = false; 323 document = false; 324 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 325 } 326``` 327 328:::{.note} 329`nix-prefetch-git` is added to the `nix-shell -p` invocation above, because it has to be in `$PATH` for `dhall-to-nixpkgs` to work. 330::: 331 332The utility takes care of automatically detecting remote imports and converting 333them to package dependencies. You can also use the utility on local 334Dhall directories, too: 335 336```ShellSession 337$ dhall-to-nixpkgs directory ~/proj/dhall-semver 338{ buildDhallDirectoryPackage, Prelude }: 339 buildDhallDirectoryPackage { 340 name = "proj"; 341 src = ~/proj/dhall-semver; 342 file = "package.dhall"; 343 source = false; 344 document = false; 345 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 346 } 347``` 348 349### Remote imports as fixed-output derivations {#ssec-dhall-remote-imports-as-fod} 350 351`dhall-to-nixpkgs` has the ability to fetch and build remote imports as 352fixed-output derivations by using their Dhall integrity check. This is 353sometimes easier than manually packaging all remote imports. 354 355This can be used like the following: 356 357```ShellSession 358$ dhall-to-nixpkgs directory --fixed-output-derivations ~/proj/dhall-semver 359{ buildDhallDirectoryPackage, buildDhallUrl }: 360 buildDhallDirectoryPackage { 361 name = "proj"; 362 src = ~/proj/dhall-semver; 363 file = "package.dhall"; 364 source = false; 365 document = false; 366 dependencies = [ 367 (buildDhallUrl { 368 url = "https://prelude.dhall-lang.org/v17.0.0/package.dhall"; 369 hash = "sha256-ENs8kZwl6QRoM9+Jeo/+JwHcOQ+giT2VjDQwUkvlpD4="; 370 dhallHash = "sha256:10db3c919c25e9046833df897a8ffe2701dc390fa0893d958c3430524be5a43e"; 371 }) 372 ]; 373 } 374``` 375 376Here, `dhall-semver`'s `Prelude` dependency is fetched and built with the 377`buildDhallUrl` helper function, instead of being passed in as a function 378argument. 379 380## Overriding dependency versions {#ssec-dhall-overriding-dependency-versions} 381 382Suppose that we change our `true.dhall` example expression to depend on an older 383version of the Prelude (19.0.0): 384 385```dhall 386-- ./true.dhall 387 388let Prelude = 389 https://prelude.dhall-lang.org/v19.0.0/package.dhall 390 sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 391 392in Prelude.Bool.not False 393``` 394 395If we try to rebuild that expression the build will fail: 396 397```ShellSession 398$ nix build --file ./example.nix dhallPackages.true 399builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines: 400 401 Dhall was compiled without the 'with-http' flag. 402 403 The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall 404 405 406 4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall 407 5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 408 409 /nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7 410[1 built (1 failed), 0.0 MiB DL] 411error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed 412``` 413 414… because the default Prelude selected by Nixpkgs revision 415`94b2848559b12a8ed1fe433084686b2a81123c99is` is version 20.1.0, which doesn't 416have the same integrity check as version 19.0.0. This means that version 41719.0.0 is not cached and the interpreter is not allowed to fall back to 418importing the URL. 419 420However, we can override the default Prelude version by using `dhall-to-nixpkgs` 421to create a Dhall package for our desired Prelude: 422 423```ShellSession 424$ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \ 425 --name Prelude \ 426 --directory Prelude \ 427 --rev v19.0.0 \ 428 > Prelude.nix 429``` 430 431… and then referencing that package in our Dhall overlay, by either overriding 432the Prelude globally for all packages, like this: 433 434```nix 435{ 436 dhallOverrides = self: super: { 437 true = self.callPackage ./true.nix { }; 438 439 Prelude = self.callPackage ./Prelude.nix { }; 440 }; 441} 442``` 443 444… or selectively overriding the Prelude dependency for just the `true` package, 445like this: 446 447```nix 448{ 449 dhallOverrides = self: super: { 450 true = self.callPackage ./true.nix { 451 Prelude = self.callPackage ./Prelude.nix { }; 452 }; 453 }; 454} 455``` 456 457## Overrides {#ssec-dhall-overrides} 458 459You can override any of the arguments to `buildDhallGitHubPackage` or 460`buildDhallDirectoryPackage` using the `overridePackage` attribute of a package. 461For example, suppose we wanted to selectively enable `source = true` just for the Prelude. We can do that like this: 462 463```nix 464{ 465 dhallOverrides = self: super: { 466 Prelude = super.Prelude.overridePackage { source = true; }; 467 468 # ... 469 }; 470} 471``` 472 473[semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages