If you use Jane’s Core.Command
for command line parsing (and if not, why aren’t you?), it can take arguments like -version
and -build-info
which are super useful if you want to bake in important build information.
Aside: why does anyone want this? One excellent release engineer use-case is to figure out which binaries are more than, say, 6 months out of date and schedule them for a refresh. Another one is if you want to hotfix a bug in a binary, but not introduce all of the changes made in the main repo, you can git checkout the version baked into some ad-hoc binary that nobody is quite sure when it was released, and then roll a new release of that which only contains your fix.
Anyway, to get this, you have to have your build system generate some info that you then pass into Core.Command
.
I’m having a bit of trouble doing this with dune
. I can follow these directions and get pretty close to what I want: https://github.com/ocaml/dune/tree/master/example/sample-projects/with-configure-step
I can stick a dune stanza like this into my dune
file:
(rule
(target build_info_gen.ml)
(action (run ocaml %{dep:gen_build_info.ml})))m
which runs an script called gen_build_info.ml
beaver$ cat gen_build_info.ml
#load "unix.cma"
let sh_line s =
let ic = Unix.open_process_in s in
let line = input_line ic in
close_in ic;
line
;;
let () =
let oc = open_out_bin "build_info_gen.ml" in
Printf.fprintf oc {|
let git_revision = "%s" ;;
let build_host = "%s" ;;
let build_user = "%s" ;;
let build_time = "%s" ;;
let ocamlopt_version = "%s" ;;
|} (sh_line "git describe --always --dirty")
(sh_line "hostname")
(sh_line "whoami")
(sh_line "date")
(sh_line "ocamlopt --version");
close_out oc
;;
What that will do is generate a module called build_info_gen.ml
, which I can use in a module called build_info.ml
with sexp to make auto-generation easy.
$ cat build_info.ml
open! Core
type t =
{ git_revision : string
; build_user : string
; build_host : string
; build_time : string
; ocamlopt_version : string }
[@@deriving sexp]
let t =
{ git_revision = Build_info_gen.git_revision
; build_user = Build_info_gen.build_user
; build_host = Build_info_gen.build_host
; build_time = Build_info_gen.build_time
; ocamlopt_version = Build_info_gen.ocamlopt_version }
let version = t.git_revision
let build_info = sexp_of_t t |> Sexp.to_string_hum
Which I can feed into Command
in mymain.ml
and get cool stuff to happen:
open! Core
(*...*)
let () =
Command.run
~version:Build_info.version
~build_info:Build_info.build_info cmd
Cool stuff:
beaver$ mymain.exe -build-info | sexp pp
((git_revision 095e463-dirty)
(build_user mbac)
(build_host beaver)
(build_time "Fri Jan 3 10:10:44 PST 2020")
(ocamlopt_version 4.08.1))
(You can get the sexp
tool with opam install sexp
if you don’t already have it)
This almost works perfectly. What I’m having trouble doing is convincing dune
it should regenerate build_info_gen.ml
every time it tries to build an executable, not just the first time I build the executable.
Any pointers on this?