nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at litex 463 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 sha256 = "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 dhallOverrides = self: super: { 191 # Enable source for all Dhall packages 192 buildDhallPackage = 193 args: super.buildDhallPackage (args // { source = true; }); 194 195 true = self.callPackage ./true.nix { }; 196 }; 197``` 198 199… and now the Prelude will contain the fully decoded result of interpreting 200the Prelude: 201 202```ShellSession 203$ nix build --file ./example.nix dhallPackages.Prelude 204 205$ tree -a result 206result 207├── .cache 208│   └── dhall 209│   └── 122026b0ef498663d269e4dc6a82b0ee289ec565d683ef4c00d0ebdd25333a5a3c98 210├── binary.dhall 211└── source.dhall 212 213$ cat ./result/source.dhall 214{ Bool = 215 { and = 216 \(_ : List Bool) -> 217 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 && _) True 218 , build = \(_ : Type -> _ -> _@1 -> _@2) -> _ Bool True False 219 , even = 220 \(_ : List Bool) -> 221 List/fold Bool _ Bool (\(_ : Bool) -> \(_ : Bool) -> _@1 == _) True 222 , fold = 223 \(_ : Bool) -> 224225``` 226 227## Packaging functions {#ssec-dhall-packaging-functions} 228 229We already saw an example of using `buildDhallPackage` to create a Dhall 230package from a single file, but most Dhall packages consist of more than one 231file and there are two derived utilities that you may find more useful when 232packaging multiple files: 233 234* `buildDhallDirectoryPackage` - build a Dhall package from a local directory 235 236* `buildDhallGitHubPackage` - build a Dhall package from a GitHub repository 237 238The `buildDhallPackage` is the lowest-level function and accepts the following 239arguments: 240 241* `name`: The name of the derivation 242 243* `dependencies`: Dhall dependencies to build and cache ahead of time 244 245* `code`: The top-level expression to build for this package 246 247 Note that the `code` field accepts an arbitrary Dhall expression. You're 248 not limited to just a file. 249 250* `source`: Set to `true` to include the decoded result as `source.dhall` in the 251 build product, at the expense of requiring more disk space 252 253* `documentationRoot`: Set to the root directory of the package if you want 254 `dhall-docs` to generate documentation underneath the `docs` subdirectory of 255 the build product 256 257The `buildDhallDirectoryPackage` is a higher-level function implemented in terms 258of `buildDhallPackage` that accepts the following arguments: 259 260* `name`: Same as `buildDhallPackage` 261 262* `dependencies`: Same as `buildDhallPackage` 263 264* `source`: Same as `buildDhallPackage` 265 266* `src`: The directory containing Dhall code that you want to turn into a Dhall 267 package 268 269* `file`: The top-level file (`package.dhall` by default) that is the entrypoint 270 to the rest of the package 271 272* `document`: Set to `true` to generate documentation for the package 273 274The `buildDhallGitHubPackage` is another higher-level function implemented in 275terms of `buildDhallPackage` that accepts the following arguments: 276 277* `name`: Same as `buildDhallPackage` 278 279* `dependencies`: Same as `buildDhallPackage` 280 281* `source`: Same as `buildDhallPackage` 282 283* `owner`: The owner of the repository 284 285* `repo`: The repository name 286 287* `rev`: The desired revision (or branch, or tag) 288 289* `directory`: The subdirectory of the Git repository to package (if a 290 directory other than the root of the repository) 291 292* `file`: The top-level file (`${directory}/package.dhall` by default) that is 293 the entrypoint to the rest of the package 294 295* `document`: Set to `true` to generate documentation for the package 296 297Additionally, `buildDhallGitHubPackage` accepts the same arguments as 298`fetchFromGitHub`, such as `hash` or `fetchSubmodules`. 299 300## `dhall-to-nixpkgs` {#ssec-dhall-dhall-to-nixpkgs} 301 302You can use the `dhall-to-nixpkgs` command-line utility to automate 303packaging Dhall code. For example: 304 305```ShellSession 306$ nix-env --install --attr haskellPackages.dhall-nixpkgs 307 308$ nix-env --install --attr nix-prefetch-git # Used by dhall-to-nixpkgs 309 310$ 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 328The utility takes care of automatically detecting remote imports and converting 329them to package dependencies. You can also use the utility on local 330Dhall directories, too: 331 332```ShellSession 333$ dhall-to-nixpkgs directory ~/proj/dhall-semver 334{ buildDhallDirectoryPackage, Prelude }: 335 buildDhallDirectoryPackage { 336 name = "proj"; 337 src = ~/proj/dhall-semver; 338 file = "package.dhall"; 339 source = false; 340 document = false; 341 dependencies = [ (Prelude.overridePackage { file = "package.dhall"; }) ]; 342 } 343``` 344 345### Remote imports as fixed-output derivations {#ssec-dhall-remote-imports-as-fod} 346 347`dhall-to-nixpkgs` has the ability to fetch and build remote imports as 348fixed-output derivations by using their Dhall integrity check. This is 349sometimes easier than manually packaging all remote imports. 350 351This can be used like the following: 352 353```ShellSession 354$ dhall-to-nixpkgs directory --fixed-output-derivations ~/proj/dhall-semver 355{ buildDhallDirectoryPackage, buildDhallUrl }: 356 buildDhallDirectoryPackage { 357 name = "proj"; 358 src = ~/proj/dhall-semver; 359 file = "package.dhall"; 360 source = false; 361 document = false; 362 dependencies = [ 363 (buildDhallUrl { 364 url = "https://prelude.dhall-lang.org/v17.0.0/package.dhall"; 365 hash = "sha256-ENs8kZwl6QRoM9+Jeo/+JwHcOQ+giT2VjDQwUkvlpD4="; 366 dhallHash = "sha256:10db3c919c25e9046833df897a8ffe2701dc390fa0893d958c3430524be5a43e"; 367 }) 368 ]; 369 } 370``` 371 372Here, `dhall-semver`'s `Prelude` dependency is fetched and built with the 373`buildDhallUrl` helper function, instead of being passed in as a function 374argument. 375 376## Overriding dependency versions {#ssec-dhall-overriding-dependency-versions} 377 378Suppose that we change our `true.dhall` example expression to depend on an older 379version of the Prelude (19.0.0): 380 381```dhall 382-- ./true.dhall 383 384let Prelude = 385 https://prelude.dhall-lang.org/v19.0.0/package.dhall 386 sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 387 388in Prelude.Bool.not False 389``` 390 391If we try to rebuild that expression the build will fail: 392 393```ShellSession 394$ nix build --file ./example.nix dhallPackages.true 395builder for '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed with exit code 1; last 10 log lines: 396 397 Dhall was compiled without the 'with-http' flag. 398 399 The requested URL was: https://prelude.dhall-lang.org/v19.0.0/package.dhall 400 401 402 4│ https://prelude.dhall-lang.org/v19.0.0/package.dhall 403 5│ sha256:eb693342eb769f782174157eba9b5924cf8ac6793897fc36a31ccbd6f56dafe2 404 405 /nix/store/rsab4y99h14912h4zplqx2iizr5n4rc2-true.dhall:4:7 406[1 built (1 failed), 0.0 MiB DL] 407error: build of '/nix/store/0f1hla7ff1wiaqyk1r2ky4wnhnw114fi-true.drv' failed 408``` 409 410… because the default Prelude selected by Nixpkgs revision 411`94b2848559b12a8ed1fe433084686b2a81123c99is` is version 20.1.0, which doesn't 412have the same integrity check as version 19.0.0. This means that version 41319.0.0 is not cached and the interpreter is not allowed to fall back to 414importing the URL. 415 416However, we can override the default Prelude version by using `dhall-to-nixpkgs` 417to create a Dhall package for our desired Prelude: 418 419```ShellSession 420$ dhall-to-nixpkgs github https://github.com/dhall-lang/dhall-lang.git \ 421 --name Prelude \ 422 --directory Prelude \ 423 --rev v19.0.0 \ 424 > Prelude.nix 425``` 426 427… and then referencing that package in our Dhall overlay, by either overriding 428the Prelude globally for all packages, like this: 429 430```nix 431 dhallOverrides = self: super: { 432 true = self.callPackage ./true.nix { }; 433 434 Prelude = self.callPackage ./Prelude.nix { }; 435 }; 436``` 437 438… or selectively overriding the Prelude dependency for just the `true` package, 439like this: 440 441```nix 442 dhallOverrides = self: super: { 443 true = self.callPackage ./true.nix { 444 Prelude = self.callPackage ./Prelude.nix { }; 445 }; 446 }; 447``` 448 449## Overrides {#ssec-dhall-overrides} 450 451You can override any of the arguments to `buildDhallGitHubPackage` or 452`buildDhallDirectoryPackage` using the `overridePackage` attribute of a package. 453For example, suppose we wanted to selectively enable `source = true` just for the Prelude. We can do that like this: 454 455```nix 456 dhallOverrides = self: super: { 457 Prelude = super.Prelude.overridePackage { source = true; }; 458 459460 }; 461``` 462 463[semantic-integrity-checks]: https://docs.dhall-lang.org/tutorials/Language-Tour.html#installing-packages