{ config, lib, pkgs, ... }: with lib; with builtins; let cfg = config.ocaml; tuaregPackages = optionals cfg.tuareg.enable (with pkgs; [ ocamlformat opam ]) ++ (with cfg.ocamlPackages; [ merlin ocp-indent utop ]); userPackages = cfg.packages cfg.ocamlPackages; ocamlBuildInputs = (with cfg.ocamlPackages; [ ocaml findlib ]) ++ (with pkgs; if versionAtLeast cfg.version "4.12" then [ dune_2 ] else (optional (versionAtLeast cfg.version "4.02") dune_1)) ++ tuaregPackages ++ userPackages; ocamlInit = pkgs.writeText "ocamlinit" ('' let () = try Topdirs.dir_directory "${cfg.ocamlPackages.findlib}/lib/ocaml/${cfg.version}/site-lib" with Not_found -> () ;; #use "topfind";; '' + (optionalString cfg.toplevel.list "#list;;") + (optionalString cfg.toplevel.thread "#thread;;") + (concatStringsSep "\n" (map (pkg: ''#require "${pkg.pname}";;'') userPackages)) + cfg.toplevel.extraInit); in { options.ocaml = { enable = mkEnableOption { name = "ocaml"; }; version = mkOption { type = types.uniq types.str; default = (builtins.parseDrvName cfg.ocamlPackages.ocaml.name).version; defaultText = literalExample "(builtins.parseDrvName config.ocaml.ocamlPackages.ocaml.name).version"; description = '' The version of OCaml. This option only exist for propagating the version of OCaml through the configuration. As such, it should not be set manually but through the ocamlPackages option. ''; }; ocamlPackages = mkOption { type = types.lazyAttrsOf types.package; default = pkgs.ocamlPackages; defaultText = literalExample "pkgs.ocamlPackages"; description = '' The set of OCaml packages from which to get OCaml and its packages. Use this option to set the version of OCaml. ''; example = literalExample "pkgs.ocaml-ng.ocamlPackages_4_11"; }; packages = mkOption { type = with types; functionTo (listOf package); default = _: [ ]; defaultText = literalExample "_ : []"; description = '' OCaml packages that will be made available to the environment. ''; example = literalExample '' ocamlPackages: with ocamlPackages; [ owl lwt ]; ''; }; toplevel = { require = mkOption { type = types.listOf types.str; default = builtins.map (pkg: pkg.pname) cfg.packages; defaultText = "builtins.map (pkg: pkg.pname) config.ocaml.packages"; description = '' The list of names of packages to load when launching a top-level. ''; example = [ "owl" "lwt" ]; }; # Whether to list loaded packages when launching a top-level. list = mkEnableOption "#require list;;"; # Whether to enable threading when running a top-level. thread = mkEnableOption "#require thread;;"; extraInit = mkOption { type = types.lines; default = ""; description = '' Additional commands to run when running a top-level. ''; example = "Topfind.reset();;"; }; }; # Whether to load packages required by Tuareg (Emacs' OCaml mode). tuareg.enable = mkEnableOption "tuareg"; }; config = mkIf cfg.enable { buildInputs = ocamlBuildInputs; aliases = { utop = let utops = builtins.filter (p: match "(.*-utop)" (parseDrvName p.name).name != null) ocamlBuildInputs; utop = head utops; in mkIf (utops != [ ]) ''${utop}/bin/utop -init "${ocamlInit}"''; ocaml = ''${cfg.ocamlPackages.ocaml}/bin/ocaml -init "${ocamlInit}"''; }; }; }