nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at fix-function-merge 472 lines 14 kB view raw view rendered
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 = 104 self.lib.composeExtensions (old.overrides or (_: _: {})) dhallOverlay; 105 }); 106 }; 107 108 pkgs = import nixpkgs { config = {}; overlays = [ overlay ]; }; 109 110in 111 pkgs 112``` 113 114… which we can then build using this command: 115 116```ShellSession 117$ nix build --file ./example.nix dhallPackages.true 118``` 119 120## Contents of a Dhall package {#ssec-dhall-package-contents} 121 122The above package produces the following directory tree: 123 124```ShellSession 125$ tree -a ./result 126result 127├── .cache 128│   └── dhall 129│   └── 122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 130├── binary.dhall 131└── source.dhall 132``` 133 134… where: 135 136* `source.dhall` contains the result of interpreting our Dhall package: 137 138 ```ShellSession 139 $ cat ./result/source.dhall 140 True 141 ``` 142 143* The `.cache` subdirectory contains one binary cache product encoding the 144 same result as `source.dhall`: 145 146 ```ShellSession 147 $ dhall decode < ./result/.cache/dhall/122027abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 148 True 149 ``` 150 151* `binary.dhall` contains a Dhall expression which handles fetching and decoding 152 the same cache product: 153 154 ```ShellSession 155 $ cat ./result/binary.dhall 156 missing sha256:27abdeddfe8503496adeb623466caa47da5f63abd2bc6fa19f6cfcb73ecfed70 157 $ cp -r ./result/.cache .cache 158 159 $ chmod -R u+w .cache 160 161 $ XDG_CACHE_HOME=.cache dhall --file ./result/binary.dhall 162 True 163 ``` 164 165The `source.dhall` file is only present for packages that specify 166`source = true;`. By default, Dhall packages omit the `source.dhall` in order 167to conserve disk space when they are used exclusively as dependencies. For 168example, if we build the Prelude package it will only contain the binary 169encoding of the expression: 170 171```ShellSession 172$ nix build --file ./example.nix dhallPackages.Prelude 173 174$ tree -a result 175result 176├── .cache 177│   └── dhall 178│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 179└── binary.dhall 180 1812 directories, 2 files 182``` 183 184Typically, you only specify `source = true;` for the top-level Dhall expression 185of interest (such as our example `true.nix` Dhall package). However, if you 186wish to specify `source = true` for all Dhall packages, then you can amend the 187Dhall overlay like this: 188 189```nix 190{ 191 dhallOverrides = self: super: { 192 # Enable source for all Dhall packages 193 buildDhallPackage = 194 args: super.buildDhallPackage (args // { source = true; }); 195 196 true = self.callPackage ./true.nix { }; 197 }; 198} 199``` 200 201… and now the Prelude will contain the fully decoded result of interpreting 202the Prelude: 203 204```ShellSession 205$ nix build --file ./example.nix dhallPackages.Prelude 206 207$ tree -a result 208result 209├── .cache 210│   └── dhall 211│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 212├── binary.dhall 213└── source.dhall 214 215$ cat ./result/source.dhall 216{ Bool = 217 { and = 218 \(_ : List Bool) -> 219 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 && _) True 220 , build = \(_ : Type -> _ -> _@1 -> _@2) -> _ Bool True False 221 , even = 222 \(_ : List Bool) -> 223 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 == _) True 224 , fold = 225 \(_ : Bool) -> 226227``` 228 229## Packaging functions {#ssec-dhall-packaging-functions} 230 231We already saw an example of using `buildDhallPackage` to create a Dhall 232package from a single file, but most Dhall packages consist of more than one 233file and there are two derived utilities that you may find more useful when 234packaging multiple files: 235 236* `buildDhallDirectoryPackage` - build a Dhall package from a local directory 237 238* `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository 239 240The `buildDhallPackage` is the lowest-level function and accepts the following 241arguments: 242 243* `name`: The name of the derivation 244 245* `dependencies`: Dhall dependencies to build and cache ahead of time 246 247* `code`: The top-level expression to build for this package 248 249 Note that the `code` field accepts an arbitrary Dhall expression. You're 250 not limited to just a file. 251 252* `source`: Set to `true` to include the decoded result as `source.dhall` in the 253 build product, at the expense of requiring more disk space 254 255* `documentationRoot`: Set to the root directory of the package if you want 256 `dhall-docs` to generate documentation underneath the `docs` subdirectory of 257 the build product 258 259The `buildDhallDirectoryPackage` is a higher-level function implemented in terms 260of `buildDhallPackage` that accepts the following arguments: 261 262* `name`: Same as `buildDhallPackage` 263 264* `dependencies`: Same as `buildDhallPackage` 265 266* `source`: Same as `buildDhallPackage` 267 268* `src`: The directory containing Dhall code that you want to turn into a Dhall 269 package 270 271* `file`: The top-level file (`package.dhall` by default) that is the entrypoint 272 to the rest of the package 273 274* `document`: Set to `true` to generate documentation for the package 275 276The `buildDhallGitHubPackage` is another higher-level function implemented in 277terms of `buildDhallPackage` that accepts the following arguments: 278 279* `name`: Same as `buildDhallPackage` 280 281* `dependencies`: Same as `buildDhallPackage` 282 283* `source`: Same as `buildDhallPackage` 284 285* `owner`: The owner of the repository 286 287* `repo`: The repository name 288 289* `rev`: The desired revision (or branch, or tag) 290 291* `directory`: The subdirectory of the Git repository to package (if a 292 directory other than the root of the repository) 293 294* `file`: The top-level file (`${directory}/package.dhall` by default) that is 295 the entrypoint to the rest of the package 296 297* `document`: Set to `true` to generate documentation for the package 298 299Additionally, `buildDhallGitHubPackage` accepts the same arguments as 300`fetchFromGitHub`, such as `hash` or `fetchSubmodules`. 301 302## `dhall-to-nixpkgs` {#ssec-dhall-dhall-to-nixpkgs} 303 304You can use the `dhall-to-nixpkgs` command-line utility to automate 305packaging Dhall code. For example: 306 307```ShellSession 308$ nix-shell -p haskellPackages.dhall-nixpkgs nix-prefetch-git 309[nix-shell]$ dhall-to-nixpkgs github https://github.com/Gabriella439/dhall-semver.git 310{ buildDhallGitHubPackage, Prelude }: 311 buildDhallGitHubPackage { 312 name = "dhall-semver"; 313 githubBase = "github.com"; 314 owner = "Gabriella439"; 315 repo = "dhall-semver"; 316 rev = "2d44ae605302ce5dc6c657a1216887fbb96392a4"; 317 fetchSubmodules = false; 318 hash = "sha256-n0nQtswVapWi/x7or0O3MEYmAkt/a1uvlOtnje6GGnk="; 319 directory = ""; 320 file = "package.dhall"; 321 source = false; 322 document = false; 323 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 324 } 325``` 326 327:::{.note} 328`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. 329::: 330 331The utility takes care of automatically detecting remote imports and converting 332them to package dependencies. You can also use the utility on local 333Dhall directories, too: 334 335```ShellSession 336$ dhall-to-nixpkgs directory ~/proj/dhall-semver 337{ buildDhallDirectoryPackage, Prelude }: 338 buildDhallDirectoryPackage { 339 name = "proj"; 340 src = ~/proj/dhall-semver; 341 file = "package.dhall"; 342 source = false; 343 document = false; 344 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 345 } 346``` 347 348### Remote imports as fixed-output derivations {#ssec-dhall-remote-imports-as-fod} 349 350`dhall-to-nixpkgs` has the ability to fetch and build remote imports as 351fixed-output derivations by using their Dhall integrity check. This is 352sometimes easier than manually packaging all remote imports. 353 354This can be used like the following: 355 356```ShellSession 357$ dhall-to-nixpkgs directory --fixed-output-derivations ~/proj/dhall-semver 358{ buildDhallDirectoryPackage, buildDhallUrl }: 359 buildDhallDirectoryPackage { 360 name = "proj"; 361 src = ~/proj/dhall-semver; 362 file = "package.dhall"; 363 source = false; 364 document = false; 365 dependencies = [ 366 (buildDhallUrl { 367 url = "https://prelude.dhall-lang.org/v17.0.0/package.dhall"; 368 hash = "sha256-ENs8kZwl6QRoM9+Jeo/+JwHcOQ+giT2VjDQwUkvlpD4="; 369 dhallHash = "sha256:10db3c919c25e9046833df897a8ffe2701dc390fa0893d958c3430524be5a43e"; 370 }) 371 ]; 372 } 373``` 374 375Here, `dhall-semver`'s `Prelude` dependency is fetched and built with the 376`buildDhallUrl` helper function, instead of being passed in as a function 377argument. 378 379## Overriding dependency versions {#ssec-dhall-overriding-dependency-versions} 380 381Suppose that we change our `true.dhall` example expression to depend on an older 382version of the Prelude (19.0.0): 383 384```dhall 385-- ./true.dhall 386 387let Prelude = 388 https://prelude.dhall-lang.org/v19.0.0/package.dhall 389 sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 390 391in Prelude.Bool.not False 392``` 393 394If we try to rebuild that expression the build will fail: 395 396```ShellSession 397$ nix build --file ./example.nix dhallPackages.true 398builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines: 399 400 Dhall was compiled without the 'with-http' flag. 401 402 The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall 403 404 405 4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall 406 5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 407 408 /nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7 409[1 built (1 failed), 0.0 MiB DL] 410error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed 411``` 412 413… because the default Prelude selected by Nixpkgs revision 414`94b2848559b12a8ed1fe433084686b2a81123c99is` is version 20.1.0, which doesn't 415have the same integrity check as version 19.0.0. This means that version 41619.0.0 is not cached and the interpreter is not allowed to fall back to 417importing the URL. 418 419However, we can override the default Prelude version by using `dhall-to-nixpkgs` 420to create a Dhall package for our desired Prelude: 421 422```ShellSession 423$ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \ 424 --name Prelude \ 425 --directory Prelude \ 426 --rev v19.0.0 \ 427 > Prelude.nix 428``` 429 430… and then referencing that package in our Dhall overlay, by either overriding 431the Prelude globally for all packages, like this: 432 433```nix 434{ 435 dhallOverrides = self: super: { 436 true = self.callPackage ./true.nix { }; 437 438 Prelude = self.callPackage ./Prelude.nix { }; 439 }; 440} 441``` 442 443… or selectively overriding the Prelude dependency for just the `true` package, 444like this: 445 446```nix 447{ 448 dhallOverrides = self: super: { 449 true = self.callPackage ./true.nix { 450 Prelude = self.callPackage ./Prelude.nix { }; 451 }; 452 }; 453} 454``` 455 456## Overrides {#ssec-dhall-overrides} 457 458You can override any of the arguments to `buildDhallGitHubPackage` or 459`buildDhallDirectoryPackage` using the `overridePackage` attribute of a package. 460For example, suppose we wanted to selectively enable `source = true` just for the Prelude. We can do that like this: 461 462```nix 463{ 464 dhallOverrides = self: super: { 465 Prelude = super.Prelude.overridePackage { source = true; }; 466 467 # ... 468 }; 469} 470``` 471 472[semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages