tweag / opam-nix

Turn opam-based OCaml projects into Nix derivations
MIT License
111 stars 33 forks source link

Document adding dependencies to dune projects #51

Closed gleachkr closed 1 year ago

gleachkr commented 1 year ago

Is your feature request related to a problem? Please describe.

It's not obvious how to add dependencies to a dune project. There's a particular ocaml tool that I'm able to build with opam-nix, but only by overriding the build phase to use dune build --release, and by manually adding packages to a depends in the dune-project. Since I don't maintain this tool, I'd like to be able to add the dependencies from within nix.

Describe the solution you'd like

I'd like documentation of how to add a package dependency from within the nix file in such a way that the effect is the same as adding the dependency from within the dune-project file.

Describe alternatives you've considered

I've tried some probably silly stuff: applying a patch to the dune-project during the patching phase, using passthru to override depends from builder.nix, and adding the dependencies to the query argument in buildDuneProject.

Additional context Here's a reference flake:

  inputs = {
    opam-nix.url = "github:tweag/opam-nix";
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.follows = "opam-nix/nixpkgs";
    sailRepo = {
      url = "github:rems-project/sail";
      flake = false;
    };
  };
  outputs = { self, flake-utils, opam-nix, sailRepo, nixpkgs }@inputs:
    flake-utils.lib.eachDefaultSystem (system:
      let
        on = opam-nix.lib.${system};
        scope = on.buildDuneProject { } "sail" sailRepo { 
          ocaml-base-compiler = "*"; 
        };
        overlay = final: prev:
          {
            sail = prev.sail.overrideAttrs (_: {
              buildPhase = "dune build --release"; 
              postFixup = "mv $out/share/sail/lib $out/share/lib";
            });
          };
      in {
        legacyPackages = scope.overrideScope' overlay;

        packages.default = self.legacyPackages.${system}.sail;
      });
}

Using this, nix build fails because of a missing base64 and omd dependency. If you apply the following patch to a clone of the sail repo

diff --git a/dune-project b/dune-project
index 23ab4217..76f403f1 100644
--- a/dune-project
+++ b/dune-project
@@ -135,4 +135,6 @@ http://www.cl.cam.ac.uk/~pes20/sail/.
     (sail_latex_backend (and (= :version) :post))
     (sail_doc_backend (and (= :version) :post))
     (sail_output (and (= :version) :post))
+    (base64 (>= 3.1.0))
+    (omd (and (>= 1.3.1) (< 1.4.0)))
     (linenoise (>= 1.1.0))))

and build in that context (moving the flake above to within the clone, and using ./. rather than sailRepo), everything builds correctly.

balsoft commented 1 year ago

Hi! To add dependencies to a package, you need to (1) add them to your scope, (2) take them from the scope and add them to buildInputs of your package. Something like this:

{
    inputs = {
    opam-nix.url = "github:tweag/opam-nix";
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.follows = "opam-nix/nixpkgs";
    sailRepo = {
      url = "github:rems-project/sail";
      flake = false;
    };
  };
  outputs = { self, flake-utils, opam-nix, sailRepo, nixpkgs }@inputs:
    flake-utils.lib.eachDefaultSystem (system:
      let
        on = opam-nix.lib.${system};
        scope = on.buildDuneProject { } "sail" sailRepo { 
          ocaml-base-compiler = "*";
          # Ask opam to add any suitable version of these packages to the scope
          base64 = "*";
          omd = "*";
        };
        overlay = final: prev:
          {
            # `oa` stands for "Old Attributes", the attributes of the derivation we are overriding
            sail = prev.sail.overrideAttrs (oa: {
              # Take omd and base64 from the scope and add them to the `buildInputs` that are already there
              buildInputs = oa.buildInputs ++ [ final.omd final.base64 ];
              buildPhase = "dune build --release"; 
              postFixup = "mv $out/share/sail/lib $out/share/lib";
            });
          };
      in {
        legacyPackages = scope.overrideScope' overlay;

        packages.default = self.legacyPackages.${system}.sail;
      });
}

I believe the first part is already documented, the second one should indeed be documented as part of "Package" documentation.

gleachkr commented 1 year ago

Outstanding. Thanks for your help!