I'm using NixOS and compiling the cohttp server example using dune. The example is notable in that it links to two C-libraries: openssl and libev.
initial attempt
Here's my shell.nix:
with import <nixpkgs> { };
let spec = {
buildInputs = with ocamlPackages; [
ocaml
findlib
dune
# ocaml libs (and external library deps)
cohttp-lwt-unix openssl libev
]);
};
in runCommand "dummy" spec ""
Here's my dune file:
(executable
(name server_example)
(libraries cohttp-lwt-unix))
And the output of dune build server_example.exe
...
/nix/store/3xwc1ip20b0p68sxqbjjll0va4pv5hbv-binutils-2.30/bin/ld: cannot find -lssl
/nix/store/3xwc1ip20b0p68sxqbjjll0va4pv5hbv-binutils-2.30/bin/ld: cannot find -lcrypto
/nix/store/3xwc1ip20b0p68sxqbjjll0va4pv5hbv-binutils-2.30/bin/ld: cannot find -lev
Ok, not terribly surprising since these are in non-standard locations in NixOS. I need to add the relevant paths to the ocamlopt commandline invoked by dune, e.g.: -I ${openssl.out}/lib -I ${libev}/lib
.
Now, openssl includes pkg-config files, but adding pkg-config to my shell.nix
has no apparent effect.
second attempt, using configurator
I used configurator to create a program to add flags from an environment variable to the build flags of my dune executable.
shell.nix
with import <nixpkgs> { };
let spec = {
buildInputs = with ocamlPackages; [
ocaml
findlib
dune
configurator
# ocaml libs (and external library deps)
cohttp-lwt-unix openssl libev
]);
shellHook = ''
export OCAML_FLAGS="-I ${openssl.out}/lib -I ${libev}/lib"
'';
};
in runCommand "dummy" spec ""
dune
(executable
(name server_example)
(flags (:standard (:include flags.sexp)))
(libraries cohttp-lwt-unix))
(rule
(targets flags.sexp)
(deps (:discover config/discover.exe))
(action (run %{discover})))
config/dune
(executable
(name discover)
(libraries dune.configurator))
config/discover.ml
open Sys
module C = Configurator.V1
let () =
C.main ~name:"getflags" (fun _c ->
let libs =
match getenv_opt "OCAML_FLAGS" with
| None -> []
| Some flags -> C.Flags.extract_blank_separated_words flags
in
C.Flags.write_sexp "flags.sexp" libs)
Compilation now succeeds, but this approach of writing a custom program to take an environment variable and put it into the flags argument seems clunky.
Is there a standard way to accomplish this in dune (adding paths to ocamlopt commandline with -I
)?
If not, is there an easier way to read an environment variable from within dune
files?
Use stdenv.mkDerivation
or (as suggested by Robert Hensing above) ocamlPackages.buildDunePackage
instead of runCommand
. The latter results in an environment that does not include openssl and libev in the NIX_CFLAGS_COMPILE
or NIX_LDFLAGS
environment variables. Using mkDerivation
does result in those variables being populated.
Once those variables are in place, compilation via dune
succeeds, and the executable is linked to libssl and libev.
Furthermore, explicit inclusion of libev and openssl is unnecessary since they are declared as propagated build inputs of through cohttp-lwt-unix.
shell.nix:
with import <nixpkgs> { };
with ocamlPackages;
buildDunePackage {
pname = "dummy";
version = "0";
buildInputs = [
cohttp-lwt-unix
];
}