Skip to content

vapourismo/opam-nix-integration

Repository files navigation

opam-nix-integration

This project aims to provide an integration mechanism for OPAM packages into Nix.

To achieve this, it supplies two key solutions for common use cases:

  • Build a package set from a list of constraints or OPAM files, and an opam-repository.
  • Produce a Nix derivation from an OPAM file.

Getting started

The following sets up a package set using a version of opam-repository and some constraints we have against those packages. After that it produces a derivation for the OPAM package in the current directory.

let
  # Fetch the Opam Nix integration library.
  opam-nix-integration =
    import
    (fetchTarball "https://github.com/vapourismo/opam-nix-integration/archive/master.tar.gz");

  # Fetch Nixpkgs and inject our overlay.
  pkgs =
    import
    (fetchTarball "https://github.com/NixOS/nixpkgs/archive/master.tar.gz")
    {overlays = [opam-nix-integration.overlay];};

  # Fetch the opam-repository.
  opam-repository = pkgs.fetchFromGitHub {
    owner = "ocaml";
    repo = "opam-repository";
    rev = "2a1d95dd9f9379c992e963988e89db3cf94c1788";
    sha256 = "sha256-BSbn85tRKeaxuS2iXf6fOaFTG14xrLw5LFm2opwkt+s=";
  };

  # Create a package set using some constraints against the packages available in opam-repository.
  packageSet = pkgs.opamPackages.overrideScope' (pkgs.lib.composeManyExtensions [
    # Set the opam-repository which has all our package descriptions.
    (final: prev: {
      repository = prev.repository.override {
        # You can pull from multiple repositories. They will be merged with increasing priority
        # left to right.
        srcs = [opam-repository];
      };
    })

    # Specify the constraints we have.
    (final: prev:
      prev.repository.select {
        # Set some common constraints.
        packageConstraints = [
          "ocaml = 4.14.1"
          "dune >= 3.4"
        ];

        # Include these OPAM packages in the package set.
        # This is useful for inferring the dependencies of these packages.
        opams = [
          {
            name = "nix";
            src = ./.;
          }
        ];
      })
  ]);
in
  packageSet.nix

Package set functionality

callOpam2Nix

callOpam2Nix takes 2 attribute set parameters and generates a derivation. The first configures the generation of the Nix derivation from the OPAM file. Whereas the second is passed directly to the derivation in a similar way to callPackage.

Example:

packageSet.callOpam2Nix
  {
    name = "my_package";
    version = "1.2.3";
    src = /source/of/my_package;
  }
  {
    jobs = 16;
    with-test = true;
  }

Options for the first parameter:

Name Type Required? Description
name string Yes OPAM package name
version string Yes Package version
opam path No Path to the OPAM file to processed. This will default to ${src}/${name}.opam if you omit it. That means you have to provide src when skipping opam.
src path No You can specify this parameter if you'd like to override the source for the package. If you skip it, the source specified in the OPAM file will be used.
patches list of paths No These patches will be applied to the OPAM file ahead of processing.
extraFiles path No Some packages need extra files via the extra-files stanza. Those files will be looked up in extraFiles.

These are some of the options for the second parameter:

Name Type Description
jobs int Sets the jobs OPAM variable for that package. This can be used to scale the build parallelism.
with-test bool Sets the with-test OPAM variable for that package. Usually that enables building and running tests.
with-doc bool Sets the with-doc OPAM variable for that package. In most cases documentation will be built and installed if set to true.

callOpam

callOpam is almost identical to callOpam2Nix except that it finds the right values for opam and extraFiles parameters specific to the configured OPAM repository for you.

Example:

let
  packageSet = ...;

  dune_2 = packageSet.callOpam {
    name = "dune";
    version = "2.9.3";
  } {};
in
...

repository.packages.${name}.${version}

This is a shortcut for calling callOpam { inherit name version; } {}.

Example:

let
  packageSet = ...;

  dune_2 = packageSet.repository.packages.dune."2.9.3";
in
...

repository.packages.${name}.latest

Like repository.packages.${name}.${version} but for the latest version of that package.

Example:

let
  packageSet = ...;

  latest_dune = packageSet.repository.packages.dune.latest;
in
...

repository.select

This function allows you to select an attribute set of packages given some constraints.

Parameter Type Description
packageConstraints list of strings Here you can specify which packages you'd like to have in the package set including an optional version constraint. A constraint is imposed if you add a relation operator and version after the package name like so: package = 1.2.3.
testablePackages list of strings These are the names of packages whose test dependencies should be included in the package set.
opams list of attrset This list of pinned .opam package descriptions will be included in the resolution of the package set.

Example:

let
  packageSet = ...;
in
packageSet.overrideScope' (final: prev: prev.repository.select {
  packageConstraints = ["dune >= 3.2"];
  opams = [
    { 
      name = "my-package";
      src = ./.; # Needed when you want to build the package. 
      opam = ./my-package.opam; # Optional if you specify `src`.
    }
  ];
})

Known problems

Dune 3.0 and 3.1 direct installation mode

As reported in ocaml/dune#5455, Dune 3+ wants either an explicit --prefix command-line argument or the opam executable in scope when installing build results directly. We can't do either at the moment unfortunately.

Luckily, this installation method is not super common - most Dune-based projects generate .install files instead which work fine.

A workaround has been accepted in Dune that will be available from version 3.2 onwards.

About

Nix integration for OPAM packages

Topics

Resources

License

Stars

Watchers

Forks

Languages