How are you âcheckingâ if the executable is behaving how you expect? Depending on that you may be able to turn that part into an automated cram test.
If you are somehow asserting that foo.ml and bar.rs have the correct contents, I often use cram for this. If you are just needed to generate them and then use themâŚhave you considered using a dune rule to do that?
No. Iâm living dangerously by âif it compiles; itâs (probably) rightâ.
It is not clear what this buys me.
We have the following:
types.spec: contains original specs
conv.ml: program converting the specs
out_ocaml.ml : ocaml output
out_rust.rs : rust output
except I intend to combine types.spec/conv.ml into one file, and encode the âtypesâ of types.spec as values in conv.ml (as itâs something I rarely modify, and the only thing I care about is machine generated intâs for the type_ids so all the tags / layouts line up).
If you combine the spec and the conv.ml into a single file, doesnât that mean you wonât need to run the separate dune exec bin/ffi_gen.exe -w any more? The conv.ml will just be part of your normal dune build, right?
EDIT: or, if you are planning to make conv.ml continue to be a program that generates both OCaml and Rust code, then the benefit of using a dune rule is that the generated foo.ml file will be defined as part of your regular build, and you can use the Foo module in your project and dune will take care of regenerating both foo.ml and bar.rs if foo.ml gets out of date. You wonât need to run dune exec conv.exe -w separately.
I am going to preface this by saying: I know Cargo; I know Makefiles. I do NOT know dune. With dune, I have a ânearest neighborâ understanding, not a âfrom first principlesâ understanding. So it is entirely possible I am misunderstanding what you are saying here.
Every time I modify types.spec (part of conv.ml), I need to execute conv.ml in order to have it generate /path1/output_ocaml.ml and /path2/output_rust.rs .
I do not see how this is dine without a âdune execâ; nor do I see the benefit of making this a rule.
If you have time, could I trouble you for a dune + conv.ml, where conv.ml just writesx âhello ocamlâ to /path1/ocaml.ml and âhello rustâ to /path2/rust.rs ?
Right now, I have no idea what this âalternative to dune execâ looks like.
Sorry for chiming in, my very rough understanding is that a binary of a rule is a dependency of the rule, so if the source of the ruleâs binary changes, the rule will build and execute the binary so that the result of the rule is fresh wrt. the dependencies of the rule.
conv/
(* conv.ml *)
let () =
let ml = open_out_bin "foo.ml"
and rs = open_out_bin "foo.rs" in
output_string ml "let f x = x";
output_string rs "pub fn f(x: i32) -> i32 { x }";
close_out ml;
close_out rs
; dune
(executable
(name conv))
ml/
; dune
(executable
(name ml))
(rule
(mode (promote (only foo.rs)))
(targets foo.ml foo.rs)
(action
(run ../conv/conv.exe)))
(* ml.ml *)
let () = print_int (Foo.f 1)
This writes the foo.rs in the ml directory as well. But you can, I think without too much trouble, configure it to save in any directory you prefer. It will be produced and saved as a side effect of running the dune build.
Thank you for the working example. I am now convinced the âruleâ approach is better.
This is a bit âtooâ magical right now. How is dune inferring the dependencies ?
When I change conv.ml â rust.rs gets updated. What is happening here ?
2a. from the âruleâ line, dune has to infer that âfoo.rsâ depends on ââŚ/conv/conv.exeâ
2b. then from the ââŚ/conv/conv.exeâ part, dune has to somehow infer that this depends on âconv.mlâ â this part is a bit magical to me
2c. is it making the inference in 2b, or is the reason that âdune buildâ auto build âconv.exeâ whenever âconv.mlâ changes, and the updated conv.exe triggers the rule to execute it ?
By the rule in the same directory, it knows to run ../conv/conv.exe to generate foo.ml
By the executable stanza in conv/dune, it knows to build conv/conv.exe from conv/conv.ml.
In my opinion at heart dune is not much different from make. It operates on a system of targets and dependencies. Usually we donât think about it because it already encodes the rules for OCaml sources, but for codegen like this we make it explicit.
I actually wasnât running this step. I was running dune build -w, and everytime I modified conv.ml, it apparently recompiled it, ran it, and put the foo.rs into the right location. (via the promote)