├── .gitignore ├── DOCUMENTATION.md ├── LICENSE ├── README.md ├── default.nix ├── examples ├── 0install │ └── flake.nix ├── coq │ └── flake.nix ├── docfile │ ├── default.nix │ ├── empty │ │ └── .gitkeep │ ├── my-package-dune │ │ ├── .gitkeep │ │ ├── bin │ │ │ ├── dune │ │ │ └── hello.ml │ │ ├── dune-project │ │ └── hello.ml │ ├── my-package │ │ ├── .gitkeep │ │ ├── hello.ml │ │ ├── my-package.opam │ │ ├── package-defs.json │ │ └── test.sh │ └── opam-import │ │ ├── .gitkeep │ │ ├── hello.ml │ │ ├── my-package.opam │ │ └── opam.export ├── frama-c │ └── flake.nix ├── materialized-opam-ed │ ├── flake.nix │ └── package-defs.json ├── ocaml-lsp │ └── flake.nix ├── opam-ed │ └── flake.nix ├── opam2json-static │ └── flake.nix ├── opam2json │ └── flake.nix └── tezos │ └── flake.nix ├── flake.lock ├── flake.nix ├── patches └── ocamlfind │ ├── install_topfind_192.patch │ ├── install_topfind_193.patch │ ├── install_topfind_194.patch │ ├── install_topfind_196.patch │ └── install_topfind_198.patch ├── scripts ├── opam-nix-gen.in └── opam-nix-regen.in ├── src ├── builder.nix ├── evaluator │ ├── default.nix │ └── setup.sh ├── global-variables.nix ├── lib.nix ├── opam.nix └── overlays │ ├── external │ ├── debian.nix │ └── homebrew.nix │ ├── lib.nix │ ├── ocaml-darwin.nix │ ├── ocaml-static.nix │ └── ocaml.nix └── templates ├── executable ├── .envrc └── flake.nix ├── multi-package ├── .envrc └── flake.nix └── simple └── flake.nix /.gitignore: -------------------------------------------------------------------------------- 1 | result* -------------------------------------------------------------------------------- /DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | # `opam-nix` documentation 2 | 3 | ## Terminology 4 | 5 | ### Package 6 | 7 | `Derivation` 8 | 9 | An `opam-nix` "Package" is just a nixpkgs-based derivation which has 10 | some additional properties. It corresponds to an opam package (more 11 | specifically, it directly corresponds to an opam package installed in 12 | some switch, switch being the [Scope](#Scope)). 13 | 14 | Its output contains an empty file `nix-support/is-opam-nix-package`, 15 | and also it has a `nix-support/setup-hook` setting some internal 16 | variables, `OCAMLPATH`, `CAML_LD_LIBRARY_PATH`, and other variables 17 | exported by packages using `variables` in `.config` or 18 | `setenv` in `.opam`. 19 | 20 | The derivation has a `passthru.pkgdef` attribute, which can be used to 21 | get information about the opam file this Package came from. It can also 22 | be overriden with `overrideAttrs` to alter the generated package. 23 | 24 | The behaviour of the build script can be controlled using build-time 25 | environment variables. If you want to set an opam environment variable 26 | (be it for substitution or a package filter), you can do so by passing 27 | it to `overrideAttrs` of the package with a special transformation 28 | applied to the variable's name: replace `-` and `+` in the name with 29 | underscores (`_`), replace all `:` (separators between package names 30 | and their corresponding variables) with two underscores (`__`), and 31 | prepend `opam__`. For example, if you want to get a package `cmdliner` 32 | with `conf-g++:installed` set to `true`, do `cmdliner.overrideAttrs 33 | (_: { opam__conf_g____installed = "true"; })`. 34 | 35 | If you wish to change the build in some other arbitrary way, do so as 36 | you would with any nixpkgs package. 37 | 38 | Some special attributes which you can override are: 39 | 40 | - You can override phases, but note that `configurePhase` is special and 41 | should not be overriden (unless you read `builder.nix` and understand 42 | what it is doing); 43 | - `buildInputs`: Add libraries (whether OCaml or system) to the package build. 44 | Note that you can add packages to the scope and then take them from `final` 45 | argument of the overlay; 46 | - `nativeBuildInputs`: Add binaries that are needed at build-time (e.g. 47 | compilers, documentation generators, etc). 48 | - `withFakeOpam` (default `true`): Whether to provide a fake opam executable 49 | during the build & install phases. This executable supports a small subset of 50 | opam subcommands, allowing some configuration tooling to query it for 51 | build-time information (e.g. config variables). Disabling it can be useful if 52 | you provide a real opam in the environment and it is clashing with the fake 53 | one. 54 | - `dontPatchShebangsEarly` (default `false`): Disable patching shebangs in all 55 | scripts in the source directory before running any build or install commands. 56 | Can be useful if the shebangs are Nix-compatible already (e.g. with 57 | `/usr/bin/env`) and changing them invalidates some hash (e.g. the git index 58 | hash). [See also](https://github.com/tweag/opam-nix/issues/7) 59 | - `doNixSupport` (default `true`): Whether to produce `$out/nix-support`. This 60 | handles both build input and environment variable propagation to dependencies. 61 | + `propagateInputs` (default `true`): Whether to propagate all transitive 62 | build inputs to dependencies. 63 | + `exportSetupHook` (default `true`): Whether to propagate environment 64 | variables (e.g. set with `build-env`) to dependencies. 65 | - `removeOcamlReferences` (default `false`): Whether to remove all references to 66 | the ocaml compiler from the resulting executable. Can reduce the size of the 67 | resulting closure. Might break the application. 68 | 69 | ### Repository 70 | 71 | `Path or Derivation` 72 | 73 | A repository is understood in the same way as for opam itself. It is a 74 | directory (or a derivation producing a directory) which contains at 75 | least `repo` and `packages/`. Directories in `packages` must be 76 | package names, directories in those must be of the format 77 | `name.version`, and each such subdirectory must have at least an 78 | `opam` file. 79 | 80 | If a repository is a derivation, it may contain `passthru.sourceMap`, 81 | which maps package names to their corresponding sources. 82 | 83 | ### Query 84 | 85 | `{ ${package_name} = package_version : String or "*"; ... }` 86 | 87 | A "query" is a attrset, mapping from package names to package 88 | versions. It is used to "query" the repositories for the required 89 | packages and their versions. A special version of `"*"` means 90 | "latest" for functions dealing with version resolution 91 | (i.e. `opamList`), and shouldn't be used elsewhere. 92 | 93 | ### Scope 94 | 95 | ``` 96 | { overrideScope = (Scope → Scope → Scope) → Scope 97 | ; callPackage = (Dependencies → Package) → Dependencies → Package 98 | ; ${package_name} = package : Package; ... } 99 | ``` 100 | 101 | A [nixpkgs "scope" (package 102 | set)](https://github.com/NixOS/nixpkgs/blob/5f596e2bf5bea4a5d378883b37fc124fb39f5447/lib/customisation.nix#L199). 103 | The scope is self-referential, i.e. packages in the set may refer to 104 | other packages from that same set. A scope corresponds to an opam 105 | switch, with the most important difference being that packages are 106 | built in isolation and can't alter the outputs of other packages, or 107 | use packages which aren't in their dependency tree. 108 | 109 | Note that there can only be one version of each package in the set, 110 | due to constraints in OCaml's way of linking. 111 | 112 | `overrideScope` can be used to apply overlays to the scope, and 113 | `callPackage` can be used to get `Package`s from the output of 114 | `opam2nix`, with dependencies supplied from the scope. 115 | 116 | ## Public-facing API 117 | 118 | Public-facing API is presented in the `lib` output of the flake. It is 119 | mapped over the platforms, e.g. `lib.x86_64-linux` provides the 120 | functions usable on x86_64-linux. Functions documented below reside in 121 | those per-platform package sets, so if you want to use 122 | e.g. `makeOpamRepo`, you'll have to use 123 | `opam-nix.lib.x86_64-linux.makeOpamRepo`. All examples assume that the 124 | relevant per-platform `lib` is in scope, something like this flake: 125 | 126 | ```nix 127 | { 128 | inputs.opam-nix.url = "github:tweag/opam-nix"; 129 | inputs.flake-utils.url = "github:numtide/flake-utils"; 130 | 131 | outputs = { self, opam-nix, flake-utils }: 132 | flake-utils.lib.eachDefaultSystem (system: 133 | with opam-nix.lib.${system}; { 134 | defaultPackage = # 135 | }); 136 | } 137 | ``` 138 | 139 | Or this flake-less expression: 140 | 141 | ``` 142 | with (import (builtins.fetchTarball "https://github.com/tweag/opam-nix/archive/main.tar.gz")).lib.${builtins.currentSystem}; 143 | # example goes here 144 | ``` 145 | 146 | You can instantiate `opam.nix` yourself, by passing at least some 147 | `pkgs` (containing `opam2json`), and optionally `opam-repository` for 148 | use as the default repository (if you don't pass `opam-repository`, 149 | `repos` argument becomes required everywhere). 150 | 151 | ### `queryToScope` 152 | 153 | ``` 154 | { repos = ?[Repository] 155 | ; pkgs = ?Nixpkgs 156 | ; overlays = ?[Overlay] 157 | ; resolveArgs = ?ResolveArgs } 158 | → Query 159 | → Scope 160 | ``` 161 | 162 | ``` 163 | ResolveEnv : { ${var_name} = value : String; ... } 164 | ``` 165 | 166 | ``` 167 | ResolveArgs : 168 | { env = ?ResolveEnv 169 | ; with-test = ?Bool 170 | ; with-doc = ?Bool 171 | ; dev = ?Bool 172 | ; depopts = ?Bool 173 | ; best-effort = ?Bool 174 | } 175 | ``` 176 | 177 | Turn a `Query` into a `Scope`. 178 | 179 | Special value of `"*"` can be passed as a version in the `Query` to 180 | let opam figure out the latest possible version for the package. 181 | 182 | The first argument allows providing custom repositories & top-level 183 | nixpkgs, adding overlays and passing an environment to the resolver. 184 | 185 | #### `repos`, `env` & version resolution 186 | 187 | Versions are resolved using upstream opam. The passed repositories 188 | (`repos`, containing `opam-repository` by default) are merged and then 189 | `opam admin list --resolve` is called on the resulting 190 | directory. Package versions from earlier repositories take precedence 191 | over package versions from later repositories. `env` allows to pass 192 | additional "environment" to `opam admin list`, affecting its version 193 | resolution decisions. See [`man 194 | opam-admin`](https://opam.ocaml.org/doc/man/opam-admin.html) for 195 | further information about the environment. 196 | 197 | When a repository in `repos` is a derivation and contains 198 | `passthru.sourceMap`, sources for packages taken from that repository 199 | are taken from that source map. 200 | 201 | #### `pkgs` & `overlays` 202 | 203 | By default, `pkgs` match the `pkgs` argument to `opam.nix`, which, in 204 | turn, is the `nixpkgs` input of the flake. `overlays` default to 205 | `defaultOverlay` and `staticOverlay` in case the passed nixpkgs appear 206 | to be targeting static building. 207 | 208 | #### Examples 209 | 210 | Build a package from `opam-repository`, using all sane defaults: 211 | 212 |
213 | 214 | 215 | 216 | ```nix 217 | (queryToScope { } { opam-ed = "*"; ocaml-system = "*"; }).opam-ed 218 | ``` 219 | 220 |
221 | 222 | Build a specific version of the package, overriding some dependencies: 223 | 224 |
225 | 226 | 227 | 228 | ```nix 229 | let 230 | scope = queryToScope { } { opam-ed = "0.3"; ocaml-system = "*"; }; 231 | overlay = self: super: { 232 | opam-file-format = super.opam-file-format.overrideAttrs 233 | (oa: { opam__ocaml__native = "true"; }); 234 | }; 235 | in (scope.overrideScope overlay).opam-ed 236 | ``` 237 | 238 |
239 | 240 | Pass static nixpkgs (to get statically linked libraries and 241 | executables): 242 | 243 |
244 | 245 | 246 | 247 | ```nix 248 | let 249 | scope = queryToScope { 250 | pkgs = pkgsStatic; 251 | } { opam-ed = "*"; ocaml-system = "*"; }; 252 | in scope.opam-ed 253 | ``` 254 | 255 |
256 | 257 | ### `buildOpamProject` 258 | 259 | ``` 260 | { repos = ?[Repository] 261 | ; pkgs = ?Nixpkgs 262 | ; overlays = ?[Overlay] 263 | ; resolveArgs = ?ResolveArgs 264 | ; pinDepends = ?Bool 265 | ; recursive = ?Bool } 266 | → name: String 267 | → project: Path 268 | → Query 269 | → Scope 270 | ``` 271 | 272 | A convenience wrapper around `queryToScope`. 273 | 274 | Turn an opam project (found in the directory passed as the third argument) into 275 | a `Scope`. More concretely, produce a scope containing the package called `name` 276 | from the `project` directory, together with other packages from the `Query`. 277 | 278 | Analogous to `opam install .`. 279 | 280 | The first argument is the same as the first argument of `queryToScope`, except 281 | the repository produced by calling `makeOpamRepo` on the project directory is 282 | prepended to `repos`. An additional `pinDepends` attribute can be supplied. When 283 | `true`, it pins the dependencies specified in `pin-depends` of the packages in 284 | the project. 285 | 286 | `recursive` controls whether subdirectories are searched for opam files (when 287 | `true`), or only the top-level project directory and the `opam/` subdirectory 288 | (when `false`). 289 | 290 | #### Examples 291 | 292 | Build a package from a local directory: 293 | 294 |
295 | 296 | 297 | 298 | ```nix 299 | (buildOpamProject { } "my-package" ./. { }).my-package 300 | ``` 301 | 302 |
303 | 304 | Build a package from a local directory, forcing opam to use the 305 | non-"system" compiler: 306 | 307 |
308 | 309 | 310 | 311 | ```nix 312 | (buildOpamProject { } "my-package" ./. { ocaml-base-compiler = "*"; }).my-package 313 | ``` 314 | 315 |
316 | 317 | Building a statically linked library or binary from a local directory: 318 | 319 |
320 | 321 | 322 | 323 | ```nix 324 | (buildOpamProject { pkgs = pkgsStatic; } "my-package" ./. { }).my-package 325 | ``` 326 | 327 |
328 | 329 | Build a project with tests: 330 | 331 |
332 | 333 | 334 | 335 | ```nix 336 | (buildOpamProject { resolveArgs.with-test = true; } "my-package" ./. { }).my-package 337 | ``` 338 | 339 |
340 | 341 | ### `buildOpamProject'` 342 | 343 | ``` 344 | { repos = ?[Repository] 345 | ; pkgs = ?Nixpkgs 346 | ; overlays = ?[Overlay] 347 | ; resolveArgs = ?ResolveArgs 348 | ; pinDepends = ?Bool 349 | ; recursive = ?Bool } 350 | → project: Path 351 | → Query 352 | → Scope 353 | ``` 354 | 355 | Similar to `buildOpamProject`, but adds all packages found in the 356 | project directory to the resulting `Scope`. 357 | 358 | #### Examples 359 | 360 | Build a package from a local directory: 361 | 362 |
363 | 364 | 365 | 366 | ```nix 367 | (buildOpamProject' { } ./. { }).my-package 368 | ``` 369 | 370 |
371 | 372 | ### `buildDuneProject` 373 | 374 | ``` 375 | { repos = ?[Repository] 376 | ; pkgs = ?Nixpkgs 377 | ; overlays = ?[Overlay] 378 | ; resolveArgs = ?ResolveArgs } 379 | → name: String 380 | → project: Path 381 | → Query 382 | → Scope 383 | ``` 384 | 385 | A convenience wrapper around `buildOpamProject`. Behaves exactly as 386 | `buildOpamProject`, except runs `dune build ${name}.opam` in an 387 | environment with `dune_3` and `ocaml` from nixpkgs beforehand. This is 388 | supposed to be used with dune's `generate_opam_files` 389 | 390 | #### Examples 391 | 392 | Build a local project which uses dune and doesn't have an opam file: 393 | 394 | 395 |
396 | 397 | 398 | 399 | ```nix 400 | (buildDuneProject { } "my-package" ./. { }).my-package 401 | ``` 402 | 403 |
404 | 405 | ### `readDirRecursive` / `filterOpamFiles` / `constructOpamRepo` 406 | 407 | `Dir : { ${node_name} = "regular" | "symlink" | Dir }` 408 | 409 | `readDirRecursive : Path → Dir` 410 | 411 | `filterOpamFiles: Dir → Dir` 412 | 413 | `constructOpamRepo : Path → Dir → Derivation` 414 | 415 | `readDirRecursive` is like `builtins.readDir` but instead of `"directory"` each 416 | subdirectory are the attrset representing it. 417 | 418 | `filterOpamFiles` takes the attrset produced by `readDir` or `readDirRecursive` 419 | and leaves only `opam` files in (files named `opam` or `*.opam`). 420 | 421 | `constructOpamRepo` takes the attrset produced by `filterOpamFiles` and 422 | produces a directory conforming to the `opam-repository` format. The resulting 423 | derivation will also provide `passthru.sourceMap`, which is a map from package 424 | names to package sources taken from the original `Path`. 425 | 426 | Packages for which the version can not be inferred get `dev` as their version. 427 | 428 | Note that all `opam` files in this directory will be evaluated using 429 | `importOpam`, to get their corresponding package names and versions. 430 | 431 | ### `makeOpamRepo` / `makeOpamRepoRec` 432 | 433 | `Path → Derivation` 434 | 435 | Construct a directory conforming to the `opam-repository` format from a 436 | directory, either recursively (`makeOpamRepoRec`) or only from top-level and the 437 | `opam/` subdirectory (`makeOpamRepo`). 438 | 439 | Also see all notes for `constructOpamRepo`. 440 | 441 | #### Examples 442 | 443 | Build a package from a local directory, which depends on packages from opam-repository: 444 | 445 |
446 | 447 | 448 | 449 | ```nix 450 | let 451 | repos = [ (makeOpamRepo ./.) opamRepository ]; 452 | scope = queryToScope { inherit repos; } { my-package = "*"; }; 453 | in scope.my-package 454 | ``` 455 | 456 |
457 | 458 | ### `listRepo` 459 | 460 | `Repository → {${package_name} = [version : String]}` 461 | 462 | Produce a mapping from package names to lists of versions (sorted 463 | older-to-newer) for an opam repository. 464 | 465 | ### `opamImport` 466 | 467 | ``` 468 | { repos = ?[Repository] 469 | ; pkgs = ?Nixpkgs 470 | ; overlays = ?[Overlay] } 471 | → Path 472 | → Scope 473 | ``` 474 | 475 | Import an opam switch, similarly to `opam import`, and provide a 476 | package combining all the packages installed in that switch. `repos`, 477 | `pkgs`, `overlays` and `Scope` are understood identically to 478 | `queryToScope`, except no version resolution is performed. 479 | 480 | ### `opam2nix` 481 | 482 | ``` 483 | { src = Path 484 | ; opamFile = ?Path 485 | ; name = ?String 486 | ; version = ?String 487 | ; resolveEnv = ?ResolveEnv } 488 | → Dependencies 489 | → Package 490 | ``` 491 | 492 | Produce a callPackage-able `Package` from an opam file. This should be 493 | called using `callPackage` from a `Scope`. Note that you are 494 | responsible to ensure that the package versions in `Scope` are 495 | consistent with package versions required by the package. May be 496 | useful in conjunction with `opamImport`. 497 | 498 | #### Examples 499 | 500 |
501 | 502 | 503 | 504 | ```nix 505 | let 506 | scope = opamImport { } ./opam.export; 507 | pkg = opam2nix { src = ./.; name = "my-package"; }; 508 | in scope.callPackage pkg {} 509 | ``` 510 | 511 |
512 | 513 | ### `defaultOverlay`, `staticOverlay` 514 | 515 | `Overlay : Scope → Scope → Scope` 516 | 517 | Overlays for the `Scope`'s. Contain enough to build the 518 | examples. Apply with `overrideScope`. 519 | 520 | ### Materialization 521 | 522 | Materialization is a way to speed up builds for your users and avoid 523 | IFD (import from derivation) at the cost of committing a generated 524 | file to your repository. It can be thought of as splitting the 525 | `queryToScope` (or `buildOpamProject`) in two parts: 526 | 527 | 1. Resolving package versions and reading package definitions (`queryToDefs`); 528 | 2. Building the package definitions (`defsToScope`). 529 | 530 | Notably, (1) requires IFD and can take a while, especially for new 531 | users who don't have the required eval-time dependencies on their 532 | machines. The idea is to save the result of (1) to a file, and then 533 | read that file and pass the contents to (2). 534 | 535 | ``` 536 | materialize : 537 | { repos = ?[Repository] 538 | ; resolveArgs = ?ResolveArgs 539 | ; regenCommand = ?String} 540 | → Query 541 | → Path 542 | ``` 543 | 544 | ``` 545 | materializeOpamProject : 546 | { repos = ?[Repository] 547 | ; resolveArgs = ?ResolveArgs 548 | ; pinDepends = ?Boolean 549 | ; regenCommand = ?[String]} 550 | → name : String 551 | → project : Path 552 | → Query 553 | → Path 554 | ``` 555 | 556 | ``` 557 | materializeOpamProject' : 558 | { repos = ?[Repository] 559 | ; resolveArgs = ?ResolveArgs 560 | ; pinDepends = ?Boolean 561 | ; regenCommand = ?[String]} 562 | → project : Path 563 | → Query 564 | → Path 565 | ``` 566 | 567 | ``` 568 | materializedDefsToScope : 569 | { pkgs = ?Nixpkgs 570 | ; overlays = ?[Overlay] } 571 | → Path 572 | → Scope 573 | ``` 574 | 575 | `materialize` resolves a query in much the same way as `queryToScope` 576 | would, but instead of producing a scope it produces a JSON file 577 | containing all the package definitions for the packages required by 578 | the query. 579 | 580 | `materializeOpamProject` is a wrapper around `materialize`. It is 581 | similar to `buildOpamProject` (which is a wrapper around 582 | `queryToScope`), but again instead of producing a scope it produces a 583 | JSON file with all the package definitions. It also handles 584 | `pin-depends` unless it is passed `pinDepends = false`, just like 585 | `buildOpamProject`. 586 | 587 | `materializeOpamProject'` is similar to `materializeOpamProject` but 588 | adds all packages found in the project directory. Like 589 | `buildOpamProject` compared to `buildOpamProject'`. 590 | 591 | Both `materialize` and `materializeOpamProject` take a `regenCommand` 592 | argument, which will be added to their output as `__opam_nix_regen` 593 | attribute. This is the command that should be executed to regenerate 594 | the definition file. 595 | 596 | `materializedDefsToScope` takes a JSON file with package definitions as 597 | produced by `materialize` and turns it into a scope. It is quick, does 598 | not use IFD or have any dependency on `opam` or `opam2json`. Note that 599 | `opam2json` is still required for actually building the package (it 600 | parses the `.config` file). 601 | 602 | There also are convenience scripts called `opam-nix-gen` and 603 | `opam-nix-regen`. It is available as `packages` on this repo, 604 | e.g. `nix shell github:tweag/opam-nix#opam-nix-gen` should get you 605 | `opam-nix-gen` in scope. Internally: 606 | - `opam-nix-gen` calls `materialize` or `materializeOpamProject`. You 607 | can use it to generate the `package-defs.json`, and then pass that 608 | file to `materializedDefsToScope` in your `flake.nix` 609 | - `opam-nix-regen` reads `__opam_nix_regen` from the 610 | `package-defs.json` file you supply to it, and runs the command it 611 | finds there. It can be used to regenerate the `package-defs.json` 612 | file. 613 | 614 | #### Examples 615 | 616 | First, create a `package-defs.json`: 617 | 618 | ```sh 619 | opam-nix-gen my-package . package-defs.json 620 | ``` 621 | 622 | Alternatively, if you wish to specify your own `opam-repository` or other 623 | arguments to `materializeOpamProject`, use it directly: 624 | 625 | ```nix 626 | # ... 627 | package-defs = materializeOpamProject { } "my-package" ./. { }; 628 | # ... 629 | ``` 630 | 631 | And then evaluate the resulting file: 632 | 633 | ```sh 634 | cat $(nix eval --raw .#package-defs) > package-defs.json 635 | ``` 636 | 637 | Then, import it: 638 | 639 |
640 | 641 | 642 | 643 | 644 | ```nix 645 | (materializedDefsToScope { sourceMap.my-package = ./.; } ./package-defs.json).my-package 646 | ``` 647 | 648 |
649 | 650 | ### `fromOpam` / `importOpam` 651 | 652 | `fromOpam : String → {...}` 653 | 654 | `importOpam : Path → {...}` 655 | 656 | Generate a nix attribute set from the opam file. This is just a Nix 657 | representation of the JSON produced by `opam2json`. 658 | 659 | ### Monorepo functions 660 | 661 | #### `queryToMonorepo` 662 | 663 | ``` 664 | { repos = ?[Repository] 665 | ; resolveArgs = ?ResolveArgs 666 | ; filterPkgs ?[ ] } 667 | → Query 668 | → Scope 669 | ``` 670 | 671 | Similar to `queryToScope`, but creates a attribute set (instead of a 672 | scope) with package names mapping to sources for replicating the 673 | [`opam monorepo`](https://github.com/tarides/opam-monorepo) workflow. 674 | 675 | The `filterPkgs` argument gives a list of package names to filter from 676 | the resulting attribute set, rather than removing them based on their 677 | opam `dev-repo` name. 678 | 679 | #### `buildOpamMonorepo` 680 | 681 | ``` 682 | { repos = ?[Repository] 683 | ; resolveArgs = ?ResolveArgs 684 | ; pinDepends = ?Bool 685 | ; recursive = ?Bool 686 | ; extraFilterPkgs ?[ ] } 687 | → project: Path 688 | → Query 689 | → Sources 690 | ``` 691 | 692 | A convenience wrapper around `queryToMonorepo`. 693 | 694 | Creates a monorepo for an opam project (found in the directory passed 695 | as the second argument). The monorepo consists of an attribute set of 696 | opam package `dev-repo`s to sources, for all dependancies of the 697 | packages found in the project directory as well as other packages from 698 | the `Query`. 699 | 700 | The packages in the project directory are excluded from 701 | the resulting monorepo along with `ocaml-system`, `opam-monorepo`, and 702 | packages in the `extraFilterPkgs` argument. 703 | 704 | ### Lower-level functions 705 | 706 | `joinRepos : [Repository] → Repository` 707 | 708 | `opamList : Repository → ResolveArgs → Query → [String]` 709 | 710 | `opamListToQuery : [String] → Query` 711 | 712 | `queryToDefs : [Repository] → Query → Defs` 713 | 714 | `defsToScope : Nixpkgs → ResolveEnv → Defs → Scope` 715 | 716 | `applyOverlays : [Overlay] → Scope → Scope` 717 | 718 | `getPinDepends : Pkgdef → [Repository]` 719 | 720 | `filterOpamRepo : Query → Repository → Repository` 721 | 722 | `defsToSrcs : Defs → Sources` 723 | 724 | `deduplicateSrcs : Sources → Sources` 725 | 726 | `mkMonorepo : Sources → Scope` 727 | 728 | `opamList` resolves package versions using the repo (first argument) and 729 | ResolveArgs (second argument). Note that it accepts only one repo. If you want 730 | to pass multiple repositories, merge them together yourself with `joinRepos`. 731 | The result of `opamList` is a list of strings, each containing a package name 732 | and a package version. Use `opamListToQuery` to turn this list into a "`Query`" 733 | (but with all the versions specified). 734 | 735 | `queryToDefs` takes a query (with all the version specified, 736 | e.g. produced by `opamListToQuery` or by reading the `installed` 737 | section of `opam.export` file) and produces an attribute set of 738 | package definitions (using `importOpam`). 739 | 740 | `defsToScope` takes a nixpkgs instantiataion, a resolve environment and an 741 | attribute set of definitions (as produced by `queryToDefs`) and produces a 742 | `Scope`. 743 | 744 | `applyOverlays` applies a list of overlays to a scope. 745 | 746 | `getPinDepends` Takes a package definition and produces the list of 747 | repositories corresponding to `pin-depends` of the 748 | packagedefs. Requires `--impure` (to fetch the repos specified in 749 | `pin-depends`). Each repository includes only one package. 750 | 751 | `filterOpamRepo` filters the repository to only include packages (and 752 | their particular versions) present in the supplied Query. 753 | 754 | `defsToSrcs` takes an attribute set of definitions (as produced by 755 | `queryToDefs`) and produces a list `Sources` 756 | ( `[ { name; version; src; } ... ]`). 757 | 758 | `deduplicateSrcs` deduplicates `Sources` produced by `defsToSrcs`, as 759 | some packages may share sources if they are developed in the same repo. 760 | 761 | `mkMonorepo` takes `Sources` and creates an attribute set mapping 762 | package names to sources with a derivation that fetches the source 763 | at the `src` URL. 764 | 765 | #### `Defs` (set of package definitions) 766 | 767 | The attribute set of package definitions has package names as 768 | attribute names, and package definitions as nix attrsets. These are 769 | basically the nix representation of 770 | [opam2json](https://github.com/tweag/opam2json/) output. The format of 771 | opam files is described here: 772 | https://opam.ocaml.org/doc/Manual.html#opam . 773 | 774 | ### Examples 775 | 776 | Build a local package, using an exported opam switch and some 777 | "vendored" dependencies, and applying some local overlay on top. 778 | 779 | ```nix 780 | let 781 | pkgs = import { }; 782 | 783 | repos = [ 784 | (opam-nix.makeOpamRepo ./.) # "Pin" vendored packages 785 | inputs.opam-repository 786 | ]; 787 | 788 | export = 789 | opam-nix.opamListToQuery (opam-nix.fromOPAM ./opam.export).installed; 790 | 791 | vendored-packages = { 792 | "my-vendored-package" = "local"; 793 | "my-other-vendored-package" = "v1.2.3"; 794 | "my-package" = "local"; # Note: you can't use "*" here! 795 | }; 796 | 797 | myOverlay = import ./overlay.nix; 798 | 799 | scope = applyOverlays [ defaultOverlay myOverlay ] 800 | (defsToScope pkgs defaultResolveEnv (queryToDefs repos (export // vendored-packages))); 801 | in scope.my-package 802 | ``` 803 | 804 | ### Auxiliary functions 805 | 806 | `splitNameVer : String → { name = String; version = String; }` 807 | 808 | `nameVerToValuePair : String → { name = String; value = String; }` 809 | 810 | Split opam's package definition (`name.version`) into 811 | components. `nameVerToValuePair` is useful together with 812 | `listToAttrs`. 813 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 Tweag 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `opam-nix` 2 | 3 | Turn opam-based OCaml projects into [Nix](https://nixos.org) derivations, 4 | automatically resolving both OCaml and system dependencies. 5 | 6 | `opam-nix` can build packages 7 | 8 | - from [`opam`](./DOCUMENTATION.md#buildOpamProject) and [`dune-project`](./DOCUMENTATION.md#buildDuneProject) files, 9 | - for Linux and macOS, 10 | - on x86_64 and aarch64 (including M1 macs), 11 | - using a compiler from [nixpkgs](https://github.com/nixos/nixpkgs) or from [opam-repository](https://github.com/ocaml/opam-repository), 12 | - with either dynamic or static linking. 13 | 14 | It also comes with the power of Nix, allowing you to effortlessly manage 15 | multiple projects with different dependency versions, override dependencies, 16 | cache builds, and more. 17 | 18 | ## Quick start 19 | 20 | For a quick introduction to `opam-nix` and a guide to get you started, read [this blog post on the Tweag blog](https://www.tweag.io/blog/2023-02-16-opam-nix/). 21 | 22 | ## Templates 23 | 24 | `opam-nix` comes with some templates that can help you package opam packages with Nix. 25 | 26 | > **Note** 27 | > 28 | > All of these templates assume that you already have an OCaml project packaged 29 | > with opam, and just want to package it with Nix. If you're starting from 30 | > scratch, you have to set up the opam files separately. The easiest way to do 31 | > this is by running `nix-shell -p dune_3 --run "dune init project ."` 32 | 33 | - A simple package build, no frills: `nix flake init -t github:tweag/opam-nix` 34 | - A more featured flake, building an executable and providing a shell in which you can conveniently work on it: `nix flake init -t github:tweag/opam-nix#executable` 35 | - Build multiple packages from the same workspace, and have a shell in which you can work on them: `nix flake init -t github:tweag/opam-nix#multi-package` 36 | 37 | > **Note** 38 | > 39 | > If you're using Git, you should `git add flake.nix` after initializing, as Nix operates on the git index contents. 40 | 41 | ## Examples 42 | 43 | There are also some examples which can give you some ideas of what is possible with `opam-nix`: 44 | 45 | - [Building a package from opam-repository](./examples/0install/flake.nix) 46 | - [Building a package from a custom github repository](./examples/opam2json/flake.nix) 47 | - [Building a package from a multi-package github repository with submodules](./examples/ocaml-lsp/flake.nix) 48 | - [Building a Coq package](./examples/coq/flake.nix) 49 | - [Building a static version of a package using compiler from nixpkgs](./examples/opam-ed/flake.nix) 50 | - [Building a static version of a package using the compiler from opam](./examples/opam2json-static/flake.nix) 51 | - [Building a GUI package](./examples/frama-c/flake.nix) 52 | - [Building the entirety of Tezos](./examples/tezos/flake.nix) 53 | 54 | All examples are checks and packages, so you can do e.g. `nix build 55 | github:tweag/opam-nix#opam-ed` to try them out individually, or `nix 56 | flake check github:tweag/opam-nix` to build them all. 57 | 58 | ## Building packages from opam-repository with Nix 59 | 60 | `opam-nix` provides all packages from `opam-repository` in its `legacyPackages` flake output. 61 | You can use this to quickly check out some package, or get a shell with particular version without messing with opam switches. 62 | 63 | For example: 64 | 65 | - `nix build 'github:tweag/opam-nix#utop."2.10.0"'` to build a specific version of utop (available in `./result`), 66 | - `nix shell github:tweag/opam-nix#camyll.latest` to get a shell with the latest version of [`camyll`](https://alan-j-hu.github.io/camyll/). 67 | 68 | ## Complete documentation 69 | 70 | Complete documentation for `opam-nix` is available in the [DOCUMENTATION.md](./DOCUMENTATION.md) file. 71 | 72 | ## Questions? Problems? 73 | 74 | You can reach out to us via the [Discussions page](https://github.com/tweag/opam-nix/discussions/categories/q-a) or directly to the maintainer [via email](mailto:alexander.bantyev@tweag.io). 75 | 76 | ## Related projects 77 | 78 | - [Nix](https://github.com/nixos/nix): A powerful package manager that makes package management reliable and reproducible; 79 | - [opam](https://github.com/ocaml/opam): the OCaml package manager; 80 | - [hillingar](https://github.com/ryanGibb/hillingar): Tool for building MirageOS unikernels with opam-nix. 81 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | (import ( 2 | let 3 | lock = builtins.fromJSON (builtins.readFile ./flake.lock); 4 | in 5 | fetchTarball { 6 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; 7 | sha256 = lock.nodes.flake-compat.locked.narHash; 8 | } 9 | ) { src = ./.; }).defaultNix 10 | -------------------------------------------------------------------------------- /examples/0install/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Build a package from opam-repository, using the non-system compiler"; 3 | inputs.opam-nix.url = "github:tweag/opam-nix"; 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | outputs = 6 | { 7 | self, 8 | opam-nix, 9 | flake-utils, 10 | }: 11 | flake-utils.lib.eachDefaultSystem (system: { 12 | legacyPackages = 13 | let 14 | inherit (opam-nix.lib.${system}) queryToScope; 15 | scope = queryToScope { } { 16 | "0install" = "*"; 17 | # The following line forces opam to choose the compiler from opam instead of the nixpkgs one 18 | ocaml-base-compiler = "*"; 19 | }; 20 | in 21 | scope.overrideScope' ( 22 | final: prev: { 23 | "0install" = prev."0install".overrideAttrs (_: { 24 | preInstall = "cp _build/default/0install.install ."; 25 | doNixSupport = false; 26 | removeOcamlReferences = true; 27 | }); 28 | } 29 | ); 30 | 31 | defaultPackage = self.legacyPackages.${system}."0install"; 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /examples/coq/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Build a coq package"; 3 | inputs.opam-nix.url = "github:tweag/opam-nix"; 4 | inputs.opam-repository.url = "github:ocaml/opam-repository"; 5 | inputs.opam-repository.flake = false; 6 | inputs.nixpkgs.url = "github:nixos/nixpkgs"; 7 | inputs.flake-utils.url = "github:numtide/flake-utils"; 8 | outputs = 9 | { 10 | self, 11 | opam-nix, 12 | flake-utils, 13 | opam-repository, 14 | nixpkgs, 15 | }: 16 | flake-utils.lib.eachDefaultSystem (system: { 17 | legacyPackages = 18 | let 19 | pkgs = nixpkgs.legacyPackages.${system}; 20 | opam-coq-archive = pkgs.fetchFromGitHub { 21 | owner = "coq"; 22 | repo = "opam-coq-archive"; 23 | rev = "e73f7a6f58f02a6798f31d6e77caad21e8127789"; 24 | sha256 = "nfrHmjF84K4/Js96U7MNNaqW8TfkEmrOePH3SNPlMiw="; 25 | }; 26 | inherit (opam-nix.lib.${system}) queryToScope; 27 | scope = 28 | queryToScope 29 | { 30 | repos = [ 31 | "${opam-repository}" 32 | "${opam-coq-archive}/extra-dev" 33 | ]; 34 | } 35 | { 36 | coq = "*"; 37 | coq-inf-seq-ext = "*"; 38 | ocaml-base-compiler = "*"; 39 | }; 40 | in 41 | scope; 42 | 43 | defaultPackage = self.legacyPackages.${system}."coq-inf-seq-ext"; 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /examples/docfile/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | stdenv, 3 | lib, 4 | pandoc, 5 | htmlq, 6 | opam-nix, 7 | pkgs, 8 | docfile ? ../../DOCUMENTATION.md, 9 | }: 10 | let 11 | examples = stdenv.mkDerivation { 12 | name = "docfile-check"; 13 | src = ./.; 14 | nativeBuildInputs = [ 15 | pandoc 16 | htmlq 17 | ]; 18 | phases = [ 19 | "unpackPhase" 20 | "buildPhase" 21 | ]; 22 | buildPhase = '' 23 | mkdir -p $out 24 | html="$(pandoc -i ${docfile})" 25 | for id in $(htmlq '.example' -a id <<< $html); do 26 | dir=$(htmlq ".example#$id" -a dir <<< $html) 27 | cp -R $dir $out/$id 28 | htmlq ".example#$id" -t <<< $html >> $out/$id/default.nix 29 | done 30 | ''; 31 | }; 32 | in 33 | { 34 | checks = lib.mapAttrs' ( 35 | name: value: 36 | lib.nameValuePair "docfile-${name}" (builtins.scopedImport (pkgs // opam-nix) "${examples}/${name}") 37 | ) (builtins.readDir examples); 38 | } 39 | -------------------------------------------------------------------------------- /examples/docfile/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweag/opam-nix/dfb0328bf6326a100387b8864845ab7658214d3e/examples/docfile/empty/.gitkeep -------------------------------------------------------------------------------- /examples/docfile/my-package-dune/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweag/opam-nix/dfb0328bf6326a100387b8864845ab7658214d3e/examples/docfile/my-package-dune/.gitkeep -------------------------------------------------------------------------------- /examples/docfile/my-package-dune/bin/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name hello) 3 | (public_name hello)) -------------------------------------------------------------------------------- /examples/docfile/my-package-dune/bin/hello.ml: -------------------------------------------------------------------------------- 1 | let () = 2 | print_endline "Hello, World!" 3 | -------------------------------------------------------------------------------- /examples/docfile/my-package-dune/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.9) 2 | (name my-package) 3 | 4 | (generate_opam_files true) 5 | 6 | (package 7 | (name my-package) 8 | (synopsis "Friendly greetings") 9 | (depends ("ocaml" (= "4.14.1")))) -------------------------------------------------------------------------------- /examples/docfile/my-package-dune/hello.ml: -------------------------------------------------------------------------------- 1 | open Cmdliner 2 | 3 | let greet () = print_endline "Hello, world!" 4 | 5 | let greet_t = Term.(const greet $ const ()) 6 | 7 | let () = Term.exit @@ Term.eval (greet_t, Term.info "greet") 8 | -------------------------------------------------------------------------------- /examples/docfile/my-package/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweag/opam-nix/dfb0328bf6326a100387b8864845ab7658214d3e/examples/docfile/my-package/.gitkeep -------------------------------------------------------------------------------- /examples/docfile/my-package/hello.ml: -------------------------------------------------------------------------------- 1 | open Cmdliner 2 | 3 | let greet () = print_endline "Hello, world!" 4 | 5 | let greet_t = Term.(const greet $ const ()) 6 | 7 | let () = Term.exit @@ Term.eval (greet_t, Term.info "greet") 8 | -------------------------------------------------------------------------------- /examples/docfile/my-package/my-package.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | name: "my-package" 3 | version: "0.1" 4 | depends: [ 5 | "ocaml" {= "4.14.1"} 6 | "ocamlfind" 7 | "cmdliner" 8 | ] 9 | build: [ 10 | [ "mkdir" "-p" "%{bin}%" ] 11 | [ "ocamlfind" "ocamlopt" "-package" "cmdliner" "-linkpkg" "hello.ml" "-o" "%{bin}%/hello" ] 12 | [ "./test.sh" "%{bin}%/hello" ] { with-test } 13 | ] 14 | pin-depends: [ 15 | ["cmdliner.dev" "git://git@github.com:dbuenzli/cmdliner#ed6d829156d1a86f56973f87e8b151802029c11e"] 16 | ] 17 | -------------------------------------------------------------------------------- /examples/docfile/my-package/package-defs.json: -------------------------------------------------------------------------------- 1 | {"__opam_nix_env":{},"__opam_nix_regen":["opam-nix-gen","-p","ocaml-base-compiler","my-package",".","package-defs.json"],"base-bigarray":{"description":"\nBigarray library distributed with the OCaml compiler\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml/opam-repository/issues","name":"base-bigarray","opam-version":"2.0","version":"base"},"base-threads":{"description":"\nThreads library distributed with the OCaml compiler\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml/opam-repository/issues","name":"base-threads","opam-version":"2.0","version":"base"},"base-unix":{"description":"\nUnix library distributed with the OCaml compiler\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml/opam-repository/issues","name":"base-unix","opam-version":"2.0","version":"base"},"cmdliner":{"authors":["The cmdliner programmers"],"bug-reports":"https://github.com/dbuenzli/cmdliner/issues","build":[[{"id":"make"},"all","PREFIX=%{prefix}%"]],"depends":[{"conditions":[{"arg":"4.03.0","prefix_relop":"geq"}],"val":"ocaml"}],"description":"\nCmdliner allows the declarative definition of command line interfaces\nfor OCaml.\n\nIt provides a simple and compositional mechanism to convert command\nline arguments to OCaml values and pass them to your functions. The\nmodule automatically handles syntax errors, help messages and UNIX man\npage generation. It supports programs with single or multiple commands\nand respects most of the [POSIX][1] and [GNU][2] conventions.\n\nCmdliner has no dependencies and is distributed under the ISC license.\n\n[1]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html\n[2]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html\n\nHome page: http://erratique.ch/software/cmdliner","dev-repo":"git+https://erratique.ch/repos/cmdliner.git","doc":"https://erratique.ch/software/cmdliner/doc","files-contents":{},"homepage":"https://erratique.ch/software/cmdliner","install":[[{"id":"make"},"install","LIBDIR=%{_:lib}%","DOCDIR=%{_:doc}%"],[{"id":"make"},"install-doc","LIBDIR=%{_:lib}%","DOCDIR=%{_:doc}%"]],"isLocal":true,"license":["ISC"],"maintainer":["Daniel Bünzli "],"name":"cmdliner","opam-version":"2.0","src":{"hash":"sha256-+ExYMpU7tZyIbhoww3gAme7VrsMYgBWg4VtdxfgbMe0=","rev":"ed6d829156d1a86f56973f87e8b151802029c11e","subdir":"/","url":"https://github.com/dbuenzli/cmdliner"},"synopsis":"Declarative definition of command line interfaces for OCaml","tags":["cli","system","declarative","org:erratique"],"version":"dev"},"my-package":{"build":[["mkdir","-p","%{bin}%"],["ocamlfind","ocamlopt","-package","cmdliner","-linkpkg","hello.ml","-o","%{bin}%/hello"],{"conditions":[{"id":"with-test"}],"val":["./test.sh","%{bin}%/hello"]}],"depends":[{"conditions":[{"arg":"4.14.1","prefix_relop":"eq"}],"val":"ocaml"},"ocamlfind","cmdliner"],"files-contents":{},"isLocal":true,"name":"my-package","opam-version":"2.0","pin-depends":[["cmdliner.dev","git+https://github.com/dbuenzli/cmdliner#ed6d829156d1a86f56973f87e8b151802029c11e"]],"version":"0.1"},"ocaml":{"authors":["Xavier Leroy","Damien Doligez","Alain Frisch","Jacques Garrigue","Didier Rémy","Jérôme Vouillon"],"bug-reports":"https://github.com/ocaml/opam-repository/issues","build":["ocaml","%{ocaml-config:share}%/gen_ocaml_config.ml",{"id":"_:version"},{"id":"_:name"}],"build-env":{"lhs":{"id":"CAML_LD_LIBRARY_PATH"},"relop":"eq","rhs":""},"depends":[{"conditions":[{"arg":"2","prefix_relop":"geq"}],"val":"ocaml-config"},{"lhs":{"lhs":{"conditions":[{"lhs":{"arg":"4.14.1~","prefix_relop":"geq"},"logop":"and","rhs":{"arg":"4.14.2~","prefix_relop":"lt"}}],"val":"ocaml-base-compiler"},"logop":"or","rhs":{"conditions":[{"lhs":{"arg":"4.14.1~","prefix_relop":"geq"},"logop":"and","rhs":{"arg":"4.14.2~","prefix_relop":"lt"}}],"val":"ocaml-variants"}},"logop":"or","rhs":{"conditions":[{"lhs":{"arg":"4.14.1","prefix_relop":"geq"},"logop":"and","rhs":{"arg":"4.14.2~","prefix_relop":"lt"}}],"val":"ocaml-system"}}],"description":"\nThis package requires a matching implementation of OCaml,\nand polls it to initialise specific variables like `ocaml:native-dynlink`","files-contents":{},"flags":{"id":"conf"},"homepage":"https://ocaml.org","isLocal":false,"license":"LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception","maintainer":"platform@lists.ocaml.org","name":"ocaml","opam-version":"2.0","setenv":[[{"lhs":{"id":"CAML_LD_LIBRARY_PATH"},"relop":"eq","rhs":"%{_:stubsdir}%"}],[{"env_update":"prepend","lhs":{"id":"CAML_LD_LIBRARY_PATH"},"rhs":"%{lib}%/stublibs"}],[{"lhs":{"id":"OCAML_TOPLEVEL_PATH"},"relop":"eq","rhs":"%{toplevel}%"}]],"synopsis":"The OCaml compiler (virtual package)","version":"4.14.1"},"ocaml-base-compiler":{"authors":"Xavier Leroy and many contributors","bug-reports":"https://github.com/ocaml/opam-repository/issues","build":[["./configure","--prefix=%{prefix}%","--docdir=%{doc}%/ocaml","-C",{"conditions":[{"lhs":{"lhs":{"id":"os"},"relop":"eq","rhs":"openbsd"},"logop":"or","rhs":{"lhs":{"id":"os"},"relop":"eq","rhs":"macos"}}],"val":"CC=cc"},{"conditions":[{"lhs":{"lhs":{"id":"os"},"relop":"eq","rhs":"openbsd"},"logop":"or","rhs":{"lhs":{"id":"os"},"relop":"eq","rhs":"macos"}}],"val":"ASPP=cc -c"}],[{"id":"make"},"-j%{jobs}%"]],"conflict-class":"ocaml-core-compiler","depends":[{"conditions":[{"lhs":{"arg":"4.14.1","prefix_relop":"eq"},"logop":"and","rhs":{"id":"post"}}],"val":"ocaml"},{"conditions":[{"id":"post"}],"val":"base-unix"},{"conditions":[{"id":"post"}],"val":"base-bigarray"},{"conditions":[{"id":"post"}],"val":"base-threads"},{"conditions":[{"id":"post"}],"val":"ocaml-options-vanilla"},{"conditions":[{"lhs":{"id":"opam-version"},"relop":"lt","rhs":"2.1.0"}],"val":"ocaml-beta"}],"dev-repo":"git+https://github.com/ocaml/ocaml#4.14","extra-files":["ocaml-base-compiler.install","md5=3e969b841df1f51ca448e6e6295cb451"],"files-contents":{"ocaml-base-compiler.install":"share_root: [\"config.cache\" {\"ocaml/config.cache\"}]\n"},"flags":[{"id":"compiler"},{"id":"avoid-version"}],"homepage":"https://ocaml.org","install":[{"id":"make"},"install"],"isLocal":false,"license":"LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception","maintainer":"platform@lists.ocaml.org","name":"ocaml-base-compiler","opam-version":"2.0","post-messages":[{"conditions":[{"lhs":{"id":"failure"},"logop":"and","rhs":{"lhs":{"id":"jobs"},"relop":"gt","rhs":1}}],"val":"A failure in the middle of the build may be caused by build parallelism\n (enabled by default).\n Please file a bug report at https://github.com/ocaml/opam-repository/issues"},{"conditions":[{"lhs":{"lhs":{"id":"failure"},"logop":"and","rhs":{"lhs":{"id":"jobs"},"relop":"gt","rhs":1}},"logop":"and","rhs":{"lhs":{"id":"opam-version"},"relop":"geq","rhs":"2.0.5"}}],"val":"You can try installing again including --jobs=1\n to force a sequential build instead."}],"setenv":{"lhs":{"id":"CAML_LD_LIBRARY_PATH"},"relop":"eq","rhs":"%{lib}%/stublibs"},"synopsis":"First release candidate of OCaml 4.14.1","url":{"section":{"checksum":"sha256=c0dc72b35d510524a80d8da82e0054b82d35bea339911136671201ce9943ebf3","src":"https://github.com/ocaml/ocaml/archive/4.14.1-rc1.tar.gz"}},"version":"4.14.1~rc1"},"ocaml-config":{"authors":["Louis Gesbert ","David Allsopp "],"bug-reports":"https://github.com/ocaml/opam/issues","depends":[{"lhs":{"lhs":{"conditions":[{"arg":"4.12.0~","prefix_relop":"geq"}],"val":"ocaml-base-compiler"},"logop":"or","rhs":{"conditions":[{"arg":"4.12.0~","prefix_relop":"geq"}],"val":"ocaml-variants"}},"logop":"or","rhs":{"conditions":[{"arg":"4.12.0~","prefix_relop":"geq"}],"val":"ocaml-system"}}],"description":"\nThis package is used by the OCaml package to set-up its variables.","extra-files":[["gen_ocaml_config.ml.in","md5=a4b41e3236593d8271295b84b0969172"],["ocaml-config.install","md5=8e50c5e2517d3463b3aad649748cafd7"]],"files-contents":{"gen_ocaml_config.ml.in":"let () =\n let ocaml_version =\n let v = Sys.ocaml_version in\n let l = String.length v in\n let plus = try String.index v '+' with Not_found -> l in\n (* Introduced in 4.11.0; used from 4.12.0 *)\n let tilde = try String.index v '~' with Not_found -> l in\n String.sub v 0 (min (min plus tilde) l)\n in\n if ocaml_version <> Sys.argv.(1) then\n (Printf.eprintf\n \"OCaml version mismatch: %%s, expected %s\"\n ocaml_version Sys.argv.(1);\n exit 1)\n else\n let oc = open_out (Sys.argv.(2) ^ \".config\") in\n let exe = \".exe\" in\n let (ocaml, suffix) =\n let s = Sys.executable_name in\n if Filename.check_suffix s exe then\n (Filename.chop_suffix s exe, exe)\n else\n (s, \"\")\n in\n let ocamlc = ocaml^\"c\"^suffix in\n let libdir =\n if Sys.command (ocamlc^\" -where > where\") = 0 then\n (* Must be opened in text mode for Windows *)\n let ic = open_in \"where\" in\n let r = input_line ic in\n close_in ic; r\n else\n failwith \"Bad return from 'ocamlc -where'\"\n in\n let stubsdir =\n let ic = open_in (Filename.concat libdir \"ld.conf\") in\n let rec r acc = try r (input_line ic::acc) with End_of_file -> acc in\n let lines = List.rev (r []) in\n close_in ic;\n String.concat \":\" lines\n in\n let p fmt = Printf.fprintf oc (fmt ^^ \"\\n\") in\n p \"opam-version: \\\"2.0\\\"\";\n p \"variables {\";\n p \" native: %%b\"\n (Sys.file_exists (ocaml^\"opt\"^suffix));\n p \" native-tools: %%b\"\n (Sys.file_exists (ocamlc^\".opt\"^suffix));\n p \" native-dynlink: %%b\"\n (Sys.file_exists (Filename.concat libdir \"dynlink.cmxa\"));\n p \" stubsdir: %%S\"\n stubsdir;\n p \" preinstalled: %{ocaml-system:installed}%\";\n p \" compiler: \\\"%{ocaml-system:installed?system:}%%{ocaml-base-compiler:version}%%{ocaml-variants:version}%%{ocaml-option-32bit:installed?+32bit:}%%{ocaml-option-afl:installed?+afl:}%%{ocaml-option-bytecode-only:installed?+bytecode-only:}%%{ocaml-option-default-unsafe-string:installed?+default-unsafe-string:}%%{ocaml-option-fp:installed?+fp:}%%{ocaml-option-flambda:installed?+flambda:}%%{ocaml-option-musl:installed?+musl:}%%{ocaml-option-nnp:installed?+nnp:}%%{ocaml-option-no-flat-float-array:installed?+no-flat-float-array:}%%{ocaml-option-spacetime:installed?+spacetime:}%%{ocaml-option-static:installed?+static:}%\\\"\";\n p \"}\";\n close_out oc\n","ocaml-config.install":"share: [\"gen_ocaml_config.ml\"]\n"},"homepage":"https://opam.ocaml.org/","isLocal":false,"maintainer":"platform@lists.ocaml.org","name":"ocaml-config","opam-version":"2.0","substs":"gen_ocaml_config.ml","synopsis":"OCaml Switch Configuration","version":"2"},"ocaml-options-vanilla":{"conflicts":["ocaml-option-32bit","ocaml-option-afl",{"conditions":[{"lhs":{"lhs":{"lhs":{"lhs":{"lhs":{"id":"ocaml-system:version"},"relop":"lt","rhs":"5"},"logop":"or","rhs":{"lhs":{"id":"ocaml-base-compiler:version"},"relop":"lt","rhs":"5"}},"logop":"or","rhs":{"lhs":{"id":"ocaml-variants:version"},"relop":"lt","rhs":"5"}},"logop":"or","rhs":{"lhs":{"id":"arch"},"relop":"eq","rhs":"arm64"}},"logop":"or","rhs":{"lhs":{"id":"arch"},"relop":"eq","rhs":"x86_64"}}],"val":"ocaml-option-bytecode-only"},"ocaml-option-default-unsafe-string","ocaml-option-flambda","ocaml-option-fp","ocaml-option-musl","ocaml-option-no-flat-float-array","ocaml-option-spacetime","ocaml-option-static","ocaml-option-nnp","ocaml-option-nnpchecker","ocaml-option-address-sanitizer","ocaml-option-leak-sanitizer"],"depends":[{"lhs":{"lhs":{"conditions":[{"id":"post"}],"val":"ocaml-base-compiler"},"logop":"or","rhs":{"conditions":[{"id":"post"}],"val":"ocaml-system"}},"logop":"or","rhs":{"conditions":[{"lhs":{"id":"post"},"logop":"and","rhs":{"arg":"4.12.0~","prefix_relop":"geq"}}],"val":"ocaml-variants"}}],"files-contents":{},"flags":{"id":"compiler"},"isLocal":false,"maintainer":"platform@lists.ocaml.org","name":"ocaml-options-vanilla","opam-version":"2.0","synopsis":"Ensure that OCaml is compiled with no special options enabled","version":"1"},"ocamlfind":{"authors":"Gerd Stolpmann ","available":{"lhs":{"id":"os"},"relop":"neq","rhs":"win32"},"bug-reports":"https://github.com/ocaml/ocamlfind/issues","build":[["./configure","-bindir",{"id":"bin"},"-sitelib",{"id":"lib"},"-mandir",{"id":"man"},"-config","%{lib}%/findlib.conf","-no-custom",{"conditions":[{"lhs":{"arg":{"id":"ocaml:preinstalled"},"pfxop":"not"},"logop":"and","rhs":{"lhs":{"id":"ocaml:version"},"relop":"geq","rhs":"4.02.0"}}],"val":"-no-camlp4"},{"conditions":[{"id":"ocaml:preinstalled"}],"val":"-no-topfind"}],[{"id":"make"},"all"],{"conditions":[{"id":"ocaml:native"}],"val":[{"id":"make"},"opt"]}],"depends":[{"conditions":[{"arg":"3.08.0","prefix_relop":"geq"}],"val":"ocaml"}],"depopts":["graphics"],"description":"\nFindlib is a library manager for OCaml. It provides a convention how\nto store libraries, and a file format (\"META\") to describe the\nproperties of libraries. There is also a tool (ocamlfind) for\ninterpreting the META files, so that it is very easy to use libraries\nin programs and scripts.\n","dev-repo":"git+https://github.com/ocaml/ocamlfind.git","extra-files":["0001-Harden-test-for-OCaml-5.patch","md5=3cddbf72164c29d4e50e077a92a37c6c"],"files-contents":{"0001-Harden-test-for-OCaml-5.patch":"diff a/configure b/configure\n--- a/configure\n+++ b/configure\n@@ -294,7 +294,7 @@\n # If findlib has been configured -sitelib $(ocamlc -where) then there's\n # nothing to do, but otherwise we need to put OCaml's Standard Library\n # into the path setting.\n- if [ ! -e \"${ocaml_sitelib}/stdlib/META\" ]; then\n+ if [ ! -e \"${ocaml_sitelib}/stdlib.cmi\" ]; then\n ocamlpath=\"${ocaml_core_stdlib}${path_sep}${ocamlpath}\"\n fi\n fi\n"},"homepage":"http://projects.camlcity.org/projects/findlib.html","install":[[{"id":"make"},"install"],{"conditions":[{"id":"ocaml:preinstalled"}],"val":["install","-m","0755","ocaml-stub","%{bin}%/ocaml"]}],"isLocal":false,"license":"MIT","maintainer":"Thomas Gazagnaire ","name":"ocamlfind","opam-version":"2.0","patches":["0001-Harden-test-for-OCaml-5.patch"],"synopsis":"A library manager for OCaml","url":{"section":{"checksum":["md5=96c6ee50a32cca9ca277321262dbec57","sha512=cfaf1872d6ccda548f07d32cc6b90c3aafe136d2aa6539e03143702171ee0199add55269bba894c77115535dc46a5835901a5d7c75768999e72db503bfd83027"],"src":"http://download.camlcity.org/download/findlib-1.9.6.tar.gz"}},"version":"1.9.6"}} -------------------------------------------------------------------------------- /examples/docfile/my-package/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | "$1" | grep -q "Hello, world" 6 | -------------------------------------------------------------------------------- /examples/docfile/opam-import/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tweag/opam-nix/dfb0328bf6326a100387b8864845ab7658214d3e/examples/docfile/opam-import/.gitkeep -------------------------------------------------------------------------------- /examples/docfile/opam-import/hello.ml: -------------------------------------------------------------------------------- 1 | open Cmdliner 2 | 3 | let greet () = print_endline "Hello, world!" 4 | 5 | let greet_t = Term.(const greet $ const ()) 6 | 7 | let () = Term.exit @@ Term.eval (greet_t, Term.info "greet") 8 | -------------------------------------------------------------------------------- /examples/docfile/opam-import/my-package.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | name: "my-package" 3 | version: "0.1" 4 | depends: [ 5 | "ocaml" 6 | "ocamlfind" 7 | "cmdliner" 8 | ] 9 | build: [ 10 | [ "mkdir" "-p" "%{bin}%" ] 11 | [ "ocamlfind" "ocamlopt" "-package" "cmdliner" "-linkpkg" "hello.ml" "-o" "%{bin}%/hello" ] 12 | ] 13 | -------------------------------------------------------------------------------- /examples/docfile/opam-import/opam.export: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | compiler: ["ocaml.4.12.0"] 3 | roots: ["cmdliner.1.0.4" "ocaml.4.12.0" "ocamlfind.1.8.1"] 4 | installed: [ 5 | "base-bigarray.base" 6 | "base-threads.base" 7 | "base-unix.base" 8 | "cmdliner.1.0.4" 9 | "conf-m4.1" 10 | "ocaml.4.12.0" 11 | "ocaml-base-compiler.4.12.0" 12 | "ocaml-config.2" 13 | "ocaml-options-vanilla.1" 14 | "ocamlfind.1.8.1" 15 | ] 16 | -------------------------------------------------------------------------------- /examples/frama-c/flake.nix: -------------------------------------------------------------------------------- 1 | # We can build GUI stuff! 2 | # Don't try to build it statically though 3 | { 4 | inputs.opam-nix.url = "github:tweag/opam-nix"; 5 | inputs.flake-utils.url = "github:numtide/flake-utils"; 6 | outputs = 7 | { 8 | self, 9 | opam-nix, 10 | flake-utils, 11 | }: 12 | flake-utils.lib.eachDefaultSystem (system: { 13 | legacyPackages = 14 | let 15 | inherit (opam-nix.lib.${system}) queryToScope; 16 | 17 | pkgs = opam-nix.inputs.nixpkgs.legacyPackages.${system}; 18 | 19 | pkgs' = if pkgs.stdenv.isDarwin then pkgs else pkgs.pkgsStatic; 20 | 21 | scope = queryToScope { } { 22 | frama-c = "*"; 23 | alt-ergo = "2.4.1"; 24 | lablgtk3 = "*"; # Use lablgtk3 when appropriate 25 | lablgtk3-sourceview3 = "*"; 26 | ocaml-base-compiler = "*"; 27 | }; 28 | 29 | overlay = self: super: { 30 | frama-c = super.frama-c.overrideAttrs (oa: { 31 | buildInputs = oa.buildInputs ++ [ pkgs'.freetype ]; 32 | nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.makeWrapper ]; 33 | 34 | NIX_LDFLAGS = 35 | with pkgs; 36 | "-L${pkgs'.fontconfig.lib}/lib -L${pkgs'.pkgsStatic.expat}/lib -lfontconfig -lfreetype -lexpat"; 37 | postInstall = '' 38 | for i in $(find $out/bin -type f); do 39 | wrapProgram "$i" --prefix OCAMLPATH : "$OCAMLPATH" 40 | done 41 | ''; 42 | }); 43 | }; 44 | 45 | in 46 | scope.overrideScope' overlay; 47 | 48 | defaultPackage = self.legacyPackages.${system}.frama-c; 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /examples/materialized-opam-ed/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "opam-ed, without any IFD"; 3 | nixConfig.allow-import-from-derivation = false; 4 | inputs.opam-nix.url = "github:tweag/opam-nix"; 5 | inputs.flake-utils.url = "github:numtide/flake-utils"; 6 | outputs = 7 | { 8 | self, 9 | opam-nix, 10 | flake-utils, 11 | }: 12 | flake-utils.lib.eachDefaultSystem (system: { 13 | legacyPackages = 14 | let 15 | inherit (opam-nix.lib.${system}) materializedDefsToScope; 16 | scope = materializedDefsToScope { } ./package-defs.json; 17 | overlay = self: super: { }; 18 | 19 | in 20 | scope.overrideScope' overlay; 21 | 22 | defaultPackage = self.legacyPackages.${system}.opam-ed; 23 | 24 | devShell = 25 | with opam-nix.inputs.nixpkgs.legacyPackages.${system}; 26 | mkShell { buildInputs = [ opam-nix.packages.${system}.opam-nix-gen ]; }; 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /examples/materialized-opam-ed/package-defs.json: -------------------------------------------------------------------------------- 1 | {"__opam_nix_env":{},"__opam_nix_regen":["opam-nix-gen","-p","opam-ed","-p","ocaml-system"],"base-bigarray":{"description":"\nBigarray library distributed with the OCaml compiler\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml/opam-repository/issues","name":"base-bigarray","opam-version":"2.0","version":"base"},"base-domains":{"depends":[{"lhs":{"conditions":[{"arg":"5.0","prefix_relop":"geq"}],"val":"ocaml"},"logop":"or","rhs":{"conditions":[{"lhs":{"lhs":{"lhs":{"lhs":{"lhs":{"arg":"4.14.0+domains","prefix_relop":"eq"},"logop":"or","rhs":{"arg":"4.12.0+domains+effects","prefix_relop":"eq"}},"logop":"or","rhs":{"arg":"4.12.0+domains","prefix_relop":"eq"}},"logop":"or","rhs":{"arg":"4.10.0+multicore","prefix_relop":"eq"}},"logop":"or","rhs":{"arg":"4.10.0+multicore+no-effect-syntax","prefix_relop":"eq"}},"logop":"or","rhs":{"arg":"4.06.1+multicore","prefix_relop":"eq"}}],"val":"ocaml-variants"}}],"description":"\nDomains-based parallelism distributed with the Multicore OCaml compiler\"\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml-multicore/multicore-opam/issues","name":"base-domains","opam-version":"2.0","version":"base"},"base-nnp":{"depends":{"lhs":{"lhs":"base-domains","logop":"or","rhs":"ocaml-option-nnp"},"logop":"or","rhs":{"conditions":[{"arg":"4.06.1+no-naked-pointers+flambda","prefix_relop":"eq"}],"val":"ocaml-variants"}},"description":"\nInstalled when the compiler does not permit naked pointers in\nthe heap. Prior to OCaml 5.00.0, this mode was expressly selected\nby configuring with --disable-naked-pointers. The shared memory\nparallelism added in OCaml 5.00.0 requires this mode.\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml/opam-repository/issues","name":"base-nnp","opam-version":"2.0","synopsis":"Naked pointers prohibited in the OCaml heap","version":"base"},"base-threads":{"description":"\nThreads library distributed with the OCaml compiler\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml/opam-repository/issues","name":"base-threads","opam-version":"2.0","version":"base"},"base-unix":{"description":"\nUnix library distributed with the OCaml compiler\n","files-contents":{},"isLocal":false,"maintainer":"https://github.com/ocaml/opam-repository/issues","name":"base-unix","opam-version":"2.0","version":"base"},"cmdliner":{"authors":"The cmdliner programmers","bug-reports":"https://github.com/dbuenzli/cmdliner/issues","build":[{"id":"make"},"all","PREFIX=%{prefix}%"],"depends":[{"conditions":[{"arg":"4.08.0","prefix_relop":"geq"}],"val":"ocaml"}],"description":"Cmdliner allows the declarative definition of command line interfaces\nfor OCaml.\n\nIt provides a simple and compositional mechanism to convert command\nline arguments to OCaml values and pass them to your functions. The\nmodule automatically handles syntax errors, help messages and UNIX man\npage generation. It supports programs with single or multiple commands\nand respects most of the [POSIX][1] and [GNU][2] conventions.\n\nCmdliner has no dependencies and is distributed under the ISC license.\n\n[1]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html\n[2]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html\n\nHome page: http://erratique.ch/software/cmdliner","dev-repo":"git+https://erratique.ch/repos/cmdliner.git","doc":"https://erratique.ch/software/cmdliner/doc","files-contents":{},"homepage":"https://erratique.ch/software/cmdliner","install":[[{"id":"make"},"install","LIBDIR=%{_:lib}%","DOCDIR=%{_:doc}%"],[{"id":"make"},"install-doc","LIBDIR=%{_:lib}%","DOCDIR=%{_:doc}%"]],"isLocal":false,"license":"ISC","maintainer":"Daniel Bünzli ","name":"cmdliner","opam-version":"2.0","synopsis":"Declarative definition of command line interfaces for OCaml","tags":["cli","system","declarative","org:erratique"],"url":{"section":{"checksum":"sha512=6fcd6a59a6fbc6986b1aecdc3e4ce7a0dc43c65a16b427d6caa5504b10b51384f6b0bc703af646b09f5f1caeb6827b37d4480ce350ca8006204c850785f2810b","src":"https://erratique.ch/software/cmdliner/releases/cmdliner-1.2.0.tbz"}},"version":"1.2.0"},"ocaml":{"authors":["Xavier Leroy","Damien Doligez","Alain Frisch","Jacques Garrigue","Didier Rémy","Jérôme Vouillon"],"bug-reports":"https://github.com/ocaml/opam-repository/issues","build":["ocaml","%{ocaml-config:share}%/gen_ocaml_config.ml",{"id":"_:version"},{"id":"_:name"}],"build-env":[[{"lhs":{"id":"CAML_LD_LIBRARY_PATH"},"relop":"eq","rhs":""}],[{"lhs":{"id":"LSAN_OPTIONS"},"relop":"eq","rhs":"detect_leaks=0,exitcode=0"}],[{"lhs":{"id":"ASAN_OPTIONS"},"relop":"eq","rhs":"detect_leaks=0,exitcode=0"}]],"depends":[{"conditions":[{"arg":"3","prefix_relop":"geq"}],"val":"ocaml-config"},{"lhs":{"lhs":{"conditions":[{"lhs":{"arg":"5.0.0~","prefix_relop":"geq"},"logop":"and","rhs":{"arg":"5.0.1~","prefix_relop":"lt"}}],"val":"ocaml-base-compiler"},"logop":"or","rhs":{"conditions":[{"lhs":{"arg":"5.0.0~","prefix_relop":"geq"},"logop":"and","rhs":{"arg":"5.0.1~","prefix_relop":"lt"}}],"val":"ocaml-variants"}},"logop":"or","rhs":{"conditions":[{"lhs":{"arg":"5.0.0","prefix_relop":"geq"},"logop":"and","rhs":{"arg":"5.0.1~","prefix_relop":"lt"}}],"val":"ocaml-system"}}],"description":"\nThis package requires a matching implementation of OCaml,\nand polls it to initialise specific variables like `ocaml:native-dynlink`","files-contents":{},"flags":{"id":"conf"},"homepage":"https://ocaml.org","isLocal":false,"license":"LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception","maintainer":"platform@lists.ocaml.org","name":"ocaml","opam-version":"2.0","setenv":[[{"lhs":{"id":"CAML_LD_LIBRARY_PATH"},"relop":"eq","rhs":"%{_:stubsdir}%"}],[{"env_update":"prepend","lhs":{"id":"CAML_LD_LIBRARY_PATH"},"rhs":"%{lib}%/stublibs"}],[{"lhs":{"id":"OCAML_TOPLEVEL_PATH"},"relop":"eq","rhs":"%{toplevel}%"}]],"synopsis":"The OCaml compiler (virtual package)","version":"5.0.0"},"ocaml-config":{"authors":["Louis Gesbert ","David Allsopp "],"bug-reports":"https://github.com/ocaml/opam/issues","depends":[{"lhs":{"lhs":{"lhs":{"conditions":[{"lhs":{"arg":"5.0.0~","prefix_relop":"geq"},"logop":"or","rhs":{"lhs":{"id":"os"},"relop":"eq","rhs":"win32"}}],"val":"ocaml-base-compiler"},"logop":"or","rhs":{"conditions":[{"lhs":{"arg":"5.0.0~","prefix_relop":"geq"},"logop":"or","rhs":{"lhs":{"id":"os"},"relop":"eq","rhs":"win32"}}],"val":"ocaml-variants"}},"logop":"or","rhs":{"conditions":[{"lhs":{"arg":"5.0.0~","prefix_relop":"geq"},"logop":"or","rhs":{"lhs":{"id":"os"},"relop":"eq","rhs":"win32"}}],"val":"ocaml-system"}},"logop":"or","rhs":{"conditions":[{"arg":"4.12.0~","prefix_relop":"geq"}],"val":"dkml-base-compiler"}}],"description":"\nThis package is used by the OCaml package to set-up its variables.","extra-files":[["gen_ocaml_config.ml.in","md5=c0a50fb1f6ffe7a48bf8600e83ae6d38"],["ocaml-config.install","md5=8e50c5e2517d3463b3aad649748cafd7"]],"files-contents":{"gen_ocaml_config.ml.in":"let () =\n let ocaml_version =\n let v = Sys.ocaml_version in\n let l = String.length v in\n let plus = try String.index v '+' with Not_found -> l in\n (* Introduced in 4.11.0; used from 4.12.0 *)\n let tilde = try String.index v '~' with Not_found -> l in\n String.sub v 0 (min (min plus tilde) l)\n in\n if ocaml_version <> Sys.argv.(1) then\n (Printf.eprintf\n \"OCaml version mismatch: %%s, expected %s\"\n ocaml_version Sys.argv.(1);\n exit 1)\n else\n let oc = open_out (Sys.argv.(2) ^ \".config\") in\n let exe = \".exe\" in\n let (ocaml, suffix) =\n let s = Sys.executable_name in\n if Filename.check_suffix s exe then\n (Filename.chop_suffix s exe, exe)\n else\n (s, \"\")\n in\n let ocamlc = ocaml^\"c\"^suffix in\n let libdir =\n if Sys.command (ocamlc^\" -where > where\") = 0 then\n (* Must be opened in text mode for Windows *)\n let ic = open_in \"where\" in\n let r = input_line ic in\n close_in ic; r\n else\n failwith \"Bad return from 'ocamlc -where'\"\n in\n let stubsdir =\n let ic = open_in (Filename.concat libdir \"ld.conf\") in\n let rec r acc = try r (input_line ic::acc) with End_of_file -> acc in\n let lines = List.rev (r []) in\n close_in ic;\n let sep = if Sys.os_type = \"Win32\" then \";\" else \":\" in\n String.concat sep lines\n in\n let has_native_dynlink =\n let check_dir libdir =\n Sys.file_exists (Filename.concat libdir \"dynlink.cmxa\")\n in\n List.exists check_dir [Filename.concat libdir \"dynlink\"; libdir]\n in\n let p fmt = Printf.fprintf oc (fmt ^^ \"\\n\") in\n p \"opam-version: \\\"2.0\\\"\";\n p \"variables {\";\n p \" native: %%b\"\n (Sys.file_exists (ocaml^\"opt\"^suffix));\n p \" native-tools: %%b\"\n (* The variable [ocamlc] already has a suffix on Windows\n (ex. '...\\bin\\ocamlc.exe') so we use [ocaml] to check *)\n (Sys.file_exists (ocaml^\"c.opt\"^suffix));\n p \" native-dynlink: %%b\"\n has_native_dynlink;\n p \" stubsdir: %%S\"\n stubsdir;\n p \" preinstalled: %{ocaml-system:installed}%\";\n p \" compiler: \\\"%{ocaml-system:installed?system:}%%{ocaml-base-compiler:version}%%{dkml-base-compiler:version}%%{ocaml-variants:version}%%{ocaml-option-32bit:installed?+32bit:}%%{ocaml-option-afl:installed?+afl:}%%{ocaml-option-bytecode-only:installed?+bytecode-only:}%%{ocaml-option-default-unsafe-string:installed?+default-unsafe-string:}%%{ocaml-option-fp:installed?+fp:}%%{ocaml-option-flambda:installed?+flambda:}%%{ocaml-option-musl:installed?+musl:}%%{ocaml-option-nnp:installed?+nnp:}%%{ocaml-option-no-flat-float-array:installed?+no-flat-float-array:}%%{ocaml-option-spacetime:installed?+spacetime:}%%{ocaml-option-static:installed?+static:}%\\\"\";\n p \"}\";\n close_out oc\n","ocaml-config.install":"share: [\"gen_ocaml_config.ml\"]\n"},"homepage":"https://opam.ocaml.org/","isLocal":false,"license":"ISC","maintainer":"platform@lists.ocaml.org","name":"ocaml-config","opam-version":"2.0","substs":"gen_ocaml_config.ml","synopsis":"OCaml Switch Configuration","version":"3"},"ocaml-system":{"authors":"Xavier Leroy and many contributors","available":{"lhs":{"id":"sys-ocaml-version"},"relop":"eq","rhs":"5.0.0"},"bug-reports":"https://github.com/ocaml/opam-repository/issues","build":["ocaml","gen_ocaml_config.ml"],"conflict-class":"ocaml-core-compiler","depends":[{"conditions":[{"id":"post"}],"val":"ocaml"},{"conditions":[{"id":"post"}],"val":"base-unix"},{"conditions":[{"id":"post"}],"val":"base-threads"},{"conditions":[{"id":"post"}],"val":"base-bigarray"},{"conditions":[{"id":"post"}],"val":"base-domains"},{"conditions":[{"id":"post"}],"val":"base-nnp"}],"dev-repo":"git+https://github.com/ocaml/ocaml","extra-files":["gen_ocaml_config.ml.in","md5=093e7bec1ec95f9e4c6a313d73c5d840"],"files-contents":{"gen_ocaml_config.ml.in":"let () =\n let exe = \".exe\" in\n let ocamlc =\n let (base, suffix) =\n let s = Sys.executable_name in\n if Filename.check_suffix s exe then\n (Filename.chop_suffix s exe, exe)\n else\n (s, \"\") in\n base ^ \"c\" ^ suffix in\n if Sys.ocaml_version <> \"%{_:version}%\" then\n (Printf.eprintf\n \"ERROR: The compiler found at %%s has version %%s,\\n\\\n and this package requires %{_:version}%.\\n\\\n You should use e.g. 'opam switch create %{_:name}%.%%s' \\\n instead.\"\n ocamlc Sys.ocaml_version Sys.ocaml_version;\n exit 1)\n else\n let ocamlc_digest = Digest.to_hex (Digest.file ocamlc) in\n let libdir =\n if Sys.command (ocamlc^\" -where > %{_:name}%.config\") = 0 then\n let ic = open_in \"%{_:name}%.config\" in\n let r = input_line ic in\n close_in ic;\n Sys.remove \"%{_:name}%.config\";\n r\n else\n failwith \"Bad return from 'ocamlc -where'\"\n in\n let graphics = Filename.concat libdir \"graphics.cmi\" in\n let graphics_digest =\n if Sys.file_exists graphics then\n Digest.to_hex (Digest.file graphics)\n else\n String.make 32 '0'\n in\n let oc = open_out \"%{_:name}%.config\" in\n Printf.fprintf oc \"opam-version: \\\"2.0\\\"\\n\\\n file-depends: [ [ %%S %%S ] [ %%S %%S ] ]\\n\\\n variables { path: %%S }\\n\"\n ocamlc ocamlc_digest graphics graphics_digest (Filename.dirname ocamlc);\n close_out oc\n"},"flags":{"id":"compiler"},"homepage":"https://ocaml.org","isLocal":false,"license":"LGPL-2.1-or-later WITH OCaml-LGPL-linking-exception","maintainer":"platform@lists.ocaml.org","name":"ocaml-system","opam-version":"2.0","substs":"gen_ocaml_config.ml","synopsis":"The OCaml compiler (system version, from outside of opam)","version":"5.0.0"},"ocamlfind":{"authors":"Gerd Stolpmann ","available":{"lhs":{"id":"os"},"relop":"neq","rhs":"win32"},"bug-reports":"https://github.com/ocaml/ocamlfind/issues","build":[["./configure","-bindir",{"id":"bin"},"-sitelib",{"id":"lib"},"-mandir",{"id":"man"},"-config","%{lib}%/findlib.conf","-no-custom",{"conditions":[{"lhs":{"arg":{"id":"ocaml:preinstalled"},"pfxop":"not"},"logop":"and","rhs":{"lhs":{"id":"ocaml:version"},"relop":"geq","rhs":"4.02.0"}}],"val":"-no-camlp4"},{"conditions":[{"id":"ocaml:preinstalled"}],"val":"-no-topfind"}],[{"id":"make"},"all"],{"conditions":[{"id":"ocaml:native"}],"val":[{"id":"make"},"opt"]}],"depends":[{"conditions":[{"arg":"3.08.0","prefix_relop":"geq"}],"val":"ocaml"}],"depopts":["graphics"],"description":"\nFindlib is a library manager for OCaml. It provides a convention how\nto store libraries, and a file format (\"META\") to describe the\nproperties of libraries. There is also a tool (ocamlfind) for\ninterpreting the META files, so that it is very easy to use libraries\nin programs and scripts.\n","dev-repo":"git+https://github.com/ocaml/ocamlfind.git","extra-files":["0001-Harden-test-for-OCaml-5.patch","md5=3cddbf72164c29d4e50e077a92a37c6c"],"files-contents":{"0001-Harden-test-for-OCaml-5.patch":"diff a/configure b/configure\n--- a/configure\n+++ b/configure\n@@ -294,7 +294,7 @@\n # If findlib has been configured -sitelib $(ocamlc -where) then there's\n # nothing to do, but otherwise we need to put OCaml's Standard Library\n # into the path setting.\n- if [ ! -e \"${ocaml_sitelib}/stdlib/META\" ]; then\n+ if [ ! -e \"${ocaml_sitelib}/stdlib.cmi\" ]; then\n ocamlpath=\"${ocaml_core_stdlib}${path_sep}${ocamlpath}\"\n fi\n fi\n"},"homepage":"http://projects.camlcity.org/projects/findlib.html","install":[[{"id":"make"},"install"],{"conditions":[{"id":"ocaml:preinstalled"}],"val":["install","-m","0755","ocaml-stub","%{bin}%/ocaml"]}],"isLocal":false,"license":"MIT","maintainer":"Thomas Gazagnaire ","name":"ocamlfind","opam-version":"2.0","patches":["0001-Harden-test-for-OCaml-5.patch"],"synopsis":"A library manager for OCaml","url":{"section":{"checksum":["md5=96c6ee50a32cca9ca277321262dbec57","sha512=cfaf1872d6ccda548f07d32cc6b90c3aafe136d2aa6539e03143702171ee0199add55269bba894c77115535dc46a5835901a5d7c75768999e72db503bfd83027"],"src":"http://download.camlcity.org/download/findlib-1.9.6.tar.gz"}},"version":"1.9.6"},"opam-ed":{"authors":"Louis Gesbert ","bug-reports":"https://github.com/AltGr/opam-ed/issues","build":[{"id":"make"},{"conditions":[{"arg":{"id":"ocaml:native"},"pfxop":"not"}],"val":"COMP=ocamlc"}],"depends":[{"conditions":[{"arg":"4.03.0","prefix_relop":"geq"}],"val":"ocaml"},{"conditions":[{"id":"build"}],"val":"ocamlfind"},{"conditions":[{"arg":"1.0.0","prefix_relop":"geq"}],"val":"cmdliner"},{"conditions":[{"lhs":{"arg":"2.0.0","prefix_relop":"geq"},"logop":"and","rhs":{"arg":"2.1","prefix_relop":"lt"}}],"val":"opam-file-format"}],"description":"\nopam-ed can read and write files in the general opam syntax. It provides a small CLI with some useful commands for mechanically extracting or modifying the file contents.\n\nThe specification for the syntax itself is available at:\n http://opam.ocaml.org/doc/Manual.html#Common-file-format\n","dev-repo":"git+https://github.com/AltGr/opam-ed.git","files-contents":{},"homepage":"https://github.com/AltGr/opam-ed","isLocal":false,"license":"LGPL-2.1-only WITH OCaml-LGPL-linking-exception","maintainer":"Louis Gesbert ","name":"opam-ed","opam-version":"2.0","synopsis":"Command-line edition tool for handling the opam file syntax","url":{"section":{"checksum":["md5=29757044dec336ceed80b528a5d5717e","sha512=97ab5404a6b3c69628626232544069030cafb9daac85a1a6342f878e69678972180667613778c508ef87f19c2477938333eda41f713cca40af5376900dda7360"],"src":"https://github.com/AltGr/opam-ed/archive/0.3.tar.gz"}},"version":"0.3"},"opam-file-format":{"authors":"Louis Gesbert ","bug-reports":"https://github.com/ocaml/opam-file-format/issues","build":[{"id":"make"},{"conditions":[{"arg":{"id":"ocaml:native"},"pfxop":"not"}],"val":"byte"},{"conditions":[{"id":"ocaml:native"}],"val":"all"}],"depends":["ocaml"],"dev-repo":"git+https://github.com/ocaml/opam-file-format","files-contents":{},"flags":{"id":"light-uninstall"},"homepage":"https://opam.ocaml.org","install":[{"id":"make"},"install","PREFIX=%{prefix}%"],"isLocal":false,"license":"LGPL-2.1-only WITH OCaml-LGPL-linking-exception","maintainer":"Louis Gesbert ","name":"opam-file-format","opam-version":"2.0","remove":["rm","-rf","%{opam-file-format:lib}%"],"synopsis":"Parser and printer for the opam file syntax","url":{"section":{"checksum":"md5=d7852cb63df0f442bed14ba2c5740135","src":"https://github.com/ocaml/opam-file-format/archive/2.0.0.tar.gz"}},"version":"2.0.0"}} -------------------------------------------------------------------------------- /examples/ocaml-lsp/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Build an opam project with multiple packages"; 3 | inputs.opam-nix.url = "github:tweag/opam-nix"; 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | inputs.nixpkgs.url = "github:nixos/nixpkgs"; 6 | outputs = 7 | { 8 | self, 9 | nixpkgs, 10 | opam-nix, 11 | flake-utils, 12 | }: 13 | flake-utils.lib.eachDefaultSystem (system: { 14 | legacyPackages = 15 | let 16 | src = nixpkgs.legacyPackages.${system}.fetchFromGitHub { 17 | owner = "ocaml"; 18 | repo = "ocaml-lsp"; 19 | rev = "c961c46fc4705b18d336ac990b9c3b39354b9d7b"; 20 | sha256 = "U7g2ilKfd8EES1EDgy46LKkG/z1jwpd5LIkkqhe13iI="; 21 | fetchSubmodules = true; 22 | }; 23 | inherit (opam-nix.lib.${system}) buildOpamProject'; 24 | scope = buildOpamProject' { } src { ocaml-base-compiler = "*"; }; 25 | in 26 | scope; 27 | defaultPackage = self.legacyPackages.${system}.ocaml-lsp-server; 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /examples/opam-ed/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Build a package from opam-repository, linked statically (on Linux)"; 3 | inputs.opam-nix.url = "github:tweag/opam-nix"; 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | outputs = 6 | { 7 | self, 8 | opam-nix, 9 | flake-utils, 10 | }: 11 | flake-utils.lib.eachDefaultSystem (system: { 12 | legacyPackages = 13 | let 14 | inherit (opam-nix.lib.${system}) queryToScope; 15 | pkgs = opam-nix.inputs.nixpkgs.legacyPackages.${system}; 16 | scope = queryToScope { pkgs = pkgs.pkgsStatic; } { 17 | opam-ed = "*"; 18 | ocaml-system = "*"; 19 | }; 20 | overlay = self: super: { 21 | # Prevent unnecessary dependencies on the resulting derivation 22 | opam-ed = super.opam-ed.overrideAttrs (_: { 23 | removeOcamlReferences = true; 24 | postFixup = "rm -rf $out/nix-support"; 25 | }); 26 | }; 27 | in 28 | scope.overrideScope' overlay; 29 | defaultPackage = self.legacyPackages.${system}.opam-ed; 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /examples/opam2json-static/flake.nix: -------------------------------------------------------------------------------- 1 | # Static build using the compiler from OPAM 2 | { 3 | inputs.opam-nix.url = "github:tweag/opam-nix"; 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | inputs.opam2json.url = "github:tweag/opam2json"; 6 | outputs = 7 | { 8 | self, 9 | opam-nix, 10 | opam2json, 11 | flake-utils, 12 | }: 13 | flake-utils.lib.eachDefaultSystem (system: { 14 | legacyPackages = 15 | let 16 | inherit (opam-nix.lib.${system}) buildOpamProject; 17 | pkgs = opam-nix.inputs.nixpkgs.legacyPackages.${system}; 18 | scope = buildOpamProject { pkgs = pkgs.pkgsStatic; } "opam2json" opam2json { 19 | ocaml-base-compiler = "*"; # This makes opam choose the non-system compiler 20 | }; 21 | overlay = self: super: { 22 | # Prevent unnecessary dependencies on the resulting derivation 23 | opam2json = super.opam2json.overrideAttrs (_: { 24 | removeOcamlReferences = true; 25 | postFixup = "rm -rf $out/nix-support"; 26 | }); 27 | }; 28 | 29 | in 30 | scope.overrideScope' overlay; 31 | 32 | defaultPackage = self.legacyPackages.${system}.opam2json; 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /examples/opam2json/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Build an opam project not in the repo, using sane defaults"; 3 | inputs.opam-nix.url = "github:tweag/opam-nix"; 4 | inputs.flake-utils.url = "github:numtide/flake-utils"; 5 | inputs.opam2json.url = "github:tweag/opam2json"; 6 | outputs = 7 | { 8 | self, 9 | opam-nix, 10 | opam2json, 11 | flake-utils, 12 | }: 13 | flake-utils.lib.eachDefaultSystem (system: { 14 | legacyPackages = 15 | let 16 | inherit (opam-nix.lib.${system}) buildOpamProject; 17 | scope = buildOpamProject { } "opam2json" opam2json { 18 | ocaml-system = "*"; 19 | }; 20 | in 21 | scope; 22 | defaultPackage = self.legacyPackages.${system}.opam2json; 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /examples/tezos/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Big, girthy package with a lot of dependencies"; 3 | inputs.flake-utils.url = "github:numtide/flake-utils"; 4 | outputs = 5 | { 6 | self, 7 | opam-nix, 8 | flake-utils, 9 | }: 10 | flake-utils.lib.eachDefaultSystem (system: { 11 | legacyPackages = 12 | let 13 | 14 | inherit (opam-nix.lib.${system}) queryToScope; 15 | 16 | scope = queryToScope { } { tezos = "*"; }; 17 | overlay = self: super: { }; 18 | in 19 | scope.overrideScope' overlay; 20 | 21 | defaultPackage = self.legacyPackages.${system}.tezos; 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1696426674, 7 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-utils": { 20 | "inputs": { 21 | "systems": "systems" 22 | }, 23 | "locked": { 24 | "lastModified": 1726560853, 25 | "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", 26 | "owner": "numtide", 27 | "repo": "flake-utils", 28 | "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "numtide", 33 | "repo": "flake-utils", 34 | "type": "github" 35 | } 36 | }, 37 | "mirage-opam-overlays": { 38 | "flake": false, 39 | "locked": { 40 | "lastModified": 1710922379, 41 | "narHash": "sha256-j4QREQDUf8oHOX7qg6wAOupgsNQoYlufxoPrgagD+pY=", 42 | "owner": "dune-universe", 43 | "repo": "mirage-opam-overlays", 44 | "rev": "797cb363df3ff763c43c8fbec5cd44de2878757e", 45 | "type": "github" 46 | }, 47 | "original": { 48 | "owner": "dune-universe", 49 | "repo": "mirage-opam-overlays", 50 | "type": "github" 51 | } 52 | }, 53 | "nixpkgs": { 54 | "locked": { 55 | "lastModified": 1740828860, 56 | "narHash": "sha256-cjbHI+zUzK5CPsQZqMhE3npTyYFt9tJ3+ohcfaOF/WM=", 57 | "owner": "nixos", 58 | "repo": "nixpkgs", 59 | "rev": "303bd8071377433a2d8f76e684ec773d70c5b642", 60 | "type": "github" 61 | }, 62 | "original": { 63 | "owner": "nixos", 64 | "ref": "nixos-unstable", 65 | "repo": "nixpkgs", 66 | "type": "github" 67 | } 68 | }, 69 | "opam-overlays": { 70 | "flake": false, 71 | "locked": { 72 | "lastModified": 1726822209, 73 | "narHash": "sha256-bwM18ydNT9fYq91xfn4gmS21q322NYrKwfq0ldG9GYw=", 74 | "owner": "dune-universe", 75 | "repo": "opam-overlays", 76 | "rev": "f2bec38beca4aea9e481f2fd3ee319c519124649", 77 | "type": "github" 78 | }, 79 | "original": { 80 | "owner": "dune-universe", 81 | "repo": "opam-overlays", 82 | "type": "github" 83 | } 84 | }, 85 | "opam-repository": { 86 | "flake": false, 87 | "locked": { 88 | "lastModified": 1740730647, 89 | "narHash": "sha256-6veU2WjUGcWDAzLDjoAI1L6GWZd0KIUq19sHcbJS+u8=", 90 | "owner": "ocaml", 91 | "repo": "opam-repository", 92 | "rev": "f1f75fef5fbf1e8bd1cc9544e50b89ba59f625e2", 93 | "type": "github" 94 | }, 95 | "original": { 96 | "owner": "ocaml", 97 | "repo": "opam-repository", 98 | "type": "github" 99 | } 100 | }, 101 | "opam2json": { 102 | "inputs": { 103 | "nixpkgs": [ 104 | "nixpkgs" 105 | ] 106 | }, 107 | "locked": { 108 | "lastModified": 1671540003, 109 | "narHash": "sha256-5pXfbUfpVABtKbii6aaI2EdAZTjHJ2QntEf0QD2O5AM=", 110 | "owner": "tweag", 111 | "repo": "opam2json", 112 | "rev": "819d291ea95e271b0e6027679de6abb4d4f7f680", 113 | "type": "github" 114 | }, 115 | "original": { 116 | "owner": "tweag", 117 | "repo": "opam2json", 118 | "type": "github" 119 | } 120 | }, 121 | "root": { 122 | "inputs": { 123 | "flake-compat": "flake-compat", 124 | "flake-utils": "flake-utils", 125 | "mirage-opam-overlays": "mirage-opam-overlays", 126 | "nixpkgs": "nixpkgs", 127 | "opam-overlays": "opam-overlays", 128 | "opam-repository": "opam-repository", 129 | "opam2json": "opam2json" 130 | } 131 | }, 132 | "systems": { 133 | "locked": { 134 | "lastModified": 1681028828, 135 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 136 | "owner": "nix-systems", 137 | "repo": "default", 138 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 139 | "type": "github" 140 | }, 141 | "original": { 142 | "owner": "nix-systems", 143 | "repo": "default", 144 | "type": "github" 145 | } 146 | } 147 | }, 148 | "root": "root", 149 | "version": 7 150 | } 151 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 4 | flake-utils.url = "github:numtide/flake-utils"; 5 | opam2json = { 6 | url = "github:tweag/opam2json"; 7 | inputs.nixpkgs.follows = "nixpkgs"; 8 | }; 9 | 10 | flake-compat = { 11 | url = "github:edolstra/flake-compat"; 12 | flake = false; 13 | }; 14 | 15 | # Used for examples/tests and as a default repository 16 | opam-repository = { 17 | url = "github:ocaml/opam-repository"; 18 | flake = false; 19 | }; 20 | 21 | # used for opam-monorepo 22 | opam-overlays = { 23 | url = "github:dune-universe/opam-overlays"; 24 | flake = false; 25 | }; 26 | mirage-opam-overlays = { 27 | url = "github:dune-universe/mirage-opam-overlays"; 28 | flake = false; 29 | }; 30 | }; 31 | 32 | outputs = 33 | { 34 | self, 35 | nixpkgs, 36 | flake-utils, 37 | opam2json, 38 | opam-repository, 39 | opam-overlays, 40 | mirage-opam-overlays, 41 | ... 42 | }@inputs: 43 | { 44 | aux = import ./src/lib.nix nixpkgs.lib; 45 | templates.simple = { 46 | description = "Simply build an opam package, preferrably a library, from a local directory"; 47 | path = ./templates/simple; 48 | }; 49 | templates.executable = { 50 | description = "Build an executable from a local opam package, and provide a development shell with some convinient tooling"; 51 | path = ./templates/executable; 52 | }; 53 | templates.multi-package = { 54 | description = "Build multiple packages from a single workspace, and provide a development shell with some convinient tooling"; 55 | path = ./templates/multi-package; 56 | }; 57 | templates.default = self.templates.simple; 58 | 59 | overlays = { 60 | ocaml-overlay = import ./src/overlays/ocaml.nix; 61 | ocaml-static-overlay = import ./src/overlays/ocaml-static.nix; 62 | }; 63 | } 64 | // flake-utils.lib.eachDefaultSystem ( 65 | system: 66 | let 67 | # The formats of opam2json output that we support 68 | opam2json-versions = [ "0.4" ]; 69 | pkgs = nixpkgs.legacyPackages.${system}.extend ( 70 | nixpkgs.lib.composeManyExtensions [ 71 | (final: prev: { 72 | opam2json = 73 | if __elem (prev.opam2json.version or null) opam2json-versions then 74 | prev.opam2json 75 | else 76 | (opam2json.overlay final prev).opam2json; 77 | }) 78 | ] 79 | ); 80 | opam-nix = import ./src/opam.nix { 81 | inherit 82 | pkgs 83 | opam-repository 84 | opam-overlays 85 | mirage-opam-overlays 86 | ; 87 | }; 88 | in 89 | rec { 90 | lib = opam-nix; 91 | checks = packages // (pkgs.callPackage ./examples/docfile { inherit opam-nix; }).checks; 92 | 93 | legacyPackages = __mapAttrs ( 94 | name: versions: 95 | let 96 | allVersions = __listToAttrs ( 97 | map ( 98 | version: 99 | nixpkgs.lib.nameValuePair version 100 | (lib.queryToScope { } ( 101 | { 102 | ocaml-base-compiler = "*"; 103 | } 104 | // { 105 | ${name} = version; 106 | } 107 | )).${name} 108 | ) versions 109 | ); 110 | in 111 | allVersions 112 | // { 113 | latest = allVersions.${nixpkgs.lib.last versions}; 114 | } 115 | ) (lib.listRepo opam-repository); 116 | 117 | allChecks = pkgs.runCommand "opam-nix-checks" { checks = __attrValues checks; } "touch $out"; 118 | 119 | packages = 120 | let 121 | examples = rec { 122 | _0install = (import ./examples/0install/flake.nix).outputs { 123 | self = _0install; 124 | opam-nix = inputs.self; 125 | inherit (inputs) flake-utils; 126 | }; 127 | coq = (import ./examples/coq/flake.nix).outputs { 128 | self = coq; 129 | opam-nix = inputs.self; 130 | inherit (inputs) nixpkgs opam-repository flake-utils; 131 | }; 132 | frama-c = (import ./examples/frama-c/flake.nix).outputs { 133 | self = frama-c; 134 | opam-nix = inputs.self; 135 | inherit (inputs) flake-utils; 136 | }; 137 | opam-ed = (import ./examples/opam-ed/flake.nix).outputs { 138 | self = opam-ed; 139 | opam-nix = inputs.self; 140 | inherit (inputs) flake-utils; 141 | }; 142 | opam2json = (import ./examples/opam2json/flake.nix).outputs { 143 | self = opam2json; 144 | opam-nix = inputs.self; 145 | inherit (inputs) opam2json flake-utils; 146 | }; 147 | ocaml-lsp = (import ./examples/ocaml-lsp/flake.nix).outputs { 148 | self = ocaml-lsp; 149 | opam-nix = inputs.self; 150 | inherit (inputs) nixpkgs flake-utils; 151 | }; 152 | opam2json-static = (import ./examples/opam2json-static/flake.nix).outputs { 153 | self = opam2json-static; 154 | opam-nix = inputs.self; 155 | inherit (inputs) opam2json flake-utils; 156 | }; 157 | tezos = (import ./examples/tezos/flake.nix).outputs { 158 | self = tezos; 159 | opam-nix = inputs.self; 160 | inherit (inputs) flake-utils; 161 | }; 162 | materialized-opam-ed = (import ./examples/materialized-opam-ed/flake.nix).outputs { 163 | self = materialized-opam-ed; 164 | opam-nix = inputs.self; 165 | inherit (inputs) flake-utils; 166 | }; 167 | }; 168 | in 169 | { 170 | opam-nix-gen = pkgs.substituteAll { 171 | name = "opam-nix-gen"; 172 | src = ./scripts/opam-nix-gen.in; 173 | dir = "bin"; 174 | isExecutable = true; 175 | inherit (pkgs) runtimeShell coreutils nix; 176 | opamNix = "${self}"; 177 | }; 178 | opam-nix-regen = pkgs.substituteAll { 179 | name = "opam-nix-regen"; 180 | src = ./scripts/opam-nix-regen.in; 181 | dir = "bin"; 182 | isExecutable = true; 183 | inherit (pkgs) runtimeShell jq; 184 | opamNixGen = "${self.packages.${system}.opam-nix-gen}/bin/opam-nix-gen"; 185 | }; 186 | } 187 | // builtins.mapAttrs (_: e: e.defaultPackage.${system}) examples; 188 | } 189 | ); 190 | } 191 | -------------------------------------------------------------------------------- /patches/ocamlfind/install_topfind_192.patch: -------------------------------------------------------------------------------- 1 | --- a/src/findlib/Makefile 2 | +++ b/src/findlib/Makefile 3 | @@ -123,7 +123,7 @@ clean: 4 | install: all 5 | mkdir -p "$(prefix)$(OCAML_SITELIB)/$(NAME)" 6 | mkdir -p "$(prefix)$(OCAMLFIND_BIN)" 7 | - test $(INSTALL_TOPFIND) -eq 0 || cp topfind "$(prefix)$(OCAML_CORE_STDLIB)" 8 | + test $(INSTALL_TOPFIND) -eq 0 || cp topfind "$(prefix)$(OCAML_SITELIB)" 9 | files=`$(SH) $(TOP)/tools/collect_files $(TOP)/Makefile.config findlib.cmi findlib.mli findlib.cma findlib.cmxa findlib$(LIB_SUFFIX) findlib.cmxs topfind.cmi topfind.mli fl_package_base.mli fl_package_base.cmi fl_metascanner.mli fl_metascanner.cmi fl_metatoken.cmi findlib_top.cma findlib_top.cmxa findlib_top$(LIB_SUFFIX) findlib_top.cmxs findlib_dynload.cma findlib_dynload.cmxa findlib_dynload$(LIB_SUFFIX) findlib_dynload.cmxs fl_dynload.mli fl_dynload.cmi META` && \ 10 | cp $$files "$(prefix)$(OCAML_SITELIB)/$(NAME)" 11 | f="ocamlfind$(EXEC_SUFFIX)"; { test -f ocamlfind_opt$(EXEC_SUFFIX) && f="ocamlfind_opt$(EXEC_SUFFIX)"; }; \ -------------------------------------------------------------------------------- /patches/ocamlfind/install_topfind_193.patch: -------------------------------------------------------------------------------- 1 | findlib annoyingly tries to install TOPFIND into the ocaml installation directory. 2 | With opam-nix, the ocaml installation directory is (thankfully) immutable, since it is part of a different derivation. 3 | Install to the correct place instead 4 | diff --git a/findlib.conf.in b/findlib.conf.in 5 | index 261d2c8..461bafc 100644 6 | --- a/findlib.conf.in 7 | +++ b/findlib.conf.in 8 | @@ -1,2 +1,3 @@ 9 | destdir="@SITELIB@" 10 | path="@SITELIB@" 11 | +ldconf="ignore" 12 | \ No newline at end of file 13 | diff --git a/src/findlib/Makefile b/src/findlib/Makefile 14 | index 4fd3f81..5b9a81e 100644 15 | --- a/src/findlib/Makefile 16 | +++ b/src/findlib/Makefile 17 | @@ -123,7 +123,7 @@ clean: 18 | install: all 19 | mkdir -p "$(prefix)$(OCAML_SITELIB)/$(NAME)" 20 | mkdir -p "$(prefix)$(OCAMLFIND_BIN)" 21 | - test $(INSTALL_TOPFIND) -eq 0 || cp topfind "$(prefix)$(OCAML_CORE_STDLIB)" 22 | + test $(INSTALL_TOPFIND) -eq 0 || cp topfind "$(prefix)$(OCAML_SITELIB)" 23 | files=`$(SH) $(TOP)/tools/collect_files $(TOP)/Makefile.config \ 24 | findlib.cmi findlib.mli findlib.cma findlib.cmxa findlib$(LIB_SUFFIX) findlib.cmxs \ 25 | findlib_config.cmi findlib_config.ml topfind.cmi topfind.mli \ 26 | -------------------------------------------------------------------------------- /patches/ocamlfind/install_topfind_194.patch: -------------------------------------------------------------------------------- 1 | See comment in ./ocamlfind_install_topfind.patch 2 | diff --git a/src/findlib/Makefile b/src/findlib/Makefile 3 | index 84514b6..12e4ef6 100644 4 | --- a/src/findlib/Makefile 5 | +++ b/src/findlib/Makefile 6 | @@ -123,8 +123,7 @@ clean: 7 | install: all 8 | $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAML_SITELIB)/$(NAME)" 9 | $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAMLFIND_BIN)" 10 | - $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAML_CORE_STDLIB)" 11 | - test $(INSTALL_TOPFIND) -eq 0 || $(INSTALLFILE) topfind "$(DESTDIR)$(prefix)$(OCAML_CORE_STDLIB)/" 12 | + test $(INSTALL_TOPFIND) -eq 0 || $(INSTALLFILE) topfind "$(DESTDIR)$(prefix)$(OCAML_SITELIB)/" 13 | files=`$(SH) $(TOP)/tools/collect_files $(TOP)/Makefile.config \ 14 | findlib.cmi findlib.mli findlib.cma findlib.cmxa findlib$(LIB_SUFFIX) findlib.cmxs \ 15 | findlib_config.cmi findlib_config.ml topfind.cmi topfind.mli \ 16 | -------------------------------------------------------------------------------- /patches/ocamlfind/install_topfind_196.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/findlib/Makefile b/src/findlib/Makefile 2 | index 69a19d1..4ea250c 100644 3 | --- a/src/findlib/Makefile 4 | +++ b/src/findlib/Makefile 5 | @@ -123,8 +123,7 @@ clean: 6 | install: all 7 | $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAML_SITELIB)/$(NAME)" 8 | $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAMLFIND_BIN)" 9 | - test $(INSTALL_TOPFIND) -eq 0 || $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAML_CORE_STDLIB)" 10 | - test $(INSTALL_TOPFIND) -eq 0 || $(INSTALLFILE) topfind "$(DESTDIR)$(prefix)$(OCAML_CORE_STDLIB)/" 11 | + test $(INSTALL_TOPFIND) -eq 0 || $(INSTALLFILE) topfind "$(DESTDIR)$(prefix)$(OCAML_SITELIB)/" 12 | files=`$(SH) $(TOP)/tools/collect_files $(TOP)/Makefile.config \ 13 | findlib.cmi findlib.mli findlib.cma findlib.cmxa findlib$(LIB_SUFFIX) findlib.cmxs \ 14 | findlib_config.cmi findlib_config.ml topfind.cmi topfind.mli \ 15 | -------------------------------------------------------------------------------- /patches/ocamlfind/install_topfind_198.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/findlib/Makefile b/src/findlib/Makefile 2 | index b2317c1..09c6239 100644 3 | --- a/src/findlib/Makefile 4 | +++ b/src/findlib/Makefile 5 | @@ -134,8 +134,7 @@ clean: 6 | install: all 7 | $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAML_SITELIB)/$(NAME)" 8 | $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAMLFIND_BIN)" 9 | - test $(INSTALL_TOPFIND) -eq 0 || $(INSTALLDIR) "$(DESTDIR)$(prefix)$(OCAML_CORE_STDLIB)" 10 | - test $(INSTALL_TOPFIND) -eq 0 || $(CP) topfind "$(DESTDIR)$(prefix)$(OCAML_CORE_STDLIB)/" 11 | + test $(INSTALL_TOPFIND) -eq 0 || $(INSTALLFILE) topfind "$(DESTDIR)$(prefix)$(OCAML_SITELIB)/" 12 | files=`$(SH) $(TOP)/tools/collect_files $(TOP)/Makefile.config \ 13 | findlib.cmi findlib.mli findlib.cma findlib.cmxa findlib$(LIB_SUFFIX) findlib.cmxs \ 14 | findlib_config.cmi findlib_config.ml topfind.cmi topfind.mli \ 15 | -------------------------------------------------------------------------------- /scripts/opam-nix-gen.in: -------------------------------------------------------------------------------- 1 | #!@runtimeShell@ 2 | 3 | set -euo pipefail 4 | 5 | PATH="$PATH:@coreutils@/bin:@nix@/bin" 6 | 7 | cmd="$(basename "$0")" 8 | 9 | usage() { 10 | { 11 | echo "$cmd: Resolve opam packages and produce a JSON file suitable for consumption by materializedDefsToScope" 12 | echo "Usage: $cmd [-r ...] [-l ...] [-e VAR=VALUE ...] [-p NAME[.VERSION] ...] [-t] [-d] <-p NAME[.VERSION] | NAME DIRECTORY>" 13 | echo " Options:" 14 | echo " -r: Add a remote repository to the resolver context" 15 | echo " -n: Don't use default opam-repository" 16 | echo " -l: Add a repository made from a local directory to the resolver context" 17 | echo " -e: Add an opam environment binding to the resolver context" 18 | echo " -p: Add a package to the resolver context" 19 | echo " -d: Build documentation (with-doc)" 20 | echo " -t: Build tests (with-test)" 21 | echo " (optional) Positional arguments:" 22 | echo " NAME: project's name" 23 | echo " DIRECTORY: project's directory" 24 | echo " (note: the project's directory must contain .opam file)" 25 | echo " Examples:" 26 | echo " Resolve a package called 'my-package', the source of which is in the current directory, and write the defs to 'package-defs.json':" 27 | echo " $cmd my-package . package-defs.json" 28 | } >&2 29 | exit 1 30 | } 31 | 32 | processPackage() { 33 | name="${1%.*}" 34 | version="${1#*.}" 35 | if [[ "$name" == "$version" ]]; then 36 | # No version was supplied 37 | printf '"%s" = "*"' "$name" 38 | else 39 | printf '"%s" = "%s"' "$name" "$version" 40 | fi 41 | } 42 | 43 | processProject() { 44 | if [[ -n "${1-}" ]] && [[ -n "${2-}" ]]; then 45 | name="${1}" 46 | directory="${2}" 47 | elif [[ -n "${1-}" ]]; then 48 | echo "Missing project directory" 2>&1 49 | usage 50 | fi 51 | } 52 | 53 | regenCommand="\"$cmd\"" 54 | for arg in "$@"; do 55 | regenCommand="$regenCommand \"$arg\"" 56 | done 57 | regenCommand="regenCommand = [$regenCommand];" 58 | 59 | repos_default="opamRepository" 60 | 61 | query="" 62 | 63 | with_test=false 64 | with_doc=false 65 | 66 | while getopts ":r:l:e:p:td" o; do 67 | case "${o}" in 68 | n) 69 | repos_default="" 70 | ;; 71 | r) 72 | repos="${repos-}${repos+ }(builtins.fetchTree \"${OPTARG}\")" 73 | ;; 74 | l) 75 | repos="${repos-}${repos+ }(makeOpamRepo $(realpath "${OPTARG}"))" 76 | ;; 77 | e) 78 | env="${env-}${env+ }${OPTARG};" 79 | ;; 80 | p) 81 | query="${query-}${query+ }$(processPackage "${OPTARG}");" 82 | ;; 83 | t) 84 | with_test=true 85 | ;; 86 | d) 87 | with_doc=true 88 | ;; 89 | *) usage 90 | esac 91 | done 92 | shift $((OPTIND-1)) 93 | 94 | repos="repos = [ ${repos_default-} ${repos-} ];" 95 | env="${env+env = {}${env-}${env+\};}" 96 | resolveArgs="resolveArgs = { with-test = ${with_test}; with-doc = ${with_doc}; };" 97 | 98 | if [ "$#" -ge 2 ]; then 99 | name="$1" 100 | directory="$(realpath "$2")" 101 | shift 2 102 | out="${1-/dev/stdout}" 103 | cat "$(nix eval --impure --raw @opamNix@#lib --apply "(m: with m.\${builtins.currentSystem}; materializeOpamProject { $repos $env $regenCommand $resolveArgs } \"$name\" $directory { $query })")" > "$out" 104 | else 105 | if [ -z "${query-}" ]; then 106 | echo "Must supply at least one package (-p) option or project NAME and DIRECTORY as positional arguments" 2>&1 107 | usage 108 | fi 109 | out="${1-/dev/stdout}" 110 | cat "$(nix eval --impure --raw @opamNix@#lib --apply "(m: with m.\${builtins.currentSystem}; materialize { $repos $env $regenCommand $resolveArgs } { $query })")" > "$out" 111 | fi 112 | -------------------------------------------------------------------------------- /scripts/opam-nix-regen.in: -------------------------------------------------------------------------------- 1 | #!@runtimeShell@ 2 | 3 | set -euo pipefail 4 | 5 | export PATH="$PATH:@jq@/bin:@opamNixGen/bin" 6 | 7 | cmd="$(basename "$0")" 8 | 9 | usage() { 10 | { 11 | echo "$cmd: Regenerate a package-defs.json file" 12 | echo "Usage: $cmd FILE" 13 | echo " Positional arguments:" 14 | echo " FILE: path to package-defs.json" 15 | } >&2 16 | exit 1 17 | } 18 | 19 | packageDefs="$1" 20 | 21 | eval "$(cat "$packageDefs" | jq -r ".__opam_nix_regen | @tsv")" 22 | -------------------------------------------------------------------------------- /src/builder.nix: -------------------------------------------------------------------------------- 1 | lib: 2 | 3 | let 4 | inherit (builtins) 5 | isString 6 | isList 7 | head 8 | filter 9 | foldl' 10 | trace 11 | toFile 12 | readDir 13 | replaceStrings 14 | concatStringsSep 15 | attrValues 16 | ; 17 | inherit (lib) 18 | optional 19 | hasSuffix 20 | optionalString 21 | foldl 22 | mapAttrs 23 | recursiveUpdate 24 | escapeShellArg 25 | warn 26 | flatten 27 | ; 28 | 29 | inherit (import ./evaluator lib) 30 | setup 31 | compareVersions' 32 | collectAllValuesFromOptionList 33 | functionArgsFor 34 | filterPackageFormula 35 | filterOptionList 36 | pkgVarsFor 37 | varsToShell 38 | filterSectionInShell 39 | getHashes 40 | envToShell 41 | getUrl 42 | ; 43 | 44 | fallbackPackageVars = name: { 45 | inherit name; 46 | installed = "false"; 47 | enable = "disable"; 48 | version = ""; 49 | }; 50 | in 51 | originalPkgdef: resolveEnv: { 52 | 53 | __functionArgs = { 54 | extraDeps = true; 55 | extraVars = true; 56 | nixpkgs = false; 57 | buildPackages = false; 58 | 59 | opam-installer = true; 60 | ocaml = true; 61 | } // functionArgsFor originalPkgdef; 62 | 63 | __functor = 64 | self: deps: 65 | deps.nixpkgs.stdenv.mkDerivation ( 66 | fa: 67 | let 68 | inherit (deps.nixpkgs.pkgsBuildBuild) 69 | unzip 70 | opam-installer 71 | jq 72 | opam2json 73 | removeReferencesTo 74 | ; 75 | 76 | inherit (fa.passthru) pkgdef; 77 | 78 | inherit (pkgdef) name version; 79 | 80 | globalVariables = (import ./global-variables.nix deps.nixpkgs.stdenv.hostPlatform) // resolveEnv; 81 | 82 | defaultVars = globalVariables // { 83 | with-test = fa.doCheck; 84 | with-doc = fa.doDoc; 85 | dev = pkgdef ? src; 86 | build = true; 87 | post = false; 88 | pinned = true; 89 | }; 90 | 91 | versionResolutionVars = 92 | pkgdef 93 | // defaultVars 94 | // { 95 | _ = pkgdef; 96 | ${name} = pkgdef; 97 | } 98 | // (mapAttrs (name: dep: dep.passthru.pkgdef.version or dep.version or null) deps) 99 | // deps.extraVars or { }; 100 | 101 | patches = filterOptionList versionResolutionVars pkgdef.patches or [ ]; 102 | 103 | substs = filterOptionList versionResolutionVars pkgdef.substs or [ ]; 104 | 105 | depends = filterPackageFormula versionResolutionVars pkgdef.depends or [ ]; 106 | 107 | depopts = filterPackageFormula versionResolutionVars pkgdef.depopts or [ ]; 108 | 109 | ocamlInputs = 110 | map ( 111 | x: deps.${x} or (lib.warn "[opam-nix] ${name}: missing required dependency: ${x}" null) 112 | ) depends 113 | ++ map ( 114 | x: deps.${x} or (trace "[opam-nix] ${name}: missing optional dependency ${x}" null) 115 | ) depopts; 116 | 117 | stubOutputs = { 118 | build = "$NIX_BUILD_TOP/$sourceRoot"; 119 | }; 120 | 121 | vars = { 122 | inherit name version; 123 | installed = true; 124 | enable = "enable"; 125 | pinned = false; 126 | build = null; 127 | hash = null; 128 | dev = pkgdef ? src; 129 | build-id = null; 130 | opamfile = null; 131 | 132 | prefix = "$out"; 133 | bin = "$out/bin"; 134 | sbin = "$out/bin"; 135 | lib = "$out/lib/ocaml/\${opam__ocaml__version}/site-lib"; 136 | man = "$out/share/man"; 137 | doc = "$out/share/doc"; 138 | share = "$out/share"; 139 | etc = "$out/etc"; 140 | }; 141 | 142 | #// pkgVarsFor "ocaml" deps.ocaml.passthru.vars; 143 | 144 | pkgVars = vars: pkgVarsFor "_" vars // pkgVarsFor name vars; 145 | 146 | setFallbackDepVars = varsToShell ( 147 | foldl recursiveUpdate { } ( 148 | map (name: pkgVarsFor name (fallbackPackageVars name)) ( 149 | collectAllValuesFromOptionList pkgdef.depends or [ ] 150 | ++ collectAllValuesFromOptionList pkgdef.depopts or [ ] 151 | ) 152 | ) 153 | ); 154 | 155 | externalPackages = 156 | if (readDir ./overlays/external) ? "${globalVariables.os-distribution}.nix" then 157 | import (./overlays/external + "/${globalVariables.os-distribution}.nix") deps.nixpkgs 158 | else 159 | warn "[opam-nix] Depexts are not supported on ${globalVariables.os-distribution}" { }; 160 | 161 | good-depexts = 162 | if (pkgdef ? depexts && (!isList pkgdef.depexts || !isList (head pkgdef.depexts))) then 163 | pkgdef.depexts 164 | else 165 | [ ]; 166 | 167 | extInputNames = filterOptionList versionResolutionVars good-depexts; 168 | 169 | extInputs = map ( 170 | x: 171 | if isString x then 172 | externalPackages.${x} or (warn '' 173 | [opam-nix] External dependency ${x} of package ${name}.${version} is missing. 174 | Please, add it to the file /overlays/external/${globalVariables.os-distribution}.nix and make a pull request with your change. 175 | In the meantime, you can manually add the dependency to buildInputs/nativeBuildInputs of your derivation with overrideAttrs. 176 | '' null) 177 | else 178 | null 179 | ) extInputNames; 180 | 181 | inherit (getUrl deps.nixpkgs pkgdef) archive src; 182 | 183 | evalOpamVar = '' 184 | evalOpamVar() { 185 | contents="''${1%*\}\%}" 186 | contents="''${contents//\%\{}" 187 | var="''${contents%\?*}" 188 | var_minus_underscores="''${var//-/_}" 189 | var_plus_underscores="''${var_minus_underscores//+/_}" 190 | var_dot_underscores="''${var_minus_underscores//./_}" 191 | varname="opam__''${var_dot_underscores//:/__}" 192 | options="''${contents#*\?}" 193 | if [[ ! "$options" == "$var" ]]; then 194 | if [[ "$(eval echo ' ''${'"$varname"'-null}')" == true ]]; then 195 | printf '%s' "''${options%:*}" 196 | else 197 | printf '%s' "''${options#*:}" 198 | fi 199 | else 200 | printf '%s' "$(eval echo '$'"$varname")" 201 | fi 202 | } 203 | ''; 204 | 205 | # Fake `opam list` using the `$OCAMLPATH` env var. 206 | # sed 1. split lines on ':' 207 | # -> /nix/store/yw6nnn1v9cx22ibg4d5ba1ff0zapl8ys-dune-3.14.2/lib/ocaml/5.1.1/site-lib 208 | # sed 2. strip '/nix/store/...' prefix 209 | # -> dune-3.14.2/lib/ocaml/5.1.1/site-lib 210 | # sed 3. strip '/lib/...' suffix 211 | # -> dune-3.14.2 212 | opamFakeList = '' 213 | opamFakeList() { 214 | echo "$OCAMLPATH" \ 215 | | sed 's/:/\n/g' \ 216 | | sed 's:^/nix/store/[a-z0-9]*-::' \ 217 | | sed 's:/.*$::' \ 218 | | sort \ 219 | | uniq 220 | } 221 | opamList() { 222 | echo -e '\e[31;1mopam-nix fake opam does not support "opam list"; for a human-readable package list, use "opam fake-list"\e[0;0m' 1>&2 223 | } 224 | ''; 225 | 226 | opamSubst = '' 227 | opamSubst() { 228 | printf "Substituting %s to %s\n" "$1" "$2" 1>&2 229 | TEMP="/tmp/opam-subst-$RANDOM$RANDOM$RANDOM" 230 | cp --no-preserve=all "$1" "$TEMP" 231 | substs="$(grep -o '%{[a-zA-Z0-9_:?+-]*}%' "$1")" 232 | shopt -u nullglob 233 | for subst in $substs; do 234 | var="$(echo "$subst")" 235 | sed -e "s@$var@$(evalOpamVar "$var")@" -i "$TEMP" 236 | done 237 | shopt -s nullglob 238 | sed -e 's/%%/%/g' "$TEMP" > "$2" 239 | rm "$TEMP" 240 | } 241 | ''; 242 | allEvalVars = defaultVars // pkgVars vars // vars // stubOutputs // deps.extraVars or { }; 243 | 244 | prepareEnvironment = '' 245 | opam__ocaml__version="''${opam__ocaml__version-${deps.ocaml.version}}" 246 | source ${toFile "set-vars.sh" (varsToShell allEvalVars)} 247 | source ${toFile "set-fallback-vars.sh" setFallbackDepVars} 248 | ${envToShell pkgdef.build-env or [ ]} 249 | ${evalOpamVar} 250 | ${opamSubst} 251 | ${opamFakeList} 252 | ''; 253 | 254 | # Some packages shell out to opam to do things. It's not great, but we need to work around that. 255 | fake-opam = deps.nixpkgs.writeShellScriptBin "opam" '' 256 | set -euo pipefail 257 | sourceRoot="" 258 | ${prepareEnvironment} 259 | args="$@" 260 | bailArgs() { 261 | echo -e "\e[31;1mopam-nix fake opam doesn't understand these arguments: $args\e[0;0m" 1>&2 262 | exit 1 263 | } 264 | case "$1" in 265 | --version) echo "2.0.0";; 266 | config) 267 | case "$2" in 268 | var) evalOpamVar "$3"; echo;; 269 | subst) opamSubst "$3.in" "$3";; 270 | *) bailArgs;; 271 | esac;; 272 | list) opamList;; 273 | fake-list) opamFakeList;; 274 | var) evalOpamVar "$2";; 275 | switch) 276 | case "$3" in 277 | show) echo "default";; 278 | *) bailArgs;; 279 | esac;; 280 | exec) 281 | shift 282 | if [[ "x$1" == "x--" ]]; then 283 | shift 284 | exec "$@" 285 | else 286 | exec "$@" 287 | fi;; 288 | *) bailArgs;; 289 | esac 290 | ''; 291 | 292 | messages = filter isString ( 293 | filterOptionList versionResolutionVars (flatten [ 294 | pkgdef.messages or [ ] 295 | pkgdef.post-messages or [ ] 296 | ]) 297 | ); 298 | 299 | traceAllMessages = val: foldl' (acc: x: trace "[opam-nix] ${name}: ${x}" acc) val messages; 300 | 301 | fetchExtraSources = concatStringsSep "\n" ( 302 | attrValues ( 303 | mapAttrs ( 304 | name: 305 | { src, checksum }: 306 | "cp ${ 307 | deps.nixpkgs.fetchurl ( 308 | { 309 | url = src; 310 | } 311 | // getHashes (if isList checksum then checksum else [ checksum ]) 312 | ) 313 | } ${escapeShellArg name}" 314 | ) pkgdef.extra-source.section or { } 315 | ) 316 | ); 317 | 318 | bz2Unpacker = deps.nixpkgs.writeTextFile { 319 | name = "bz2-unpacker"; 320 | text = '' 321 | unpackCmdHooks+=(_tryBz2) 322 | _tryBz2() { 323 | if ! [[ "$curSrc" =~ \.bz2$ ]]; then return 1; fi 324 | 325 | tar xf "$curSrc" --mode=+w --warning=no-timestamp 326 | } 327 | ''; 328 | destination = "/nix-support/setup-hook"; 329 | }; 330 | 331 | in 332 | { 333 | pname = traceAllMessages name; 334 | version = replaceStrings [ "~" ] [ "_" ] version; 335 | 336 | OPAM_PACKAGE_NAME = name; 337 | OPAM_PACKAGE_VERSION = version; 338 | 339 | buildInputs = extInputs ++ ocamlInputs; 340 | 341 | withFakeOpam = true; 342 | 343 | nativeBuildInputs = 344 | extInputs 345 | ++ ocamlInputs 346 | ++ optional fa.withFakeOpam [ fake-opam ] 347 | ++ optional (hasSuffix ".zip" archive) unzip 348 | ++ optional (hasSuffix ".bz2" archive) bz2Unpacker; 349 | 350 | strictDeps = true; 351 | 352 | doCheck = false; 353 | doDoc = false; 354 | 355 | inherit src; 356 | 357 | prePatch = '' 358 | ${prepareEnvironment} 359 | ${optionalString (pkgdef ? files) "cp -R ${pkgdef.files}/* ."} 360 | ${fetchExtraSources} 361 | for subst in ${toString (map escapeShellArg (patches ++ substs))}; do 362 | if [[ -f "$subst".in ]]; then 363 | opamSubst "$subst.in" "$subst" 364 | fi 365 | done 366 | ''; 367 | 368 | inherit patches; 369 | 370 | configurePhase = '' 371 | runHook preConfigure 372 | if [[ -z $dontPatchShebangsEarly ]]; then patchShebangs .; fi 373 | ${ 374 | if compareVersions' "geq" deps.ocaml.version "4.08" then 375 | ''export OCAMLTOP_INCLUDE_PATH="$OCAMLPATH"'' 376 | else 377 | '' 378 | for i in $(sed 's/:/ /g' <<< "$OCAMLPATH"); do 379 | [ -e "$i" ] && OCAMLPARAM=''${OCAMLPARAM-}''${OCAMLPARAM+,}I=$i 380 | done 381 | [ -n "$OCAMLPARAM" ] && export OCAMLPARAM=''${OCAMLPARAM},_ 382 | '' 383 | } 384 | ${optionalString deps.nixpkgs.stdenv.cc.isClang ''export NIX_CFLAGS_COMPILE="''${NIX_CFLAGS_COMPILE-} -Wno-error"''} 385 | export OCAMLFIND_DESTDIR="$out/lib/ocaml/''${opam__ocaml__version}/site-lib" 386 | export OCAMLLIBDIR="$OCAMLFIND_DESTDIR" 387 | OPAM_PACKAGE_NAME_="''${OPAM_PACKAGE_NAME//-/_}" 388 | export OPAM_PACKAGE_NAME_="''${OPAM_PACKAGE_NAME_//+/_}" 389 | export OPAM_PACKAGE_NAME 390 | export OPAM_PACKAGE_VERSION 391 | export OPAM_SWITCH_PREFIX="$out" 392 | source "${setup}" 393 | runHook postConfigure 394 | ''; 395 | 396 | buildPhase = '' 397 | runHook preBuild 398 | ${filterSectionInShell pkgdef.build or [ ]} 399 | runHook postBuild 400 | ''; 401 | 402 | # TODO: get rid of opam-installer and do everything with opam2json 403 | installPhase = '' 404 | runHook preInstall 405 | # Some installers expect the installation directories to be present 406 | mkdir -p "$OCAMLFIND_DESTDIR" "$OCAMLFIND_DESTDIR/stublibs" "$out/bin" "$out/share/man/man"{1,2,3,4,5,6,7,8,9} 407 | ${filterSectionInShell pkgdef.install or [ ]} 408 | if [[ -e "''${OPAM_PACKAGE_NAME}.install" ]]; then 409 | ${opam-installer}/bin/opam-installer "''${OPAM_PACKAGE_NAME}.install" --prefix="$out" --libdir="$OCAMLFIND_DESTDIR" 410 | fi 411 | runHook postInstall 412 | ''; 413 | 414 | preFixupPhases = [ 415 | "fixDumbPackagesPhase" 416 | "cleanupPhase" 417 | "nixSupportPhase" 418 | "removeOcamlReferencesPhase" 419 | ]; 420 | 421 | fixDumbPackagesPhase = '' 422 | # Some packages like to install to %{prefix}%/lib instead of %{lib}% 423 | if [[ -e "$out/lib/''${OPAM_PACKAGE_NAME}/META" ]] && [[ ! -e "$OCAMLFIND_DESTDIR/''${OPAM_PACKAGE_NAME}" ]]; then 424 | mv "$out/lib/''${OPAM_PACKAGE_NAME}" "$OCAMLFIND_DESTDIR" 425 | fi 426 | # Some packages like to install to %{libdir}% instead of %{libdir}%/%{name}% 427 | if [[ ! -d "$OCAMLFIND_DESTDIR/''${OPAM_PACKAGE_NAME}" ]] && [[ -e "$OCAMLFIND_DESTDIR/META" ]]; then 428 | mv "$OCAMLFIND_DESTDIR" "$NIX_BUILD_TOP/destdir" 429 | mkdir -p "$OCAMLFIND_DESTDIR" 430 | mv "$NIX_BUILD_TOP/destdir" "$OCAMLFIND_DESTDIR/''${OPAM_PACKAGE_NAME}" 431 | fi 432 | ''; 433 | 434 | doNixSupport = true; 435 | propagateInputs = true; 436 | exportSetupHook = true; 437 | 438 | cleanupPhase = '' 439 | pushd "$out" 440 | rmdir -p "bin" 2>/dev/null || true 441 | rmdir -p "$OCAMLFIND_DESTDIR/stublibs" 2>/dev/null || true 442 | rmdir -p "$OCAMLFIND_DESTDIR" 2>/dev/null || true 443 | rmdir -p "share/man/man*" 2>/dev/null || true 444 | rmdir -p "share/man" 2>/dev/null || true 445 | rmdir -p "share" 2>/dev/null || true 446 | popd 447 | for var in $(printenv | grep -o '^opam__'); do 448 | unset -- "''${var//=*}" 449 | done 450 | ''; 451 | 452 | nixSupportPhase = '' 453 | if [[ -n "$doNixSupport" ]]; then 454 | mkdir -p "$out/nix-support" 455 | 456 | touch "$out/nix-support/is-opam-nix-package" 457 | 458 | if [[ -n "$propagateInputs" ]]; then 459 | # Ocaml packages may expect that all their transitive dependencies are present :( 460 | # Propagate all our buildInputs, and all propagated inputs of our buildInputs. 461 | for input in $buildInputs $propagatedBuildInputs; do 462 | printf "$input\n" 463 | [ -f "$input/nix-support/is-opam-nix-package" ] || continue 464 | for subinput in $(cat "$input/nix-support/propagated-build-inputs"); do 465 | printf "$subinput\n" 466 | done 467 | done | sort | uniq | sed 's/$/ /g' > "$out/nix-support/propagated-build-inputs" 468 | 469 | for input in $nativeBuildInputs; do 470 | printf "$input\n" 471 | [ -f "$input/nix-support/is-opam-nix-package" ] || continue 472 | for subinput in $(cat "$input/nix-support/propagated-native-build-inputs"); do 473 | printf "$subinput\n" 474 | done 475 | done | sort | uniq | sed 's/$/ /g' > "$out/nix-support/propagated-native-build-inputs" 476 | fi 477 | 478 | if [[ -n "$exportSetupHook" ]]; then 479 | exportIfUnset() { 480 | sed -Ee 's/^([^=]*)=(.*)$/\1="''${\1-\2}"/' 481 | } 482 | 483 | ( set -o posix; set ) | grep "^opam__''${OPAM_PACKAGE_NAME_}__[a-zA-Z0-9_]*=" | exportIfUnset > "$out/nix-support/setup-hook" 484 | 485 | if [[ -d "$OCAMLFIND_DESTDIR" ]]; then 486 | printf '%s%s\n' ${escapeShellArg "export OCAMLPATH=\${OCAMLPATH-}\${OCAMLPATH:+:}"} "$OCAMLFIND_DESTDIR" >> $out/nix-support/setup-hook 487 | fi 488 | if [[ -d "$OCAMLFIND_DESTDIR/stublibs" ]]; then 489 | printf '%s%s\n' ${escapeShellArg "export CAML_LD_LIBRARY_PATH=\${CAML_LD_LIBRARY_PATH-}\${CAML_LD_LIBRARY_PATH:+:}"} "$OCAMLFIND_DESTDIR/stublibs" >> "$out/nix-support/setup-hook" 490 | fi 491 | printf '%s\n' ${ 492 | escapeShellArg (envToShell pkgdef.set-env.section or [ ]) 493 | } >> "$out/nix-support/setup-hook" 494 | 495 | if [[ -f "''${OPAM_PACKAGE_NAME}.config" ]]; then 496 | ${opam2json}/bin/opam2json "''${OPAM_PACKAGE_NAME}.config" | ${jq}/bin/jq \ 497 | '.variables | select (. != null) | .section | to_entries | .[] | (("opam__"+env["OPAM_PACKAGE_NAME"]+"__"+.key) | gsub("[+-]"; "_"))+"="+(.value | tostring | gsub("'"'"'"; "'"\\\\'"'") | gsub("\""; "\\\""))' -r \ 498 | | exportIfUnset \ 499 | >> "$out/nix-support/setup-hook" 500 | fi 501 | fi 502 | fi 503 | ''; 504 | 505 | removeOcamlReferences = false; 506 | 507 | removeOcamlReferencesPhase = '' 508 | if [[ -n "$removeOcamlReferences" ]] && [[ -d "$out/bin" ]] && command -v ocamlc; then 509 | echo "Stripping out references to ocaml compiler in binaries" 510 | ${removeReferencesTo}/bin/remove-references-to -t "$(dirname "$(dirname "$(command -v ocamlc)")")" $out/bin/* 511 | fi 512 | ''; 513 | 514 | passthru = { 515 | pkgdef = originalPkgdef; 516 | }; 517 | } 518 | ); 519 | 520 | } 521 | -------------------------------------------------------------------------------- /src/evaluator/default.nix: -------------------------------------------------------------------------------- 1 | lib: 2 | let 3 | inherit (builtins) 4 | elemAt 5 | replaceStrings 6 | head 7 | isString 8 | isList 9 | toJSON 10 | tail 11 | listToAttrs 12 | length 13 | attrValues 14 | mapAttrs 15 | concatStringsSep 16 | isBool 17 | isInt 18 | filter 19 | split 20 | foldl' 21 | match 22 | fromJSON 23 | nixVersion 24 | throw 25 | ; 26 | inherit (lib) 27 | splitString 28 | concatMap 29 | nameValuePair 30 | concatMapStringsSep 31 | all 32 | any 33 | zipListsWith 34 | optionalAttrs 35 | optional 36 | escapeShellArg 37 | hasInfix 38 | stringToCharacters 39 | flatten 40 | last 41 | warn 42 | path 43 | ; 44 | 45 | inherit (import ../lib.nix lib) md5sri; 46 | 47 | isImpure = builtins ? currentSystem; 48 | in 49 | rec { 50 | # Note: if you are using this evaluator directly, don't forget to source the setup 51 | setup = ./setup.sh; 52 | 53 | chrcmp = 54 | a: b: 55 | if a == b then 56 | 0 57 | else if a == "~" && b != "~" then 58 | (-1) 59 | else if a != "~" && b == "~" then 60 | 1 61 | else if a > b then 62 | 1 63 | else 64 | (-1); 65 | strcmp = 66 | a: b: 67 | let 68 | a' = stringToCharacters a ++ [ "" ]; 69 | b' = stringToCharacters b ++ [ "" ]; 70 | in 71 | head (filter (x: x != 0) (zipListsWith chrcmp a' b')); 72 | 73 | lexiCompare = 74 | a: b: 75 | if a == b then 76 | 0 77 | else if isString a && (hasInfix "~" a || hasInfix "~" b) then 78 | strcmp a b 79 | else if a > b then 80 | 1 81 | else 82 | (-1); 83 | 84 | trimZeroes = s: head (match ("[0]*([0-9]+)") s); 85 | 86 | compareVersions' = 87 | op: a: b: 88 | let 89 | prepareVersion = 90 | version: map (x: if isList x then fromJSON (trimZeroes (head x)) else x) (split "([0-9]+)" version); 91 | comp' = filter (x: x != 0) (zipListsWith lexiCompare (prepareVersion a) (prepareVersion b)); 92 | comp = if comp' == [ ] then 0 else head comp'; 93 | in 94 | if isNull a || isNull b then 95 | false 96 | else if op == "eq" then 97 | a == b 98 | else if op == "lt" then 99 | comp == -1 100 | else if op == "gt" then 101 | comp == 1 102 | else if op == "leq" then 103 | comp < 1 104 | else if op == "geq" then 105 | comp > -1 106 | else 107 | true; 108 | 109 | any' = pred: any (x: !isNull (pred x) && pred x); 110 | all' = pred: all (x: !isNull (pred x) && pred x); 111 | 112 | collectAllValuesFromOptionList = 113 | v: 114 | if isString v then 115 | [ v ] 116 | else if v ? conditions then 117 | collectAllValuesFromOptionList v.val 118 | else if v ? logop then 119 | collectAllValuesFromOptionList v.lhs ++ collectAllValuesFromOptionList v.rhs 120 | else if isList v then 121 | concatMap collectAllValuesFromOptionList v 122 | else if v ? group then 123 | concatMap collectAllValuesFromOptionList v.group 124 | else 125 | throw "unexpected dependency: ${toJSON v}"; 126 | 127 | functionArgsFor = 128 | pkgdef: 129 | let 130 | # Get _all_ dependencies mentioned in the opam file 131 | 132 | allDepends = collectAllValuesFromOptionList pkgdef.depends or [ ]; 133 | allDepopts = collectAllValuesFromOptionList pkgdef.depopts or [ ]; 134 | 135 | genArgs = deps: optional: listToAttrs (map (name: nameValuePair name optional) deps); 136 | in 137 | genArgs allDepends false // genArgs allDepopts true; 138 | 139 | envOpToShell = 140 | v@{ lhs, rhs, ... }: 141 | if v.relop or null == "eq" || v.env_update == "set" then 142 | "${lhs.id}=${escapeShellArg rhs}" 143 | else if v.env_update == "prepend" then 144 | "${lhs.id}=${escapeShellArg rhs}\${${lhs.id}+:}\${${lhs.id}-}" 145 | else if v.env_update == "append" then 146 | "${lhs.id}=\${${lhs.id}-}\${${lhs.id}+:}${escapeShellArg rhs}" 147 | else if v.env_update == "prepend_trailing" then 148 | "${lhs.id}=${escapeShellArg rhs}:\${${lhs.id}-}" 149 | else if v.env_update == "append_trailing" then 150 | "${lhs.id}=\${${lhs.id}-}:${escapeShellArg rhs}" 151 | else 152 | throw "Operation ${v.env_update} not implemented"; 153 | 154 | varToShellVar = 155 | var: 156 | let 157 | s = splitString ":" var; 158 | in 159 | concatMapStringsSep "__" (replaceStrings [ "-" "+" "." ] [ "_" "_" "_" ]) ([ "opam" ] ++ s); 160 | 161 | toShellString = 162 | { type, value }: 163 | if type == "string" then 164 | value 165 | else if type == "command" then 166 | "$(${value})" 167 | else if type == "optional" then 168 | ''$(if ${elemAt value 0}; then ${elemAt value 1}; fi)'' 169 | else 170 | throw "Can't convert ${type} to shell string"; 171 | toCommand = 172 | { type, value }: 173 | if type == "command" then 174 | value 175 | else if type == "string" then 176 | ''echo "${value}"'' 177 | else if type == "optional" then 178 | ''if ${elemAt value 0}; then ${elemAt value 1}; fi'' 179 | else 180 | throw "Can't convert ${type} to command"; 181 | toCondition = 182 | { type, value }@x: if type == "condition" then value else ''[[ "${toShellString x}" == true ]]''; 183 | # Used when combining a list of arguments into a command. 184 | toCommandArg = 185 | { type, value }: 186 | if type == "string" then 187 | ''args+=("${value}")'' 188 | else if type == "command" then 189 | ''args+=("$(${value})")'' 190 | else if type == "optional" then 191 | ''if ${elemAt value 0}; then args+=("$(${elemAt value 1})"); fi'' 192 | else 193 | throw "Can't convert ${type} to command arg"; 194 | 195 | filterPackageFormula = 196 | vars: 197 | let 198 | getVar = id: lib.attrByPath (splitString ":" id) null vars; 199 | 200 | getVersion = 201 | x: 202 | if x ? id then 203 | getVar x.id 204 | else if isString x then 205 | x 206 | else 207 | throw "Not a valid version description: ${toJSON x}"; 208 | 209 | toString' = 210 | value: 211 | if value == true then 212 | "true" 213 | else if value == false then 214 | "false" 215 | else 216 | toString value; 217 | 218 | checkVersionFormula = 219 | pkg: filter: 220 | if filter ? pfxop then 221 | if filter.pfxop == "not" then 222 | let 223 | r = checkVersionFormula pkg filter.arg; 224 | in 225 | if isNull r then null else !r 226 | else if filter.pfxop == "defined" then 227 | vars ? filter.arg.id 228 | else 229 | throw "Unknown pfxop ${filter.pfxop}" 230 | else if filter ? logop then 231 | if filter.logop == "and" then 232 | all' (checkVersionFormula pkg) [ 233 | filter.lhs 234 | filter.rhs 235 | ] 236 | else if filter.logop == "or" then 237 | any' (checkVersionFormula pkg) [ 238 | filter.lhs 239 | filter.rhs 240 | ] 241 | else 242 | throw "Unknown logop ${filter.logop}" 243 | else if filter ? prefix_relop then 244 | compareVersions' filter.prefix_relop (getVar pkg) (getVersion filter.arg) 245 | else if filter ? relop then 246 | compareVersions' filter.relop (toString' (getVersion filter.lhs)) ( 247 | toString' (getVersion filter.rhs) 248 | ) 249 | else if filter ? id then 250 | getVar filter.id 251 | else if isList filter then 252 | all' (checkVersionFormula pkg) filter 253 | else if filter ? group then 254 | all' (checkVersionFormula pkg) filter.group 255 | else 256 | throw "Couldn't understand package condition: ${toJSON filter}"; 257 | 258 | filterPackageFormulaRec = 259 | v: 260 | let 261 | lhs' = filterPackageFormulaRec v.lhs; 262 | rhs' = filterPackageFormulaRec v.rhs; 263 | in 264 | if v ? logop then 265 | if v.logop == "or" then 266 | if lhs' != [ ] then 267 | lhs' 268 | else if rhs' != [ ] then 269 | rhs' 270 | else 271 | [ ] 272 | else if v.logop == "and" then 273 | if lhs' != [ ] && rhs' != [ ] then 274 | flatten [ 275 | lhs' 276 | rhs' 277 | ] 278 | else 279 | [ ] 280 | else 281 | throw "Unknown logop ${v.logop}" 282 | else if v ? conditions then 283 | if all' (checkVersionFormula v.val) v.conditions then filterPackageFormulaRec v.val else [ ] 284 | else if isString v then 285 | if !isNull (getVar v) then v else [ ] 286 | else if isList v then 287 | map filterPackageFormulaRec v 288 | else if v ? group then 289 | flatten (map filterPackageFormulaRec v.group) 290 | else 291 | throw "Couldn't understand a part of filtered list: ${toJSON v}"; 292 | in 293 | v: flatten (filterPackageFormulaRec v); 294 | 295 | filterOptionList = 296 | vars: 297 | let 298 | getVar = id: lib.attrByPath (splitString ":" id) null vars; 299 | 300 | getVersion = 301 | x: 302 | if x ? id then 303 | getVar x.id 304 | else if isString x then 305 | x 306 | else 307 | throw "Not a valid version description: ${toJSON x}"; 308 | 309 | checkVersionFormula = 310 | pkg: filter: 311 | if filter ? pfxop then 312 | if filter.pfxop == "not" then 313 | let 314 | r = checkVersionFormula pkg filter.arg; 315 | in 316 | if isNull r then null else !r 317 | else if filter.pfxop == "defined" then 318 | vars ? filter.arg.id 319 | else 320 | throw "Unknown pfxop ${filter.pfxop}" 321 | else if filter ? logop then 322 | if filter.logop == "and" then 323 | all' (checkVersionFormula pkg) [ 324 | filter.lhs 325 | filter.rhs 326 | ] 327 | else if filter.logop == "or" then 328 | any' (checkVersionFormula pkg) [ 329 | filter.lhs 330 | filter.rhs 331 | ] 332 | else 333 | throw "Unknown logop ${filter.logop}" 334 | else if filter ? relop then 335 | compareVersions' filter.relop (getVersion filter.lhs) (getVersion filter.rhs) 336 | else if filter ? id then 337 | getVar filter.id 338 | else if isList filter then 339 | all' (checkVersionFormula pkg) filter 340 | else if filter ? group then 341 | all' (checkVersionFormula pkg) filter.group 342 | else 343 | throw "Couldn't understand option list condition: ${toJSON filter}"; 344 | 345 | filterOptionListRec = 346 | v: 347 | if v ? conditions then 348 | if all' (checkVersionFormula v.val) v.conditions then filterOptionListRec v.val else [ ] 349 | else if isString v then 350 | v 351 | else if isList v then 352 | map filterOptionListRec v 353 | else if v ? group then 354 | flatten (map filterOptionListRec v.group) 355 | else 356 | throw "Couldn't understand a part of filtered list: ${toJSON v}"; 357 | in 358 | v: flatten (filterOptionListRec v); 359 | 360 | pkgVarsFor = name: lib.mapAttrs' (var: nameValuePair "${name}:${var}"); 361 | 362 | varsToShell = 363 | vars: 364 | let 365 | v = attrValues ( 366 | mapAttrs (name: value: '' 367 | ${varToShellVar name}="''${${varToShellVar name}-${toJSON value}}" 368 | '') vars 369 | ); 370 | in 371 | concatStringsSep "" v; 372 | 373 | envToShell = 374 | env: concatMapStringsSep "\n" envOpToShell (flatten (if isList env then env else [ env ])); 375 | 376 | filterOptionListInShell = 377 | level: val: 378 | if val ? id then 379 | let 380 | s = splitString ":" val.id; 381 | pkgs = splitString "+" (head s); 382 | isEnable = last s == "enable"; 383 | trueish = if isEnable then "enable" else "true"; 384 | falseish = if isEnable then "disable" else "false"; 385 | in 386 | { 387 | type = "string"; 388 | value = 389 | if length pkgs == 1 then 390 | "\${${varToShellVar val.id}}" 391 | else 392 | "$(if ${ 393 | concatMapStringsSep " && " ( 394 | pkg: "[[ \${${varToShellVar (concatStringsSep ":" ([ pkg ] ++ tail s))}} == ${trueish} ]]" 395 | ) pkgs 396 | }; then echo ${trueish}; else echo ${falseish}; fi)"; 397 | } 398 | else if val ? relop || val ? logop then 399 | { 400 | type = "condition"; 401 | value = 402 | let 403 | op = val.relop or val.logop; 404 | lhsS = toShellString (filterOptionListInShell level val.lhs); 405 | rhsS = toShellString (filterOptionListInShell level val.rhs); 406 | lhsC = toCondition (filterOptionListInShell level val.lhs); 407 | rhsC = toCondition (filterOptionListInShell level val.rhs); 408 | in 409 | if op == "eq" then 410 | ''[ "$(compareVersions "${lhsS}" "${rhsS}")" = eq ]'' 411 | else if op == "neq" then 412 | ''[ ! "$(compareVersions "${lhsS}" "${rhsS}")" = eq ]'' 413 | else if op == "gt" then 414 | ''[ "$(compareVersions "${lhsS}" "${rhsS}")" = gt ]'' 415 | else if op == "lt" then 416 | ''[ "$(compareVersions "${lhsS}" "${rhsS}")" = lt ]'' 417 | else if op == "geq" then 418 | ''[ ! "$(compareVersions "${lhsS}" "${rhsS}")" = lt ]'' 419 | else if op == "leq" then 420 | ''[ ! "$(compareVersions "${lhsS}" "${rhsS}")" = gt ]'' 421 | else if op == "and" then 422 | "${lhsC} && ${rhsC}" 423 | else if op == "or" then 424 | "${lhsC} || ${rhsC}" 425 | else 426 | throw "Unknown op ${op}"; 427 | } 428 | else if val ? pfxop then 429 | { 430 | type = "condition"; 431 | value = 432 | if val.pfxop == "not" then 433 | "! ${toCondition (filterOptionListInShell level val.arg)}" 434 | # else if val.pfxop == "defined" then 435 | # "[[ -n ${val.arg.} ]]" 436 | else 437 | throw "Unknown pfxop ${val.pfxop}"; 438 | } 439 | else if val == [ ] then 440 | { 441 | type = "command"; 442 | value = ":"; 443 | } 444 | else if isList val then 445 | { 446 | # Build the argument list as an array to properly remove arguments 447 | # disabled by a condition. [toCommandArg] implements the convention. 448 | type = "command"; 449 | value = 450 | if level == 1 then 451 | '' 452 | args=() 453 | ${concatMapStringsSep "\n" (part: toCommandArg (filterOptionListInShell (level + 1) part)) val} 454 | "''${args[@]}"'' 455 | else if level == 0 then 456 | concatMapStringsSep "\n" (part: toCommand (filterOptionListInShell (level + 1) part)) val 457 | else 458 | throw "Level too big"; 459 | } 460 | else if val ? conditions then 461 | { 462 | type = "optional"; 463 | value = [ 464 | (concatMapStringsSep " && " (x: toCondition (filterOptionListInShell level x)) val.conditions) 465 | (toCommand (filterOptionListInShell level val.val)) 466 | ]; 467 | } 468 | else if isString val then 469 | { 470 | type = "string"; 471 | value = interpolateStringsRec val; 472 | } 473 | else if val ? group then 474 | filterOptionListInShell level (head val.group) 475 | else 476 | throw "Can't convert ${toJSON val} to shell commands"; 477 | 478 | filterSectionInShell = 479 | section: 480 | let 481 | val = x: x.val or x; 482 | normalize = 483 | section: 484 | if !isList (val section) then 485 | [ [ section ] ] 486 | else if (val section) == [ ] || !isList (val (head (val section))) then 487 | [ section ] 488 | else 489 | section; 490 | s = filterOptionListInShell 0 (normalize section); 491 | in 492 | toCommand s; 493 | 494 | interpolateStringsRec = 495 | val: 496 | if isString val then 497 | interpolateString val 498 | else if isList val then 499 | map interpolateStringsRec val 500 | else if isBool val || isInt val then 501 | toString' val 502 | else 503 | val; 504 | 505 | toString' = 506 | v: 507 | if isString v then 508 | v 509 | else if isInt v then 510 | toString v 511 | else if isBool v then 512 | if v then "true" else "false" 513 | else if isNull v then 514 | "" 515 | else 516 | throw "Don't know how to toString ${toJSON v}"; 517 | 518 | # FIXME this should be implemented correctly and not using regex 519 | interpolateString = 520 | s: 521 | let 522 | pieces = filter isString (split "([%][{]|[}][%])" s); 523 | result = 524 | foldl' 525 | ( 526 | { i, result }: 527 | piece: { 528 | i = !i; 529 | result = result + (if i then toShellString (filterOptionListInShell 2 { id = piece; }) else piece); 530 | } 531 | ) 532 | { 533 | i = false; 534 | result = ""; 535 | } 536 | pieces; 537 | in 538 | if length pieces == 1 then s else result.result; 539 | 540 | tryHash = 541 | method: c: 542 | 543 | let 544 | m = match "${method}=(.*)" c; 545 | in 546 | optional (!isNull m) { ${method} = head m; }; 547 | 548 | # md5 is special in two ways: 549 | # nixpkgs only accepts it as an SRI, 550 | # and checksums without an explicit algo are assumed to be md5 in opam 551 | trymd5 = 552 | c: 553 | let 554 | m = match "md5=(.*)" c; 555 | m' = match "([0-9a-f]{32})" c; 556 | success = md5: [ { hash = md5sri (head md5); } ]; 557 | in 558 | if !isNull m then 559 | success m 560 | else if !isNull m' then 561 | success m' 562 | else 563 | [ ]; 564 | 565 | getHashes = 566 | checksums: 567 | head (concatMap (x: tryHash "sha512" x ++ tryHash "sha256" x ++ trymd5 x) checksums ++ [ { } ]); 568 | 569 | parseUrl = 570 | url: 571 | let 572 | # Modified from https://www.rfc-editor.org/rfc/rfc3986#appendix-B 573 | m = match "^((([^:/?#+]+)[+]?([^:/?#]+)?):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?" url; 574 | in 575 | { 576 | scheme = elemAt m 1; 577 | proto = elemAt m 2; 578 | transport = elemAt m 3; 579 | authority = elemAt m 5; 580 | path = elemAt m 6; 581 | query = elemAt m 8; 582 | fragment = elemAt m 10; 583 | }; 584 | 585 | fetchGitURL = 586 | url: 587 | let 588 | isRev = s: !isNull (match "[0-9a-f]{40}" s); 589 | hasRev = (!isNull url.fragment) && isRev url.fragment; 590 | optionalRev = optionalAttrs hasRev { rev = url.fragment; }; 591 | refsOrWarn = 592 | if (!isNull url.fragment) && !hasRev then 593 | { 594 | ref = url.fragment; 595 | } 596 | else if lib.versionAtLeast nixVersion "2.4" then 597 | { 598 | allRefs = true; 599 | } 600 | else 601 | warn 602 | "[opam-nix] Nix version is too old for allRefs = true; fetching a repository may fail if the commit is on a non-master branch" 603 | { }; 604 | gitUrl = 605 | with url; 606 | (if isNull transport then "" else "${transport}://") 607 | + authority 608 | + url.path 609 | + (if isNull query then "" else "?${query}"); 610 | path = 611 | (builtins.fetchGit ( 612 | { 613 | url = gitUrl; 614 | submodules = true; 615 | } 616 | // refsOrWarn 617 | // optionalRev 618 | )) 619 | // { 620 | url = gitUrl; 621 | }; 622 | in 623 | if !hasRev && !isImpure then 624 | throw "[opam-nix] a git dependency without an explicit sha1 is not supported in pure evaluation mode; try with --impure" 625 | else 626 | path; 627 | 628 | fetchWithoutChecksum = 629 | url: project: 630 | let 631 | u = parseUrl url; 632 | in 633 | # git://git@domain:path/to/repo is interpreted as ssh, hence drop the git:// 634 | if u.proto == "git" then 635 | fetchGitURL u 636 | else if u.scheme == "http" || u.scheme == "https" then 637 | builtins.fetchTarball url 638 | # if no protocol assume a local file path 639 | else if 640 | u.scheme == null 641 | && 642 | # absolute path 643 | !path.subpath.isValid url 644 | then 645 | /. + url 646 | else if u.scheme == null && project != null then 647 | # relative path (note '..' is not accepted) 648 | path.append project url 649 | else 650 | throw "[opam-nix] URL scheme '${u.scheme}' is not yet supported"; 651 | 652 | getUrl = 653 | pkgs: pkgdef: 654 | let 655 | hashes = 656 | if pkgdef.url.section ? checksum then 657 | if isList pkgdef.url.section.checksum then 658 | getHashes pkgdef.url.section.checksum 659 | else 660 | getHashes [ pkgdef.url.section.checksum ] 661 | else 662 | { }; 663 | archive = pkgdef.url.section.src or pkgdef.url.section.archive or ""; 664 | src = 665 | if pkgdef ? url then 666 | # Default unpacker doesn't support .zip 667 | if hashes == { } then 668 | fetchWithoutChecksum archive null 669 | else 670 | pkgs.fetchurl ({ url = archive; } // hashes) 671 | else 672 | pkgdef.src or pkgs.pkgsBuildBuild.emptyDirectory; 673 | in 674 | { 675 | inherit archive src; 676 | }; 677 | 678 | } 679 | -------------------------------------------------------------------------------- /src/evaluator/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Compare versions according to opam's version comparison 4 | compareVersions() { 5 | if [ "$1" = "$2" ]; then printf eq; return 0; fi 6 | greater="$( ( printf "%s\n" "$1"; printf "%s\n" "$2" ) | sort -V | head -n1)" 7 | if [ "$greater" = "$1" ]; then printf gt 8 | elif [ "$greater" = "$2" ]; then printf lt 9 | else return 1 10 | fi 11 | } 12 | -------------------------------------------------------------------------------- /src/global-variables.nix: -------------------------------------------------------------------------------- 1 | hostPlatform: { 2 | opam-version = "2.0"; 3 | root = "/tmp/opam"; 4 | jobs = "$NIX_BUILD_CORES"; 5 | make = "make"; 6 | arch = if hostPlatform.uname.processor == "aarch64" then "arm64" else hostPlatform.uname.processor; 7 | os = 8 | if hostPlatform.isDarwin then 9 | "macos" 10 | else if hostPlatform.isLinux then 11 | "linux" 12 | else 13 | throw "${hostPlatform.uname.system} not supported"; 14 | os-distribution = if hostPlatform.isDarwin then "homebrew" else "debian"; 15 | os-family = if hostPlatform.isDarwin then "homebrew" else "debian"; # There are very few os-distribution = nixos packages 16 | os-version = "system"; 17 | ocaml-native = true; 18 | } 19 | -------------------------------------------------------------------------------- /src/lib.nix: -------------------------------------------------------------------------------- 1 | lib: 2 | 3 | let 4 | inherit (lib) 5 | stringToCharacters 6 | drop 7 | concatMap 8 | optionals 9 | attrValues 10 | converge 11 | filterAttrsRecursive 12 | nameValuePair 13 | ; 14 | inherit (builtins) 15 | elemAt 16 | length 17 | foldl' 18 | elem 19 | compareVersions 20 | listToAttrs 21 | ; 22 | 23 | in 24 | rec { 25 | base16digits = rec { 26 | "0" = 0; 27 | "1" = 1; 28 | "2" = 2; 29 | "3" = 3; 30 | "4" = 4; 31 | "5" = 5; 32 | "6" = 6; 33 | "7" = 7; 34 | "8" = 8; 35 | "9" = 9; 36 | "a" = 10; 37 | "b" = 11; 38 | "c" = 12; 39 | "d" = 13; 40 | "e" = 14; 41 | "f" = 15; 42 | A = a; 43 | B = b; 44 | C = c; 45 | D = d; 46 | E = e; 47 | F = f; 48 | }; 49 | 50 | base64digits = stringToCharacters "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 51 | 52 | mod = a: b: a - (a / b) * b; 53 | 54 | base16tobase64 = 55 | s: 56 | let 57 | chars = stringToCharacters s; 58 | go = 59 | x: 60 | let 61 | get16 = n: base16digits.${elemAt x n}; 62 | a = get16 2; 63 | b = (get16 1) * 16; 64 | c = (get16 0) * 256; 65 | sum = 66 | if length x > 2 then 67 | c + b + a 68 | else if length x == 2 then 69 | c + b 70 | else if length x == 1 then 71 | c 72 | else 73 | 0; 74 | get = elemAt base64digits; 75 | value = get (sum / 64) + get (mod sum 64); 76 | in 77 | (if length x > 0 then value else "") + (if length x > 2 then go (drop 3 x) else ""); 78 | in 79 | go chars; 80 | 81 | md5sri = md5: "md5-${base16tobase64 md5}=="; 82 | 83 | filterOutEmpty = converge (filterAttrsRecursive (_: v: v != { })); 84 | 85 | listToAttrsBy = by: list: listToAttrs (map (x: nameValuePair x.${by} x) list); 86 | } 87 | -------------------------------------------------------------------------------- /src/opam.nix: -------------------------------------------------------------------------------- 1 | args: 2 | let 3 | inherit (builtins) 4 | readDir 5 | mapAttrs 6 | concatStringsSep 7 | isString 8 | isList 9 | attrValues 10 | filter 11 | head 12 | foldl' 13 | fromJSON 14 | listToAttrs 15 | readFile 16 | toFile 17 | isAttrs 18 | pathExists 19 | toJSON 20 | deepSeq 21 | length 22 | sort 23 | concatMap 24 | attrNames 25 | match 26 | ; 27 | 28 | bootstrapPackages = args.pkgs; 29 | 30 | inherit (bootstrapPackages) lib; 31 | 32 | inherit (lib) 33 | splitString 34 | tail 35 | nameValuePair 36 | zipAttrsWith 37 | collect 38 | concatLists 39 | filterAttrsRecursive 40 | fileContents 41 | pipe 42 | makeScope 43 | optionalAttrs 44 | hasSuffix 45 | converge 46 | mapAttrsRecursive 47 | composeManyExtensions 48 | removeSuffix 49 | optionalString 50 | last 51 | init 52 | recursiveUpdate 53 | foldl 54 | optionals 55 | mapAttrsToList 56 | remove 57 | findSingle 58 | warn 59 | ; 60 | 61 | inherit (import ./evaluator lib) 62 | compareVersions' 63 | getUrl 64 | fetchWithoutChecksum 65 | ; 66 | 67 | inherit (bootstrapPackages) 68 | runCommand 69 | linkFarm 70 | symlinkJoin 71 | opam2json 72 | opam 73 | ; 74 | 75 | # Pkgdef -> Derivation 76 | builder = import ./builder.nix bootstrapPackages.lib; 77 | 78 | contentAddressedIFD = dir: deepSeq (readDir dir) (/. + builtins.unsafeDiscardStringContext dir); 79 | 80 | global-variables = import ./global-variables.nix bootstrapPackages.stdenv.hostPlatform; 81 | 82 | defaultEnv = { 83 | inherit (global-variables) 84 | arch 85 | os 86 | os-family 87 | os-distribution 88 | ; 89 | sys-ocaml-version = bootstrapPackages.ocaml-ng.ocamlPackages_latest.ocaml.version; 90 | }; 91 | defaultResolveArgs = { 92 | env = defaultEnv; 93 | criteria = "-count[avoid-version,request],-count[version-lag,request],-count[version-lag,changed]"; 94 | depopts = true; 95 | best-effort = false; 96 | dev = false; 97 | with-test = false; 98 | with-doc = false; 99 | }; 100 | 101 | mergeSortVersions = zipAttrsWith (_: sort (compareVersions' "lt")); 102 | 103 | readFileContents = 104 | { 105 | files ? bootstrapPackages.emptyDirectory, 106 | ... 107 | }@def: 108 | (builtins.removeAttrs def [ "files" ]) 109 | // { 110 | files-contents = mapAttrs (name: _: readFile (files + "/${name}")) (readDir files); 111 | }; 112 | 113 | writeFileContents = 114 | { 115 | name ? "opam", 116 | files-contents ? { }, 117 | ... 118 | }@def: 119 | (builtins.removeAttrs def [ "files-contents" ]) 120 | // optionalAttrs (files-contents != { }) { 121 | files = symlinkJoin { 122 | name = "${name}-files"; 123 | paths = (attrValues (mapAttrs bootstrapPackages.writeTextDir files-contents)); 124 | }; 125 | }; 126 | 127 | eraseStoreReferences = 128 | def: 129 | (builtins.removeAttrs def [ 130 | "repo" 131 | "opamFile" 132 | "src" 133 | ]) 134 | // optionalAttrs (def ? src.url) { 135 | # Keep srcs which can be fetched 136 | src = { 137 | inherit (def.src) url rev subdir; 138 | hash = def.src.narHash; 139 | }; 140 | }; 141 | 142 | # Note: there can only be one version of the package present in packagedefs we're working on 143 | injectSources = 144 | sourceMap: def: 145 | if sourceMap ? ${def.name} then 146 | def // { src = sourceMap.${def.name}; } 147 | else if def ? src then 148 | def 149 | // { 150 | src = (bootstrapPackages.fetchgit { inherit (def.src) url rev hash; }) + def.src.subdir; 151 | } 152 | else 153 | def; 154 | namePathPair = name: path: { inherit name path; }; 155 | in 156 | rec { 157 | 158 | splitNameVer = 159 | nameVer: 160 | let 161 | nv = nameVerToValuePair nameVer; 162 | in 163 | { 164 | inherit (nv) name; 165 | version = nv.value; 166 | }; 167 | 168 | nameVerToValuePair = 169 | nameVer: 170 | let 171 | split = splitString "." nameVer; 172 | in 173 | nameValuePair (head split) (concatStringsSep "." (tail split)); 174 | 175 | # Read 'url' and 'checksum' from a separate file called 'url' if one exists. 176 | # This supports the older opam repository format where this information was 177 | # split out into a separate file rather than being part of the main `opam` 178 | # file. 179 | legacyUrlFileContents = 180 | opamFile: 181 | let 182 | urlPath = "${dirOf opamFile}/url"; 183 | in 184 | if pathExists urlPath then 185 | let 186 | json = runCommand "url.json" { 187 | preferLocalBuild = true; 188 | allowSubstitutes = false; 189 | } "${opam2json}/bin/opam2json ${urlPath} > $out"; 190 | in 191 | { 192 | url = { 193 | section = fromJSON (readFile json); 194 | }; 195 | } 196 | else 197 | { }; 198 | 199 | # Path -> {...} 200 | importOpam = 201 | opamFile: 202 | let 203 | isStorePath = p: !isNull (match "[0-9a-z]{32}-.*" p); 204 | dir = baseNameOf (dirOf opamFile); 205 | basename = baseNameOf opamFile; 206 | name = 207 | if !isStorePath basename && hasSuffix ".opam" basename then 208 | basename 209 | else if !isStorePath basename && !isStorePath dir then 210 | "${dir}.opam" 211 | else 212 | "opam"; 213 | json = runCommand "${name}.json" { 214 | preferLocalBuild = true; 215 | allowSubstitutes = false; 216 | } "${opam2json}/bin/opam2json ${opamFile} > $out"; 217 | opamContents = fromJSON (readFile json); 218 | 219 | in 220 | if (opamContents ? url) then 221 | opamContents 222 | else 223 | let 224 | urlFileContents = legacyUrlFileContents opamFile; 225 | in 226 | opamContents // urlFileContents; 227 | 228 | fromOpam = opamText: importOpam (toFile "opam" opamText); 229 | 230 | # Path -> Derivation 231 | opam2nix = 232 | { 233 | src, 234 | opamFile ? src + "/${name}.opam", 235 | name ? null, 236 | version ? null, 237 | resolveEnv ? { }, 238 | }: 239 | builder ({ inherit src name version; } // importOpam opamFile) resolveEnv; 240 | 241 | readDirRecursive = 242 | dir: 243 | mapAttrs (name: type: if type == "directory" then readDirRecursive "${dir}/${name}" else type) ( 244 | readDir dir 245 | ); 246 | 247 | listRepo = 248 | repo: 249 | optionalAttrs (pathExists (repo + "/packages")) ( 250 | mergeSortVersions ( 251 | map (p: listToAttrs [ (nameVerToValuePair p) ]) ( 252 | concatMap attrNames (attrValues (readDirRecursive (repo + "/packages"))) 253 | ) 254 | ) 255 | ); 256 | 257 | opamListToQuery = list: listToAttrs (map nameVerToValuePair list); 258 | 259 | opamList = 260 | repo: resolveArgs: packages: 261 | let 262 | pkgRequest = 263 | name: version: 264 | if version == "*" then 265 | name 266 | else if isNull version then 267 | (warn ''[opam-nix] Using `null' as a version in a query is deprecated, because it is unintuitive to the user. Use `"*"' instead.'' name) 268 | else 269 | "${name}.${version}"; 270 | 271 | toString' = x: if isString x then x else toJSON x; 272 | 273 | args = recursiveUpdate defaultResolveArgs resolveArgs; 274 | 275 | environment = concatStringsSep "," ( 276 | attrValues (mapAttrs (name: value: "${name}=${toString' value}") args.env) 277 | ); 278 | 279 | query = concatStringsSep "," (attrValues (mapAttrs pkgRequest packages)); 280 | 281 | resolve-drv = 282 | runCommand "resolve" 283 | { 284 | nativeBuildInputs = [ 285 | opam 286 | bootstrapPackages.ocaml 287 | ]; 288 | OPAMCLI = "2.0"; 289 | } 290 | '' 291 | export OPAMROOT=$NIX_BUILD_TOP/opam 292 | 293 | cd ${repo} 294 | opam admin list \ 295 | --resolve=${query} \ 296 | --short \ 297 | --columns=package \ 298 | ${optionalString args.depopts "--depopts"} \ 299 | ${optionalString args.dev "--dev"} \ 300 | ${optionalString args.with-test "--with-test"} \ 301 | ${optionalString args.with-doc "--doc"} \ 302 | ${optionalString args.best-effort "--best-effort"} \ 303 | ${optionalString (!isNull args.env) "--environment '${environment}'"} \ 304 | ${optionalString (!isNull args.criteria) "--criteria='${args.criteria}'"} \ 305 | | tee $out 306 | ''; 307 | solution = fileContents resolve-drv; 308 | 309 | lines = s: splitString "\n" s; 310 | 311 | in 312 | lines solution; 313 | 314 | filterOpamFiles = 315 | files: 316 | converge (filterAttrsRecursive (_: v: v != { })) ( 317 | filterAttrsRecursive ( 318 | name: value: isAttrs value || ((value == "regular" || value == "symlink") && hasSuffix "opam" name) 319 | ) files 320 | ); 321 | 322 | constructOpamRepo = 323 | root: opamFiles: 324 | let 325 | packages = concatLists ( 326 | collect isList ( 327 | mapAttrsRecursive (path': _: [ 328 | rec { 329 | fileName = last path'; 330 | dirName = splitNameVer (if init path' != [ ] then last (init path') else ""); 331 | parsedOPAM = importOpam opamFile; 332 | name = 333 | parsedOPAM.name 334 | or (if hasSuffix ".opam" fileName then removeSuffix ".opam" fileName else dirName.name); 335 | 336 | version = parsedOPAM.version or (if dirName.version != "" then dirName.version else "dev"); 337 | subdir = 338 | "/" 339 | + concatStringsSep "/" ( 340 | let 341 | i = init path'; 342 | in 343 | if length i > 0 && last i == "opam" then init i else i 344 | ); 345 | source = root + subdir; 346 | opamFile = "${root + ("/" + (concatStringsSep "/" path'))}"; 347 | opamFileContents = readFile opamFile; 348 | } 349 | ]) opamFiles 350 | ) 351 | ); 352 | repo-description = namePathPair "repo" (toFile "repo" ''opam-version: "2.0"''); 353 | opamFileLinks = map ( 354 | { 355 | name, 356 | version, 357 | opamFile, 358 | ... 359 | }: 360 | namePathPair "packages/${name}/${name}.${version}/opam" opamFile 361 | ) packages; 362 | pkgdefs = foldl ( 363 | acc: x: 364 | recursiveUpdate acc { 365 | ${x.name} = { 366 | ${x.version} = x.parsedOPAM; 367 | }; 368 | } 369 | ) { } packages; 370 | sourceMap = foldl ( 371 | acc: x: 372 | recursiveUpdate acc { 373 | ${x.name} = { 374 | ${x.version} = (optionalAttrs (builtins.isAttrs root) root) // { 375 | inherit (x) subdir; 376 | outPath = contentAddressedIFD x.source; 377 | }; 378 | }; 379 | } 380 | ) { } packages; 381 | repo = linkFarm "opam-repo" ([ repo-description ] ++ opamFileLinks); 382 | in 383 | repo // { passthru = { inherit sourceMap pkgdefs; }; }; 384 | 385 | makeOpamRepo' = recursive: if recursive then makeOpamRepoRec else makeOpamRepo; 386 | 387 | makeOpamRepo = 388 | dir: 389 | let 390 | contents = readDir dir; 391 | contents' = ( 392 | contents 393 | // optionalAttrs (contents.opam or null == "directory") { 394 | opam = readDir "${dir}/opam"; 395 | } 396 | ); 397 | in 398 | constructOpamRepo dir (filterOpamFiles contents'); 399 | 400 | makeOpamRepoRec = dir: constructOpamRepo dir (filterOpamFiles (readDirRecursive dir)); 401 | 402 | findPackageInRepo = 403 | name: version: repo: 404 | let 405 | pkgDirVariants = [ 406 | (repo + "/packages/${name}.${version}") 407 | (repo + "/packages/${name}/${name}.${version}") 408 | ]; 409 | 410 | headOrNull = lst: if length lst == 0 then null else head lst; 411 | 412 | pkgDir = headOrNull (filter (pathExists) pkgDirVariants); 413 | in 414 | pkgDir; 415 | 416 | # FIXME: if the repo is formatted like packages/name.version, version defaulting will not work 417 | filterOpamRepo = 418 | packages: repo: 419 | linkFarm "opam-repo" ( 420 | [ (namePathPair "repo" "${repo}/repo") ] 421 | ++ attrValues ( 422 | mapAttrs ( 423 | name: version: 424 | let 425 | defaultPath = "${repo}/packages/${name}/${head (attrNames (readDir "${repo}/packages/${name}"))}"; 426 | in 427 | if version == "*" || isNull version then 428 | namePathPair "packages/${name}/${name}.dev" defaultPath 429 | else 430 | namePathPair "packages/${name}/${name}.${version}" ( 431 | let 432 | path = findPackageInRepo name version repo; 433 | in 434 | if !isNull path then path else defaultPath 435 | ) 436 | ) packages 437 | ) 438 | ) 439 | // optionalAttrs (repo ? passthru) { 440 | passthru = 441 | let 442 | pickRelevantVersions = 443 | from: 444 | mapAttrs (name: version: { 445 | ${if version == "*" || isNull version then "dev" else version} = 446 | if version == "*" || isNull version then 447 | head (attrValues from.${name}) 448 | else 449 | from.${name}.${version} or (head (attrValues from.${name})); 450 | }) packages; 451 | in 452 | repo.passthru 453 | // mapAttrs (_: pickRelevantVersions) { 454 | inherit (repo.passthru) sourceMap pkgdefs; 455 | }; 456 | 457 | }; 458 | 459 | queryToDefs = 460 | repos: packages: 461 | let 462 | findPackage = 463 | name: version: 464 | let 465 | pkgDir = findPackageInRepo name version; 466 | 467 | filesPath = contentAddressedIFD (pkgDir repo + "/files"); 468 | repos' = filter (repo: repo ? passthru.pkgdefs.${name}.${version} || !isNull (pkgDir repo)) repos; 469 | repo = 470 | if length repos' > 0 then 471 | head repos' 472 | else 473 | throw "[opam-nix] Could not find package ${name}.${version} in any repository. Checked:\n - ${concatStringsSep "\n - " repos}"; 474 | isLocal = repo ? passthru.sourceMap; 475 | in 476 | { 477 | opamFile = pkgDir repo + "/opam"; 478 | inherit 479 | name 480 | version 481 | isLocal 482 | repo 483 | ; 484 | } 485 | // optionalAttrs (pathExists (pkgDir repo + "/files")) { 486 | files = filesPath; 487 | } 488 | // optionalAttrs isLocal { 489 | src = repo.passthru.sourceMap.${name}.${version}; 490 | pkgdef = repo.passthru.pkgdefs.${name}.${version}; 491 | }; 492 | 493 | packageFiles = mapAttrs findPackage packages; 494 | in 495 | mapAttrs ( 496 | _: 497 | { 498 | opamFile, 499 | name, 500 | version, 501 | ... 502 | }@args: 503 | (builtins.removeAttrs args [ "pkgdef" ]) // args.pkgdef or (importOpam opamFile) 504 | ) packageFiles; 505 | 506 | callPackageWith = 507 | autoArgs: fn: args: 508 | let 509 | f = 510 | if lib.isAttrs fn then 511 | fn 512 | else if lib.isFunction fn then 513 | fn 514 | else 515 | import fn; 516 | auto = builtins.intersectAttrs (f.__functionArgs or (builtins.functionArgs f)) autoArgs; 517 | in 518 | lib.makeOverridable f (auto // args); 519 | 520 | defsToScope = 521 | pkgs: resolveEnv: defs: 522 | makeScope callPackageWith ( 523 | self: 524 | (mapAttrs (name: pkg: self.callPackage (builder pkg resolveEnv) { }) defs) 525 | // { 526 | nixpkgs = pkgs.extend (_: _: { inherit opam2json; }); 527 | } 528 | ); 529 | 530 | defaultOverlay = import ./overlays/ocaml.nix; 531 | staticOverlay = import ./overlays/ocaml-static.nix; 532 | darwinOverlay = import ./overlays/ocaml-darwin.nix; 533 | opamRepository = args.opam-repository; 534 | opamOverlays = args.opam-overlays; 535 | mirageOpamOverlays = args.mirage-opam-overlays; 536 | 537 | __overlays = [ 538 | ( 539 | final: prev: 540 | defaultOverlay final prev 541 | // optionalAttrs prev.nixpkgs.stdenv.hostPlatform.isStatic (staticOverlay final prev) 542 | // optionalAttrs prev.nixpkgs.stdenv.hostPlatform.isDarwin (darwinOverlay final prev) 543 | ) 544 | ]; 545 | 546 | applyOverlays = overlays: scope: scope.overrideScope (composeManyExtensions overlays); 547 | 548 | applyChecksDocs = 549 | { 550 | with-test ? defaultResolveArgs.with-test, 551 | with-doc ? defaultResolveArgs.with-doc, 552 | ... 553 | }: 554 | query: scope: 555 | scope.overrideScope ( 556 | _: prev: 557 | mapAttrs ( 558 | name: _: 559 | prev.${name}.overrideAttrs (_: { 560 | doCheck = with-test; 561 | doDoc = with-doc; 562 | }) 563 | ) query 564 | ); 565 | 566 | joinRepos = 567 | repos: 568 | if length repos == 0 then 569 | runCommand "empty-repo" { } "mkdir -p $out/packages" 570 | else if length repos == 1 then 571 | head repos 572 | else 573 | symlinkJoin { 574 | name = "opam-repo"; 575 | paths = repos; 576 | }; 577 | 578 | materialize = 579 | { 580 | repos ? [ opamRepository ], 581 | resolveArgs ? { }, 582 | regenCommand ? null, 583 | }: 584 | query: 585 | pipe query [ 586 | (opamList (joinRepos repos) resolveArgs) 587 | (opamListToQuery) 588 | (queryToDefs repos) 589 | 590 | (mapAttrs (_: eraseStoreReferences)) 591 | (mapAttrs (_: readFileContents)) 592 | (d: d // { __opam_nix_regen = regenCommand; }) 593 | (d: d // { __opam_nix_env = resolveArgs.env or { }; }) 594 | (toJSON) 595 | (toFile "package-defs.json") 596 | ]; 597 | 598 | materializeOpamProject = 599 | { 600 | repos ? [ opamRepository ], 601 | resolveArgs ? { }, 602 | regenCommand ? null, 603 | pinDepends ? true, 604 | recursive ? false, 605 | }: 606 | name: project: query: 607 | let 608 | repo = makeOpamRepo' recursive project; 609 | latestVersions = mapAttrs (_: last) (listRepo repo); 610 | pkgdef = repo.passthru.pkgdefs.${name}.${latestVersions.${name}}; 611 | 612 | pinDeps = getPinDepends pkgdef project; 613 | pinDepsQuery = pinDependsQuery pkgdef; 614 | in 615 | materialize { 616 | repos = [ repo ] ++ optionals pinDepends pinDeps ++ repos; 617 | resolveArgs = { 618 | dev = true; 619 | } // resolveArgs; 620 | inherit regenCommand; 621 | } ({ ${name} = latestVersions.${name}; } // pinDepsQuery // query); 622 | 623 | materializeOpamProject' = 624 | { 625 | repos ? [ opamRepository ], 626 | resolveArgs ? { }, 627 | regenCommand ? null, 628 | pinDepends ? true, 629 | recursive ? false, 630 | }: 631 | project: query: 632 | let 633 | repo = makeOpamRepo' recursive project; 634 | latestVersions = mapAttrs (_: last) (listRepo repo); 635 | 636 | pinDeps = concatLists ( 637 | attrValues ( 638 | mapAttrs ( 639 | name: version: getPinDepends repo.passthru.pkgdefs.${name}.${version} project 640 | ) latestVersions 641 | ) 642 | ); 643 | pinDepsQuery = foldl' recursiveUpdate { } ( 644 | attrValues ( 645 | mapAttrs (name: version: pinDependsQuery repo.passthru.pkgdefs.${name}.${version}) latestVersions 646 | ) 647 | ); 648 | in 649 | materialize { 650 | repos = [ repo ] ++ optionals pinDepends pinDeps ++ repos; 651 | resolveArgs = { 652 | dev = true; 653 | } // resolveArgs; 654 | inherit regenCommand; 655 | } (latestVersions // pinDepsQuery // query); 656 | 657 | materializedDefsToScope = 658 | { 659 | pkgs ? bootstrapPackages, 660 | sourceMap ? { }, 661 | overlays ? __overlays, 662 | }: 663 | file: 664 | let 665 | defs = pipe file [ 666 | (readFile) 667 | (fromJSON) 668 | (d: removeAttrs d [ "__opam_nix_regen" ]) 669 | ]; 670 | env = 671 | defs.__opam_nix_env 672 | or (warn "[opam-nix] Your package-defs.json file is missing __opam_nix_env. Please, re-generate it." 673 | { } 674 | ); 675 | in 676 | pipe defs [ 677 | (d: removeAttrs d [ "__opam_nix_env" ]) 678 | (mapAttrs (_: writeFileContents)) 679 | (mapAttrs (_: injectSources sourceMap)) 680 | 681 | (defsToScope pkgs env) 682 | (applyOverlays overlays) 683 | ]; 684 | 685 | queryToScope = 686 | { 687 | repos ? [ opamRepository ], 688 | pkgs ? bootstrapPackages, 689 | overlays ? __overlays, 690 | resolveArgs ? { }, 691 | }: 692 | query: 693 | pipe query [ 694 | (opamList (joinRepos repos) resolveArgs) 695 | (opamListToQuery) 696 | (queryToDefs repos) 697 | (defsToScope pkgs resolveArgs.env or { }) 698 | (applyOverlays overlays) 699 | (applyChecksDocs resolveArgs query) 700 | ]; 701 | 702 | opamImport = 703 | { 704 | repos ? [ opamRepository ], 705 | pkgs ? bootstrapPackages, 706 | resolveArgs ? { }, 707 | overlays ? __overlays, 708 | }: 709 | export: 710 | let 711 | installedList = (importOpam export).installed; 712 | in 713 | pipe installedList [ 714 | opamListToQuery 715 | (queryToDefs repos) 716 | (defsToScope pkgs resolveArgs.env or { }) 717 | (applyOverlays overlays) 718 | (applyChecksDocs resolveArgs (opamListToQuery installedList)) 719 | ]; 720 | 721 | getPinDepends = 722 | pkgdef: project: 723 | map ( 724 | dep: 725 | let 726 | inherit (splitNameVer (head dep)) name version; 727 | in 728 | filterOpamRepo { ${name} = version; } (makeOpamRepo (fetchWithoutChecksum (last dep) project)) 729 | ) pkgdef.pin-depends or [ ]; 730 | 731 | pinDependsQuery = 732 | pkgdef: 733 | listToAttrs ( 734 | map ( 735 | dep: 736 | let 737 | inherit (splitNameVer (head dep)) name version; 738 | in 739 | { 740 | inherit name; 741 | value = version; 742 | } 743 | ) pkgdef.pin-depends or [ ] 744 | ); 745 | 746 | buildOpamProject = 747 | { 748 | repos ? [ opamRepository ], 749 | pkgs ? bootstrapPackages, 750 | overlays ? __overlays, 751 | resolveArgs ? { }, 752 | pinDepends ? true, 753 | recursive ? false, 754 | }@args: 755 | name: project: query: 756 | let 757 | repo = makeOpamRepo' recursive project; 758 | latestVersions = mapAttrs (_: last) (listRepo repo); 759 | pkgdef = repo.passthru.pkgdefs.${name}.${latestVersions.${name}}; 760 | 761 | pinDeps = getPinDepends pkgdef project; 762 | pinDepsQuery = pinDependsQuery pkgdef; 763 | in 764 | queryToScope { 765 | repos = [ repo ] ++ optionals pinDepends pinDeps ++ repos; 766 | overlays = overlays; 767 | resolveArgs = { 768 | dev = true; 769 | } // resolveArgs; 770 | inherit pkgs; 771 | } ({ ${name} = latestVersions.${name}; } // pinDepsQuery // query); 772 | 773 | buildOpamProject' = 774 | { 775 | repos ? [ opamRepository ], 776 | pkgs ? bootstrapPackages, 777 | overlays ? __overlays, 778 | resolveArgs ? { }, 779 | pinDepends ? true, 780 | recursive ? false, 781 | }@args: 782 | project: query: 783 | let 784 | repo = makeOpamRepo' recursive project; 785 | latestVersions = mapAttrs (_: last) (listRepo repo); 786 | 787 | pinDeps = concatLists ( 788 | attrValues ( 789 | mapAttrs ( 790 | name: version: getPinDepends repo.passthru.pkgdefs.${name}.${version} project 791 | ) latestVersions 792 | ) 793 | ); 794 | pinDepsQuery = foldl' recursiveUpdate { } ( 795 | attrValues ( 796 | mapAttrs (name: version: pinDependsQuery repo.passthru.pkgdefs.${name}.${version}) latestVersions 797 | ) 798 | ); 799 | in 800 | queryToScope { 801 | repos = [ repo ] ++ optionals pinDepends pinDeps ++ repos; 802 | overlays = overlays; 803 | resolveArgs = { 804 | dev = true; 805 | } // resolveArgs; 806 | inherit pkgs; 807 | } (latestVersions // pinDepsQuery // query); 808 | 809 | buildDuneProject = 810 | { 811 | pkgs ? bootstrapPackages, 812 | dune ? pkgs.pkgsBuildBuild.dune_3, 813 | ... 814 | }@args: 815 | name: project: query: 816 | let 817 | generatedOpamFile = pkgs.pkgsBuildBuild.stdenv.mkDerivation { 818 | name = "${name}.opam"; 819 | src = project; 820 | nativeBuildInputs = [ 821 | dune 822 | pkgs.pkgsBuildBuild.ocaml 823 | ]; 824 | phases = [ 825 | "unpackPhase" 826 | "buildPhase" 827 | "installPhase" 828 | ]; 829 | buildPhase = "dune build ${name}.opam"; 830 | installPhase = '' 831 | rm _build -rf 832 | cp -R . $out 833 | ''; 834 | }; 835 | in 836 | buildOpamProject args name generatedOpamFile query; 837 | 838 | # takes an atribute set of package definitions (as produced by `queryToDefs`), 839 | # deduplicates sources, and provides a list of sources to fetch 840 | defsToSrcs = 841 | filterPkgs: defs: 842 | let 843 | # use our own version of lib.strings.nameFromURL without `assert name != filename` 844 | nameFromURL = 845 | url: sep: 846 | let 847 | components = splitString "/" url; 848 | filename = last components; 849 | name = head (splitString sep filename); 850 | in 851 | name; 852 | defToSrc = 853 | { version, ... }@pkgdef: 854 | let 855 | inherit (getUrl bootstrapPackages pkgdef) src; 856 | name = 857 | let 858 | n = nameFromURL pkgdef.dev-repo "."; 859 | in 860 | # rename dune so it doesn't clash with dune file in duniverse 861 | if n == "dune" then "_dune" else n; 862 | in 863 | # filter out pkgs without dev-repos 864 | if pkgdef ? dev-repo then { inherit name version src; } else { }; 865 | # remove filterPkgs 866 | filteredDefs = removeAttrs defs filterPkgs; 867 | srcs = mapAttrsToList (pkgName: def: defToSrc def) filteredDefs; 868 | # remove empty elements from pkgs without dev-repos 869 | cleanedSrcs = remove { } srcs; 870 | in 871 | cleanedSrcs; 872 | 873 | deduplicateSrcs = 874 | srcs: 875 | # This is O(n^2). We could try and improve this by sorting the list on name. But n is small. 876 | let 877 | op = 878 | srcs: newSrc: 879 | # Find if two packages come from the same dev-repo. 880 | # Note we are assuming no dev-repos will have different names here, but we also assume 881 | # this later when we will symlink in the duniverse directory based on this name. 882 | let 883 | duplicateSrc = findSingle (src: src.name == newSrc.name) null "multiple" srcs; 884 | in 885 | # Multiple duplicates should never be found as we deduplicate on every new element. 886 | assert duplicateSrc != "multiple"; 887 | if duplicateSrc == null then 888 | srcs 889 | ++ [ 890 | newSrc 891 | ] 892 | # > If packages from the same repo were resolved to different URLs, we need to pick 893 | # > a single one. Here we decided to go with the one associated with the package 894 | # > that has the higher version. We need a better long term solution as this won't 895 | # > play nicely with pins for instance. 896 | # > The best solution here would be to use source trimming, so we can pull each individual 897 | # > package to its own directory and strip out all the unrelated source code but we would 898 | # > need dune to provide that feature. 899 | # See [opam-monorepo](https://github.com/tarides/opam-monorepo/blob/9262e7f71d749520b7e046fbd90a4732a43866e9/lib/duniverse.ml#L143-L157) 900 | else if duplicateSrc.version >= newSrc.version then 901 | srcs 902 | else 903 | (remove duplicateSrc srcs) ++ [ newSrc ]; 904 | in 905 | foldl' op [ ] srcs; 906 | 907 | mkMonorepo = 908 | srcs: 909 | let 910 | # derivation that fetches the source 911 | mkSrc = 912 | { 913 | name, 914 | version, 915 | src, 916 | }: 917 | bootstrapPackages.pkgsBuildBuild.stdenv.mkDerivation ({ 918 | inherit name version src; 919 | phases = [ 920 | "unpackPhase" 921 | "installPhase" 922 | ]; 923 | installPhase = '' 924 | mkdir $out 925 | cp -R . $out 926 | ''; 927 | }); 928 | in 929 | listToAttrs (map (src: nameValuePair src.name (mkSrc src)) srcs); 930 | 931 | queryToMonorepo = 932 | { 933 | repos ? [ 934 | mirageOpamOverlays 935 | opamOverlays 936 | opamRepository 937 | ], 938 | resolveArgs ? { }, 939 | filterPkgs ? [ ], 940 | }: 941 | query: 942 | pipe query [ 943 | # pass monorepo = 1 to pick up dependencies marked with {?monorepo} 944 | # TODO use opam monorepo solver to filter non-dune dependant packages 945 | (opamList (joinRepos repos) (recursiveUpdate resolveArgs { env.monorepo = 1; })) 946 | opamListToQuery 947 | (queryToDefs repos) 948 | (defsToSrcs filterPkgs) 949 | deduplicateSrcs 950 | mkMonorepo 951 | ]; 952 | 953 | buildOpamMonorepo = 954 | { 955 | repos ? [ 956 | mirageOpamOverlays 957 | opamOverlays 958 | opamRepository 959 | ], 960 | resolveArgs ? { }, 961 | pinDepends ? true, 962 | recursive ? false, 963 | extraFilterPkgs ? [ ], 964 | }@args: 965 | project: query: 966 | let 967 | repo = makeOpamRepo' recursive project; 968 | latestVersions = mapAttrs (_: last) (listRepo repo); 969 | 970 | pinDeps = concatLists ( 971 | attrValues ( 972 | mapAttrs ( 973 | name: version: getPinDepends repo.passthru.pkgdefs.${name}.${version} project 974 | ) latestVersions 975 | ) 976 | ); 977 | in 978 | queryToMonorepo { 979 | repos = [ repo ] ++ optionals pinDepends pinDeps ++ repos; 980 | filterPkgs = 981 | [ 982 | "ocaml-system" 983 | "opam-monorepo" 984 | ] 985 | ++ 986 | # filter all queried packages, and packages with sources 987 | # in the project, from the monorepo 988 | (attrNames latestVersions) 989 | ++ extraFilterPkgs; 990 | resolveArgs = { 991 | dev = true; 992 | } // resolveArgs; 993 | } (latestVersions // query); 994 | } 995 | -------------------------------------------------------------------------------- /src/overlays/external/debian.nix: -------------------------------------------------------------------------------- 1 | pkgs: 2 | with pkgs; 3 | # Map from debian package names to nixpkgs packages. 4 | # To add a new package: 5 | # 1. Find the package in nixpkgs: 6 | # * Use https://search.nixos.org/packages or `nix search` to find the package by name or description; 7 | # * Use https://mynixos.com/ or `nix-index`/`nix-locate` to find the package by files contained therein; 8 | # 2. If some changes to the package are needed to be compatible with the debian one, make an override in the let binding below; 9 | # 3. Add it to the list. Keep the quotation marks around the debian package name, even if not needed, and sort the list afterwrads. 10 | let 11 | inherit (lib) warn; 12 | 13 | notPackaged = 14 | name: 15 | warn '' 16 | [opam-nix] ${name} is not packaged in nixpkgs, or at least was not packaged at the time this was written. 17 | Check https://github.com/NixOS/nixpkgs/pulls?q=${name} to see if it has been added in the meantime. 18 | If it has, please update /src/external/debian.nix; 19 | If it hasn't, your best bet is to package it yourself and then add to buildInputs of your package directly. 20 | '' null; 21 | 22 | hidapi' = hidapi.overrideAttrs (_: { 23 | postInstall = '' 24 | mv $out/include/hidapi/* $out/include 25 | rm -d $out/include/hidapi 26 | ln -s $out/lib/libhidapi-hidraw.la $out/lib/libhidapi.la 27 | ln -s $out/lib/libhidapi-hidraw.so.0.0.0 $out/lib/libhidapi.so 28 | ''; 29 | }); 30 | 31 | cargo' = buildEnv { 32 | name = "cargo"; 33 | paths = [ 34 | pkgsBuildBuild.cargo 35 | pkgsBuildHost.rustc 36 | ]; 37 | }; 38 | 39 | gtksourceview' = buildEnv { 40 | name = "gtk-bunch"; 41 | paths = [ 42 | gnome2.gtksourceview 43 | gnome2.gtk.dev 44 | pango.dev 45 | glib.dev 46 | harfbuzz.dev 47 | cairo.dev 48 | gdk-pixbuf.dev 49 | atk.dev 50 | freetype.dev 51 | fontconfig.dev 52 | ]; 53 | }; 54 | 55 | curl-gnutls = curl.override { 56 | gnutlsSupport = true; 57 | opensslSupport = false; 58 | }; 59 | 60 | xorg-dev = buildEnv { 61 | name = "xorg-combined"; 62 | ignoreCollisions = true; 63 | paths = with xorg; [ 64 | libdmx 65 | libfontenc 66 | libICE.dev 67 | libSM.dev 68 | libX11.dev 69 | libXau.dev 70 | libXaw.dev 71 | libXcomposite.dev 72 | libXcursor.dev 73 | libXdamage.dev 74 | libXdmcp.dev 75 | libXext.dev 76 | libXfixes.dev 77 | libXfont.dev 78 | libXft.dev 79 | libXi.dev 80 | libXinerama.dev 81 | libxkbfile.dev 82 | libXmu.dev 83 | libXpm.dev 84 | libXrandr.dev 85 | libXrender.dev 86 | libXres.dev 87 | libXScrnSaver 88 | libXt.dev 89 | libXtst 90 | libXv.dev 91 | libXvMC.dev 92 | xorgserver.dev 93 | xtrans 94 | xorgproto 95 | ]; 96 | }; 97 | 98 | withSdl2 = 99 | otherLib: 100 | buildEnv { 101 | name = "${otherLib.name}-combined"; 102 | paths = [ 103 | otherLib 104 | SDL2.dev 105 | ]; 106 | }; 107 | 108 | in 109 | # Please keep this list sorted alphabetically and one-line-per-package 110 | pkgs 111 | // { 112 | "adwaita-icon-theme" = gnome.adwaita-icon-theme; 113 | "autoconf" = pkgsBuildBuild.autoconf; 114 | "binutils-multiarch" = binutils; 115 | "cargo" = cargo'; 116 | "coinor-csdp" = csdp; 117 | "debianutils" = which; # eurgh 118 | "default-jdk" = openjdk; 119 | "freeglut3-dev" = freeglut.dev; 120 | "freetds-dev" = freetds; 121 | "frei0r-plugins-dev" = frei0r; 122 | "g++" = pkgsBuildHost.gcc; 123 | "gnuplot-x11" = gnuplot; 124 | "guile-3.0-dev" = guile_3_0.dev; 125 | "hdf4-tools" = hdf4; 126 | "jq" = pkgsBuildBuild.jq; 127 | "libaio-dev" = libaio; 128 | "libao-dev" = libao.dev; 129 | "libargon2-0" = libargon2; 130 | "libasound2-dev" = alsa-lib.dev; 131 | "libassimp-dev" = assimp.dev; 132 | "libaugeas-dev" = augeas; 133 | "libavcodec-dev" = ffmpeg.dev; 134 | "libavdevice-dev" = ffmpeg.dev; 135 | "libavfilter-dev" = ffmpeg.dev; 136 | "libavformat-dev" = ffmpeg.dev; 137 | "libavutil-dev" = ffmpeg.dev; 138 | "libbdd-dev" = buddy; 139 | "libblas-dev" = blas.dev; 140 | "libbluetooth-dev" = bluez5; 141 | "libboost-dev" = boost.dev; 142 | "libbrotli-dev" = brotli.dev; 143 | "libbz2-dev" = bzip2.dev; 144 | "libc6-dev" = glibc.dev; 145 | "libcairo2-dev" = cairo.dev; 146 | "libcapnp-dev" = capnproto; 147 | "libcurl4-gnutls-dev" = curl-gnutls.dev; 148 | "libdw-dev" = elfutils.dev; 149 | "libev-dev" = libev; 150 | "libevent-dev" = libevent.dev; 151 | "libexpat1-dev" = expat.dev; 152 | "libfaad-dev" = faad2; 153 | "libfarmhash-dev" = notPackaged "libfarmhash"; 154 | "libfdk-aac-dev" = fdk_aac; 155 | "libffi-dev" = libffi.dev; 156 | "libfftw3-dev" = fftw.dev; 157 | "libflac-dev" = flac.dev; 158 | "libfontconfig1-dev" = fontconfig.dev; 159 | "libfreetype6-dev" = freetype.dev; 160 | "libfswatch-dev" = fswatch; 161 | "libftgl-dev" = ftgl; 162 | "libfuse-dev" = fuse; 163 | "libgammu-dev" = gammu; 164 | "libgavl-dev" = notPackaged "libgavl"; 165 | "libgc-dev" = boehmgc.dev; 166 | "libgd-dev" = gd.dev; 167 | "libgdbm-dev" = gdbm; 168 | "libgeoip-dev" = geoip; 169 | "libgif-dev" = giflib; 170 | "libgirepository1.0-dev" = gobject-introspection.dev; 171 | "libgl1-mesa-dev" = libGL.dev; 172 | "libglade2-dev" = glade; 173 | "libgles2-mesa-dev" = libGL.dev; 174 | "libglew-dev" = glew.dev; 175 | "libglfw3-dev" = glfw3; 176 | "libglib2.0-dev" = glib.dev; 177 | "libglpk-dev" = glpk; 178 | "libglu1-mesa-dev" = libGL.dev; 179 | "libgmp-dev" = gmp.dev; 180 | "libgnomecanvas2-dev" = gnome2.libgnomecanvas.dev; 181 | "libgnutls28-dev" = gnutls.dev; 182 | "libgoocanvas-2.0-dev" = goocanvas2.dev; 183 | "libgoogle-perftools-dev" = gperftools; 184 | "libgrib-api-dev" = grib-api; 185 | "libgsasl7-dev" = gsasl; 186 | "libgsl-dev" = gsl; 187 | "libgstreamer-plugins-base1.0-dev" = gst_all_1.gst-plugins-base.dev; 188 | "libgstreamer1.0-dev" = gst_all_1.gstreamer.dev; 189 | "libgtk-3-dev" = gtk3.dev; 190 | "libgtk2.0-dev" = gtk2.dev; 191 | "libgtksourceview-3.0-dev" = gtksourceview3.dev; 192 | "libgtksourceview2.0-dev" = gtksourceview'; 193 | "libgtkspell3-3-dev" = gtkspell3; 194 | "libhdf4-dev" = hdf4.dev; 195 | "libhdf5-dev" = hdf5.dev; 196 | "libhidapi-dev" = hidapi'; 197 | "libipc-system-simple-perl" = perlPackages.IPCSystemSimple; 198 | "libirrlicht-dev" = irrlicht; 199 | "libjack-dev" = jack1; 200 | "libjack-jackd2-dev" = jack2; 201 | "libjavascriptcoregtk-3.0-dev" = webkitgtk.dev; 202 | "libjemalloc-dev" = jemalloc; 203 | "libjpeg-dev" = libjpeg.dev; 204 | "libkrb5-dev" = krb5.dev; 205 | "libkyotocabinet-dev" = kyotocabinet; 206 | "liblapack-dev" = lapack.dev; 207 | "liblapacke-dev" = lapack.dev; 208 | "libleveldb-dev" = leveldb.dev; 209 | "liblilv-dev" = lilv.dev; 210 | "liblinear-tools" = liblinear.bin; 211 | "liblldb-3.5-dev" = lldb.dev; 212 | "liblmdb-dev" = lmdb.dev; 213 | "liblo-dev" = liblo; 214 | "liblua5.2-dev" = lua5_2; 215 | "liblz4-dev" = lz4.dev; 216 | "liblz4-tool" = lz4.bin; 217 | "liblzma-dev" = lzma.dev; 218 | "liblzo2-dev" = lzo; 219 | "libmad0-dev" = libmad; 220 | "libmagic-dev" = file; 221 | "libmagickcore-dev" = imagemagick.dev; 222 | "libmariadb-dev" = mariadb; 223 | "libmaxminddb-dev" = libmaxminddb; 224 | "libmbedtls-dev" = mbedtls; 225 | "libmecab-dev" = mecab; 226 | "libmilter-dev" = libmilter; 227 | "libmosquitto-dev" = mosquitto; 228 | "libmp3lame-dev" = lame; 229 | "libmpfr-dev" = mpfr.dev; 230 | "libmpg123-dev" = mpg123; 231 | "libnanomsg-dev" = nanomsg; 232 | "libnauty2-dev" = nauty.dev; 233 | "libnl-3-dev" = libnl; 234 | "libnl-route-3-dev" = libnl; 235 | "libnlopt-dev" = nlopt; 236 | "libode-dev" = ode; 237 | "libogg-dev" = libogg.dev; 238 | "libonig-dev" = oniguruma; 239 | "libopenbabel-dev" = openbabel; 240 | "libopenblas-dev" = openblas.dev; 241 | "libopencc1.1" = opencc; 242 | "libopencc1" = opencc; 243 | "libopencc2" = opencc; 244 | "libopenimageio-dev" = openimageio.dev; 245 | "libopenjpeg" = openjpeg.dev; 246 | "libopus-dev" = libopus.dev; 247 | "libpango1.0-dev" = pango.dev; 248 | "libpapi-dev" = papi; 249 | "libpcre2-dev" = pcre2.dev; 250 | "libpcre3-dev" = pcre.dev; 251 | "libplplot-dev" = plplot; 252 | "libpng-dev" = libpng.dev; 253 | "libportmidi-dev" = portmidi; 254 | "libppl-dev" = ppl; 255 | "libpq-dev" = postgresql; 256 | "libproj-dev" = proj.dev; 257 | "libprotobuf-dev" = protobuf; 258 | "libprotoc-dev" = protobuf; 259 | "libpulse-dev" = pulseaudio.dev; 260 | "libqrencode-dev" = qrencode.dev; 261 | "libqt4-dev" = qt4.dev; 262 | "librdkafka-dev" = rdkafka; 263 | "librocksdb-dev" = rocksdb; 264 | "librsvg2-dev" = librsvg.dev; 265 | "libsamplerate0-dev" = libsamplerate.dev; 266 | "libschroedinger-dev" = schroedinger.dev; 267 | "libsdl-gfx1.2-dev" = SDL_gfx; 268 | "libsdl-image1.2-dev" = SDL_image; 269 | "libsdl-mixer1.2-dev" = SDL_mixer; 270 | "libsdl-net1.2-dev" = SDL_net; 271 | "libsdl-ttf2.0-dev" = SDL_ttf; 272 | "libsdl1.2-dev" = SDL.dev; 273 | "libsdl2-dev" = SDL2.dev; 274 | "libsdl2-image-dev" = withSdl2 SDL2_image; 275 | "libsdl2-mixer-dev" = withSdl2 SDL2_mixer.dev; 276 | "libsdl2-net-dev" = withSdl2 SDL2_net; 277 | "libsdl2-ttf-dev" = withSdl2 SDL2_ttf; 278 | "libseccomp-dev" = libseccomp.dev; 279 | "libsecp256k1-0" = secp256k1; 280 | "libsecp256k1-dev" = secp256k1; 281 | "libsfml-dev" = sfml; 282 | "libshine-dev" = shine; 283 | "libshp-dev" = shapelib; 284 | "libsnappy-dev" = snappy.dev; 285 | "libsodium-dev" = libsodium.dev; 286 | "libsoundtouch-dev" = soundtouch; 287 | "libsource-highlight-dev" = sourceHighlight.dev; 288 | "libspeex-dev" = speex.dev; 289 | "libspf2-dev" = libspf2; 290 | "libsqlite3-dev" = sqlite.dev; 291 | "libsrt-gnutls-dev" = warn "[opam-nix] warning: srt in nixpkgs does not support gnutls" srt; 292 | "libsrt-openssl-dev" = srt; 293 | "libssh-dev" = libssh; 294 | "libssl-dev" = openssl.dev; 295 | "libstring-shellquote-perl" = perlPackages.StringShellQuote; 296 | "libsvm-dev" = libsvm; 297 | "libsvm-tools" = libsvm; 298 | "libswresample-dev" = ffmpeg.dev; 299 | "libswscale-dev" = ffmpeg.dev; 300 | "libsystemd-dev" = systemd.dev; 301 | "libtag1-dev" = taglib; 302 | "libtheora-dev" = libtheora.dev; 303 | "libtidy-dev" = libtidy; 304 | "libtraildb-dev" = notPackaged "traildb"; 305 | "libudunits2-dev" = udunits; 306 | "libusb-1.0-0-dev" = libusb1.dev; 307 | "libuv1-dev" = libuv; 308 | "libvirt-dev" = libvirt; 309 | "libvo-aacenc-dev" = vo-aacenc; 310 | "libvorbis-dev" = libvorbis.dev; 311 | "libwxgtk-media3.0-dev" = wxGTK30; 312 | "libwxgtk-webview3.0-dev" = wxGTK30; 313 | "libwxgtk3.0-dev" = wxGTK30; 314 | "libx11-dev" = xorg.libX11.dev; 315 | "libxcb-image0-dev" = xorg.libxcb.dev; 316 | "libxcb-keysyms1-dev" = xorg.libxcb.dev; 317 | "libxcb-shm0-dev" = xorg.libxcb.dev; 318 | "libxcb-xkb-dev" = xorg.libxcb.dev; 319 | "libxcb1-dev" = xorg.libxcb.dev; 320 | "libxcursor-dev" = xorg-dev; 321 | "libxen-dev" = xen; 322 | "libxi-dev" = xorg.libXi.dev; 323 | "libxinerama-dev" = xorg-dev; 324 | "libxkbcommon-dev" = libxkbcommon.dev; 325 | "libxrandr-dev" = xorg.libXrandr.dev; 326 | "libxxhash-dev" = xxHash; 327 | "libyara-dev" = yara; 328 | "libzbar-dev" = zbar.dev; 329 | "libzmq3-dev" = zeromq4; 330 | "libzstd-dev" = zstd.dev; 331 | "ligonig-dev" = oniguruma; 332 | "linux-libc-dev" = glibc.dev; 333 | "llvm-14-dev" = llvm_14.dev; 334 | "llvm-9-dev" = llvm_9.dev; 335 | "llvm-dev" = llvm.dev; 336 | "m4" = pkgsBuildBuild.m4; 337 | "mesa-common-dev" = mesa.dev; 338 | "mpi-default-dev" = mpi; 339 | "ncurses-dev" = ncurses.dev; 340 | "neko-dev" = neko; 341 | "neko" = neko; 342 | "nettle-dev" = nettle.dev; 343 | "npm" = nodePackages.npm; 344 | "perl" = pkgsBuildBuild.perl; 345 | "pkg-config" = pkgsBuildBuild.pkg-config; 346 | "portaudio19-dev" = portaudio; 347 | "postgresql-common" = postgresql; 348 | "protobuf-compiler" = protobufc; 349 | "python2.7-dev" = python27; 350 | "python2.7" = python27; 351 | "python3" = python3; 352 | "python3-dev" = python3; 353 | "python3-distutils" = python3.pkgs.distutils_extra; 354 | "python3-yaml" = python3.pkgs.pyyaml; 355 | "qt4-qmake" = qt4; 356 | "qt5-qmake" = qt5.qmake; 357 | "qtdeclarative5-dev" = qt5.qtdeclarative.dev; 358 | "r-base-core" = R; 359 | "r-mathlib" = R; 360 | "ruby-sass" = sass; 361 | "sdpa" = notPackaged "sdpa"; 362 | "swi-prolog" = swiProlog; 363 | "tcl-dev" = tcl; 364 | "tcl8.5-dev" = tcl-8_5; 365 | "texlive-latex-base" = texlive.combined.scheme-basic; 366 | "thrift-compiler" = thrift; 367 | "tk-dev" = tk.dev; 368 | "tk8.5-dev" = tk-8_5.dev; 369 | "unixodbc-dev" = unixODBC; 370 | "uuid-dev" = libuuid.dev; 371 | "vim-nox" = vim; 372 | "wx3.0-headers" = wxGTK30; 373 | "xorg-dev" = xorg-dev; 374 | "xvfb" = xvfb-run; 375 | "zlib1g-dev" = zlib.dev; 376 | } 377 | -------------------------------------------------------------------------------- /src/overlays/external/homebrew.nix: -------------------------------------------------------------------------------- 1 | pkgs: 2 | with pkgs; 3 | # Map from homebrew package names to nixpkgs packages. 4 | # To add a new package: 5 | # 1. Find the package in nixpkgs: 6 | # * Use https://search.nixos.org/packages or `nix search` to find the package by name or description; 7 | # * Use https://mynixos.com/ or `nix-index`/`nix-locate` to find the package by files contained therein; 8 | # 2. If some changes to the package are needed to be compatible with the homebrew one, make an override in the let binding below; 9 | # 3. Add it to the list. Keep the quotation marks around the homebrew package name, even if not needed, and sort the list afterwrads. 10 | let 11 | rust' = buildEnv { 12 | name = "rust-and-cargo"; 13 | paths = [ 14 | rustc 15 | cargo 16 | libiconv 17 | ]; 18 | }; 19 | 20 | pkgconf' = pkgs.pkgconf.overrideAttrs (_: { 21 | postInstall = '' 22 | ln -s $out/bin/pkgconf $out/bin/pkg-config 23 | ''; 24 | }); 25 | 26 | in 27 | # Please keep this list sorted alphabetically and one-line-per-package 28 | pkgs 29 | // { 30 | "autoconf" = pkgsBuildBuild.autoconf; 31 | "cairo" = cairo.dev; 32 | "expat" = expat.dev; 33 | "gmp" = gmp.dev; 34 | "gtk+3" = gtk3.dev; 35 | "gtksourceview3" = gtksourceview3.dev; 36 | "libxml2" = libxml2.dev; 37 | "libpq" = postgresql; 38 | "pkgconf" = pkgconf'; 39 | "postgresql" = postgresql; 40 | "postgresql@14" = postgresql_14; 41 | "postgresql@15" = postgresql_15; 42 | "postgresql@16" = postgresql_16; 43 | "proctools" = procps; 44 | "python@3" = python3; 45 | "python@3.8" = python38; 46 | "python@3.9" = python39; 47 | "python@3.10" = python310; 48 | "python@3.12" = python312; 49 | "python@3.13" = python313; 50 | "rust" = rust'; 51 | "zlib" = zlib.dev; 52 | } 53 | -------------------------------------------------------------------------------- /src/overlays/lib.nix: -------------------------------------------------------------------------------- 1 | lib: { 2 | applyOverrides = 3 | super: overrides: 4 | builtins.removeAttrs (lib.mapAttrs' ( 5 | name: f: lib.nameValuePair (if super ? ${name} then name else "") (super.${name}.overrideAttrs f) 6 | ) overrides) [ "" ]; 7 | } 8 | -------------------------------------------------------------------------------- /src/overlays/ocaml-darwin.nix: -------------------------------------------------------------------------------- 1 | final: prev: 2 | let 3 | inherit (prev.nixpkgs) lib; 4 | 5 | pkgs = prev.nixpkgs; 6 | 7 | inherit (import ./lib.nix lib) applyOverrides; 8 | 9 | overrides = rec { 10 | ocurl = oa: { buildInputs = oa.buildInputs ++ [ pkgs.curl.dev ]; }; 11 | 12 | conf-which = oa: { 13 | nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.which ]; 14 | }; 15 | 16 | conf-m4 = oa: { nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.m4 ]; }; 17 | 18 | conf-perl = oa: { 19 | nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.perl ]; 20 | }; 21 | 22 | conf-libssl = oa: { 23 | # TODO add openssl to buildInputs? 24 | nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.pkg-config ]; 25 | buildPhase = '' 26 | pkg-config --print-errors --exists openssl 27 | ''; 28 | installPhase = "mkdir $out"; 29 | }; 30 | 31 | dune = 32 | oa: with pkgs; { 33 | buildInputs = oa.buildInputs ++ [ 34 | darwin.apple_sdk.frameworks.Foundation 35 | darwin.apple_sdk.frameworks.CoreServices 36 | ]; 37 | }; 38 | 39 | zarith = oa: { 40 | buildPhase = '' 41 | ./configure 42 | make 43 | ''; 44 | }; 45 | 46 | digestif = oa: { dontPatchShebangsEarly = true; }; 47 | 48 | conf-cairo = oa: { buildPhase = "pkg-config --libs cairo"; }; 49 | 50 | class_group_vdf = oa: { 51 | # Similar to https://github.com/NixOS/nixpkgs/issues/127608 52 | hardeningDisable = [ "stackprotector" ]; 53 | }; 54 | 55 | conf-libpcre = oa: { 56 | nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.pkg-config ]; 57 | }; 58 | conf-libpcre2-8 = oa: { 59 | nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.pkg-config ]; 60 | }; 61 | conf-libffi = oa: { 62 | nativeBuildInputs = oa.nativeBuildInputs ++ [ pkgs.pkg-config ]; 63 | }; 64 | }; 65 | in 66 | applyOverrides prev overrides 67 | // { 68 | re2 = (prev.re2.override { "conf-g++" = null; }).overrideAttrs (oa: { 69 | prePatch = 70 | oa.prePatch 71 | + '' 72 | substituteInPlace src/re2_c/dune --replace 'CXX=g++' 'CXX=c++' 73 | substituteInPlace src/dune --replace '(cxx_flags (:standard \ -pedantic) (-I re2_c/libre2))' '(cxx_flags (-undefined dynamic_lookup :standard \ -pedantic) (-I re2_c/libre2) (-x c++))' 74 | ''; 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /src/overlays/ocaml-static.nix: -------------------------------------------------------------------------------- 1 | final: prev: 2 | let 3 | 4 | inherit (import ./lib.nix prev.nixpkgs.lib) applyOverrides; 5 | 6 | fake-cxx = final.nixpkgs.writeShellScriptBin "g++" ''$CXX "$@"''; 7 | fake-cc = final.nixpkgs.writeShellScriptBin "cc" ''$CC "$@"''; 8 | overrides = { 9 | ocaml-base-compiler = oa: { 10 | buildPhase = '' 11 | ./configure \ 12 | --prefix=$out \ 13 | --disable-shared \ 14 | --enable-static \ 15 | --host=${final.nixpkgs.stdenv.hostPlatform.config} \ 16 | --target=${final.nixpkgs.stdenv.targetPlatform.config} \ 17 | -C 18 | make -j$NIX_BUILD_CORES 19 | ''; 20 | hardeningDisable = [ "pie" ] ++ oa.hardeningDisable or [ ]; 21 | }; 22 | 23 | "conf-g++" = oa: { 24 | nativeBuildInputs = oa.nativeBuildInputs ++ [ fake-cxx ]; 25 | }; 26 | 27 | sodium = oa: { 28 | buildInputs = oa.buildInputs ++ [ final.nixpkgs.sodium-static ]; 29 | nativeBuildInputs = oa.nativeBuildInputs ++ [ fake-cc ]; 30 | }; 31 | 32 | conf-gmp = oa: { nativeBuildInputs = oa.nativeBuildInputs ++ [ fake-cc ]; }; 33 | 34 | base58 = oa: { 35 | buildPhase = '' 36 | make lib.byte 37 | ocamlbuild -I src -I tests base58.cmxa 38 | ''; 39 | }; 40 | 41 | zarith = oa: { 42 | preBuild = '' 43 | sed "s/ar='ar'/ar='$AR'/" -i configure 44 | ''; 45 | }; 46 | 47 | digestif = oa: { 48 | buildPhase = '' 49 | dune build -p digestif -j $NIX_BUILD_CORES 50 | ''; 51 | }; 52 | 53 | cmdliner = oa: { 54 | buildPhase = '' 55 | make build-byte build-native 56 | ''; 57 | installPhase = '' 58 | make PREFIX=$out LIBDIR=$OCAMLFIND_DESTDIR/cmdliner install-common install-native 59 | ''; 60 | }; 61 | 62 | ocaml-extlib = oa: { 63 | buildPhase = '' 64 | make -C src all opt 65 | ''; 66 | }; 67 | 68 | ocamlgraph = oa: { 69 | buildPhase = '' 70 | ./configure 71 | sed 's/graph.cmxs//' -i Makefile 72 | make NATIVE_DYNLINK=false 73 | ''; 74 | }; 75 | 76 | opam-file-format = _: { 77 | buildPhase = "make opam-file-format.cma opam-file-format.cmxa"; 78 | }; 79 | }; 80 | in 81 | applyOverrides prev overrides 82 | -------------------------------------------------------------------------------- /src/overlays/ocaml.nix: -------------------------------------------------------------------------------- 1 | final: prev: 2 | let 3 | inherit (prev.nixpkgs) lib; 4 | 5 | inherit (import ./lib.nix lib) applyOverrides; 6 | 7 | ocamlVersion = final.ocaml.passthru.pkgdef.version; 8 | 9 | ocamlVersionList = lib.splitString "." ocamlVersion; 10 | 11 | major = builtins.elemAt ocamlVersionList 0; 12 | minor = builtins.elemAt ocamlVersionList 1; 13 | 14 | nixpkgsOcamlPackages = 15 | lib.warnIf (final.nixpkgs.stdenv.hostPlatform.system != final.nixpkgs.stdenv.targetPlatform.system) 16 | "[opam-nix] Cross-compilation is not supported! This will likely fail. See https://github.com/NixOS/nixpkgs/issues/143883 ." 17 | final.nixpkgs.ocaml-ng."ocamlPackages_${major}_${minor}" or (throw '' 18 | [opam-nix] OCaml compiler version ${major}.${minor} couldn't be found in nixpkgs. 19 | You can try: 20 | - Providing a different nixpkgs version to opam-nix; 21 | - Explicitly requiring an OCaml compiler version present in the current nixpkgs version (here are the available versions: ${toString (builtins.attrNames final.nixpkgs.ocaml-ng)}); 22 | - Using an OCaml compiler from opam by explicitly requiring ocaml-base-compiler (possibly instead of ocaml-system). 23 | ''); 24 | 25 | overrides = { 26 | ocaml-system = oa: { 27 | # Note that we take ocaml from the same package set as 28 | nativeBuildInputs = [ nixpkgsOcamlPackages.ocaml ]; 29 | }; 30 | 31 | ocaml = oa: { 32 | opam__ocaml_config__share = "${final.ocaml-config}/share/ocaml-config"; 33 | }; 34 | 35 | camlp4 = oa: { 36 | # Point to the real installation directory 37 | postInstall = '' 38 | sed -i 's@directory = "+camlp4"@directory = "../ocaml/camlp4"@' "$out/lib/ocaml/$opam__ocaml__version/site-lib/camlp4/META" 39 | ''; 40 | }; 41 | 42 | # Attempts to install to ocaml root 43 | num = 44 | if lib.versionAtLeast prev.num.version "1.4" then 45 | oa: { opam__ocaml__preinstalled = "true"; } 46 | else if lib.versionAtLeast prev.num.version "1.0" then 47 | oa: { patches = final.nixpkgs.ocamlPackages.num.patches; } 48 | else 49 | _: { }; 50 | 51 | cairo2 = oa: { 52 | NIX_CFLAGS_COMPILE = [ "-I${final.nixpkgs.freetype.dev}/include/freetype" ]; 53 | buildInputs = oa.buildInputs ++ [ final.nixpkgs.freetype.dev ]; 54 | prePatch = 55 | oa.prePatch 56 | + '' 57 | echo '#define OCAML_CAIRO_HAS_FT 1' > src/cairo_ocaml.h 58 | cat src/cairo_ocaml.h.p >> src/cairo_ocaml.h 59 | sed 's,/usr/include/cairo,${final.nixpkgs.cairo.dev}/include/cairo,' -i config/discover.ml 60 | sed 's/targets c_flags.sexp c_library_flags.sexp cairo_ocaml.h/targets c_flags.sexp c_library_flags.sexp/' -i src/dune 61 | ''; 62 | }; 63 | 64 | ocamlfind = oa: { 65 | patches = 66 | lib.optional (lib.versionOlder oa.version "1.9.3") ../../patches/ocamlfind/install_topfind_192.patch 67 | ++ lib.optional (oa.version == "1.9.3") ../../patches/ocamlfind/install_topfind_193.patch 68 | ++ lib.optional ( 69 | lib.versionAtLeast oa.version "1.9.4" && lib.versionOlder oa.version "1.9.6" 70 | ) ../../patches/ocamlfind/install_topfind_194.patch 71 | ++ lib.optional ( 72 | lib.versionAtLeast oa.version "1.9.6" && lib.versionOlder oa.version "1.9.8" 73 | ) ../../patches/ocamlfind/install_topfind_196.patch 74 | ++ lib.optional (lib.versionAtLeast oa.version "1.9.8") ../../patches/ocamlfind/install_topfind_198.patch; 75 | opam__ocaml__preinstalled = "false"; # Install topfind 76 | passthru = lib.recursiveUpdate oa.passthru { 77 | pkgdef.url.section.src = "https://web.archive.org/${oa.passthru.pkgdef.url.section.src}"; 78 | }; 79 | }; 80 | 81 | # Verifies checksums of scripts and installs to $OCAMLFIND_DESTDIR 82 | tezos-rust-libs = _: { 83 | dontPatchShebangsEarly = true; 84 | postInstall = '' 85 | mkdir -p $out/include 86 | mv $OCAMLFIND_DESTDIR/tezos-rust-libs/*.a $out/lib 87 | mv $OCAMLFIND_DESTDIR/tezos-rust-libs/*.h $out/include 88 | rm -rf $out/lib/ocaml 89 | ''; 90 | }; 91 | 92 | hacl-star-raw = _: { sourceRoot = "."; }; 93 | hacl-star = _: { sourceRoot = "."; }; 94 | 95 | feather = oa: { 96 | nativeBuildInputs = oa.nativeBuildInputs ++ [ final.nixpkgs.procps ]; 97 | }; 98 | 99 | camlimages = oa: { 100 | buildInputs = oa.buildInputs ++ [ 101 | final.nixpkgs.libpng 102 | final.nixpkgs.libjpeg 103 | ]; 104 | nativeBuildInputs = oa.nativeBuildInputs ++ [ final.nixpkgs.pkg-config ]; 105 | }; 106 | camlpdf = oa: { 107 | nativeBuildInputs = oa.nativeBuildInputs ++ [ final.nixpkgs.which ]; 108 | }; 109 | 110 | ocaml-solo5 = oa: { 111 | preBuild = '' 112 | cp -Lr $opam__ocaml_src__lib/ocaml-src ./ocaml 113 | chmod -R +rw ./ocaml 114 | sed -i 's|cp runtime/ocamlrun$(EXE) boot/ocamlrun$(EXE)|rm boot/ocamlrun$(EXE); cp runtime/ocamlrun$(EXE) boot/ocamlrun$(EXE)|g' ocaml/Makefile 115 | sed -i 's|cp -r `opam var prefix`/lib/ocaml-src ./ocaml|echo "Skipping copying ocaml-src to ./ocaml"|g' Makefile 116 | ''; 117 | }; 118 | 119 | augeas = oa: { 120 | buildInputs = [ 121 | final.nixpkgs.libxml2 122 | final.nixpkgs.augeas 123 | ]; 124 | }; 125 | 126 | coq-of-ocaml = 127 | oa: 128 | lib.optionalAttrs (lib.versionAtLeast oa.version "2.5.3") { 129 | sourceRoot = "."; 130 | }; 131 | 132 | coq = oa: { 133 | setupHook = final.nixpkgs.writeText "setupHook.sh" ( 134 | '' 135 | addCoqPath () { 136 | if test -d "$1/lib/coq/${oa.version}/user-contrib"; then 137 | export COQPATH="''${COQPATH-}''${COQPATH:+:}$1/lib/coq/${oa.version}/user-contrib/" 138 | fi 139 | } 140 | 141 | addEnvHooks "$targetOffset" addCoqPath 142 | 143 | # Note that $out refers to the output of a dependent package, not coq itself 144 | export DESTDIR="$out/lib/coq/${oa.version}" 145 | export COQLIBINSTALL="$out/lib/coq/${oa.version}/user-contrib" 146 | export COQPLUGININSTALL="$out/lib/ocaml/${final.ocaml.version}/site-lib" 147 | export COQUSERCONTRIB="$out/lib/coq/${oa.version}/user-contrib" 148 | '' 149 | + lib.optionalString (prev ? coq-stdlib) '' 150 | export COQLIB="${final.coq-stdlib}/lib/ocaml/${final.ocaml.version}/site-lib/coq" 151 | export COQCORELIB="${final.coq-core}/lib/ocaml/${final.ocaml.version}/site-lib/coq-core" 152 | '' 153 | ); 154 | }; 155 | 156 | coq-stdlib = oa: { 157 | fixupPhase = 158 | oa.fixupPhase or "" 159 | + '' 160 | mkdir -p $out/nix-support 161 | echo "export COQLIB=\"$out/lib/ocaml/${final.ocaml.version}/site-lib/coq\"" >> $out/nix-support/setup-hook 162 | ''; 163 | }; 164 | 165 | coq-core = oa: { 166 | fixupPhase = 167 | oa.fixupPhase or "" 168 | + '' 169 | mkdir -p $out/nix-support 170 | echo "export COQCORELIB=\"$out/lib/ocaml/${final.ocaml.version}/site-lib/coq-core\"" >> $out/nix-support/setup-hook 171 | ''; 172 | }; 173 | 174 | fswatch = 175 | oa: 176 | if lib.versionAtLeast oa.version "11-0.1.3" then 177 | { 178 | buildPhase = '' 179 | echo '(-I${prev.nixpkgs.fswatch}/include/libfswatch/c)' > fswatch/src/inc_cflags 180 | echo '(-lfswatch)' > fswatch/src/inc_libs 181 | dune build -p $opam__name -j $opam__jobs 182 | ''; 183 | } 184 | else 185 | { 186 | buildPhase = '' 187 | sed -i 's@/usr/local/include/libfswatch/c@${prev.nixpkgs.fswatch}/include/libfswatch/c@' fswatch/src/dune 188 | ''; 189 | }; 190 | 191 | ocsigenserver = oa: { 192 | # Ocsigen installs a FIFO. 193 | postInstall = '' 194 | rm -f "$out"/lib/ocaml/*/site-lib/ocsigenserver/var/run/ocsigenserver_command 195 | ''; 196 | }; 197 | 198 | timedesc-tzdb = _: { sourceRoot = "."; }; 199 | timedesc-tzlocal = _: { sourceRoot = "."; }; 200 | timedesc-sexp = _: { sourceRoot = "."; }; 201 | timedesc = _: { sourceRoot = "."; }; 202 | timere = _: { sourceRoot = "."; }; 203 | 204 | pyml = oa: if oa.version == "20231101" then { sourceRoot = "."; } else { }; 205 | 206 | opam-format = oa: { buildInputs = oa.buildInputs ++ [ final.opam-core ]; }; 207 | 208 | opam-repository = oa: { buildInputs = oa.buildInputs ++ [ final.opam-format ]; }; 209 | 210 | opam-state = oa: { buildInputs = oa.buildInputs ++ [ final.opam-repository ]; }; 211 | 212 | utop = oa: if oa.passthru.pkgdef.version == "2.15.0-1" then { sourceRoot = "."; } else { }; 213 | }; 214 | in 215 | lib.optionalAttrs (prev ? ocamlfind-secondary) { 216 | dune = (prev.dune.override { ocaml = final.nixpkgs.ocaml; }).overrideAttrs (_: { 217 | postFixup = "rm $out/nix-support -rf"; 218 | }); 219 | } 220 | // lib.optionalAttrs (prev ? ctypes) { 221 | ctypes = 222 | if prev ? ctypes-foreign then (prev.ctypes.override { ctypes-foreign = null; }) else prev.ctypes; 223 | } 224 | // applyOverrides prev overrides 225 | -------------------------------------------------------------------------------- /templates/executable/.envrc: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | # For use with direnv. 3 | # Installing nix-direnv will ensure a smoother experience. 4 | use flake -------------------------------------------------------------------------------- /templates/executable/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | opam-nix.url = "github:tweag/opam-nix"; 4 | flake-utils.url = "github:numtide/flake-utils"; 5 | nixpkgs.follows = "opam-nix/nixpkgs"; 6 | }; 7 | outputs = 8 | { 9 | self, 10 | flake-utils, 11 | opam-nix, 12 | nixpkgs, 13 | }@inputs: 14 | # Don't forget to put the package name instead of `throw': 15 | let 16 | package = throw "Put the package name here!"; 17 | in 18 | flake-utils.lib.eachDefaultSystem ( 19 | system: 20 | let 21 | pkgs = nixpkgs.legacyPackages.${system}; 22 | on = opam-nix.lib.${system}; 23 | devPackagesQuery = { 24 | # You can add "development" packages here. They will get added to the devShell automatically. 25 | ocaml-lsp-server = "*"; 26 | ocamlformat = "*"; 27 | }; 28 | query = devPackagesQuery // { 29 | ## You can force versions of certain packages here, e.g: 30 | ## - force the ocaml compiler to be taken from opam-repository: 31 | ocaml-base-compiler = "*"; 32 | ## - or force the compiler to be taken from nixpkgs and be a certain version: 33 | # ocaml-system = "4.14.0"; 34 | ## - or force ocamlfind to be a certain version: 35 | # ocamlfind = "1.9.2"; 36 | }; 37 | scope = on.buildOpamProject' { } ./. query; 38 | overlay = final: prev: { 39 | # You can add overrides here 40 | ${package} = prev.${package}.overrideAttrs (_: { 41 | # Prevent the ocaml dependencies from leaking into dependent environments 42 | doNixSupport = false; 43 | }); 44 | }; 45 | scope' = scope.overrideScope overlay; 46 | # The main package containing the executable 47 | main = scope'.${package}; 48 | # Packages from devPackagesQuery 49 | devPackages = builtins.attrValues (pkgs.lib.getAttrs (builtins.attrNames devPackagesQuery) scope'); 50 | in 51 | { 52 | legacyPackages = scope'; 53 | 54 | packages.default = main; 55 | 56 | devShells.default = pkgs.mkShell { 57 | inputsFrom = [ main ]; 58 | buildInputs = devPackages ++ [ 59 | # You can add packages from nixpkgs here 60 | ]; 61 | }; 62 | } 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /templates/multi-package/.envrc: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | # For use with direnv. 3 | # Installing nix-direnv will ensure a smoother experience. 4 | use flake -------------------------------------------------------------------------------- /templates/multi-package/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | opam-nix.url = "github:tweag/opam-nix"; 4 | flake-utils.url = "github:numtide/flake-utils"; 5 | nixpkgs.follows = "opam-nix/nixpkgs"; 6 | }; 7 | outputs = 8 | { 9 | self, 10 | flake-utils, 11 | opam-nix, 12 | nixpkgs, 13 | }@inputs: 14 | flake-utils.lib.eachDefaultSystem ( 15 | system: 16 | let 17 | pkgs = nixpkgs.legacyPackages.${system}; 18 | on = opam-nix.lib.${system}; 19 | localPackagesQuery = builtins.mapAttrs (_: pkgs.lib.last) (on.listRepo (on.makeOpamRepo ./.)); 20 | devPackagesQuery = { 21 | # You can add "development" packages here. They will get added to the devShell automatically. 22 | ocaml-lsp-server = "*"; 23 | ocamlformat = "*"; 24 | }; 25 | query = devPackagesQuery // { 26 | ## You can force versions of certain packages here, e.g: 27 | ## - force the ocaml compiler to be taken from opam-repository: 28 | ocaml-base-compiler = "*"; 29 | ## - or force the compiler to be taken from nixpkgs and be a certain version: 30 | # ocaml-system = "4.14.0"; 31 | ## - or force ocamlfind to be a certain version: 32 | # ocamlfind = "1.9.2"; 33 | }; 34 | scope = on.buildOpamProject' { } ./. query; 35 | overlay = final: prev: { 36 | # You can add overrides here 37 | }; 38 | scope' = scope.overrideScope overlay; 39 | # Packages from devPackagesQuery 40 | devPackages = builtins.attrValues (pkgs.lib.getAttrs (builtins.attrNames devPackagesQuery) scope'); 41 | # Packages in this workspace 42 | packages = pkgs.lib.getAttrs (builtins.attrNames localPackagesQuery) scope'; 43 | in 44 | { 45 | legacyPackages = scope'; 46 | 47 | inherit packages; 48 | 49 | ## If you want to have a "default" package which will be built with just `nix build`, do this instead of `inherit packages;`: 50 | # packages = packages // { default = packages.; }; 51 | 52 | devShells.default = pkgs.mkShell { 53 | inputsFrom = builtins.attrValues packages; 54 | buildInputs = devPackages ++ [ 55 | # You can add packages from nixpkgs here 56 | ]; 57 | }; 58 | } 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /templates/simple/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | opam-nix.url = "github:tweag/opam-nix"; 4 | flake-utils.url = "github:numtide/flake-utils"; 5 | nixpkgs.follows = "opam-nix/nixpkgs"; 6 | }; 7 | outputs = 8 | { 9 | self, 10 | flake-utils, 11 | opam-nix, 12 | nixpkgs, 13 | }@inputs: 14 | # Don't forget to put the package name instead of `throw': 15 | let 16 | package = throw "Put the package name here!"; 17 | in 18 | flake-utils.lib.eachDefaultSystem ( 19 | system: 20 | let 21 | pkgs = nixpkgs.legacyPackages.${system}; 22 | on = opam-nix.lib.${system}; 23 | scope = on.buildOpamProject { } package ./. { ocaml-base-compiler = "*"; }; 24 | overlay = final: prev: { 25 | # Your overrides go here 26 | }; 27 | in 28 | { 29 | legacyPackages = scope.overrideScope overlay; 30 | 31 | packages.default = self.legacyPackages.${system}.${package}; 32 | } 33 | ); 34 | } 35 | --------------------------------------------------------------------------------