This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,5 @@
(library
(name fooppx)
(modes byte)
(kind ppx_rewriter)
(libraries ppxlib))

View file

@ -0,0 +1,4 @@
let () =
Ppxlib.Driver.register_transformation
"linter"
~impl:(fun s -> Format.eprintf "running rewriter@.%!"; s)

View file

@ -0,0 +1,16 @@
$ cat >dune-project <<EOF
> (lang dune 3.12)
> EOF
$ mkdir bin
$ cat > bin/dune << EOF
> (executable
> (name e)
> (preprocess (pps fooppx)))
> EOF
$ cat >bin/e.ml <<EOF
> print_endline "Hello World"
> EOF
$ dune exec -- bin/e.exe
Hello World

View file

@ -0,0 +1,9 @@
(cram
(applies_to ppx-rewriter 9650-bytecode-rewriter)
(deps
(package ppxlib)
%{bin:ocamlfind}))
(cram
(applies_to ppx-rewriter)
(deps %{bin:ocamlfind}))

View file

@ -0,0 +1,132 @@
; Too many drivers
(library
(name foo2)
(public_name foo.2)
(modules foo2)
(preprocess (pps ppx1 ppx2)))
; Incompatible with Dune
(library
(name foo3)
(public_name foo.3)
(modules foo3)
(preprocess (pps ppx_other)))
; Incompatible cookies
(library
(name foo4)
(public_name foo.4)
(modules foo4)
(preprocess (pps ppx3 ppx4)))
(rule (with-stdout-to foo1.ml (echo "")))
(rule (with-stdout-to foo2.ml (echo "")))
(rule (with-stdout-to foo3.ml (echo "")))
(rule (with-stdout-to foo4.ml (echo "")))
(library
(name ppx1)
(public_name foo.ppx1)
(kind ppx_rewriter)
(modules ())
(libraries driver1))
(library
(name ppx2)
(public_name foo.ppx2)
(kind ppx_rewriter)
(modules ())
(libraries driver2))
(library
(name ppx3)
(public_name foo.ppx3)
(kind (ppx_rewriter (cookies (germany "spritzgeback"))))
(modules ())
(libraries driver2))
(library
(name ppx4)
(public_name foo.ppx4)
(kind (ppx_rewriter (cookies (germany "lebkuchen") (library-name "%{library-name}"))))
(modules ())
(libraries driver2))
(library
(name driver1)
(public_name foo.driver1)
(modules ())
(ppx.driver (main "(fun () -> assert false)")))
(library
(name driver2)
(public_name foo.driver2)
(modules ())
(ppx.driver (main "(fun () -> assert false)")))
(library
(name ppx_other)
(public_name foo.ppx-other)
(modules ())
(kind ppx_rewriter))
(library
(name driver_print_args)
(modules ())
(ppx.driver (main "(fun () -> Array.iter print_endline Sys.argv)")))
(rule (with-stdout-to test_ppx_args.ml (echo "")))
(library
(name ppx_with_cookies_print_args)
(kind (ppx_rewriter
(cookies (italy "%{env:ITALY=undefined}")
(france "%{env:FRANCE=undefined}"))))
(modules ())
(libraries driver_print_args))
(env
(_
(env-vars
(ITALY "Biscotti")
(FRANCE "Petit Beurre")
(AMERICA "Oreo")
(ENGLAND "Snickerdoodle"))))
(library
(name test_ppx_args)
(modules test_ppx_args)
(preprocess
(pps -arg1 driver_print_args ppx_with_cookies_print_args -arg2 -arg3=%{env:AMERICA=undefined} --
-foo bar %{env:ENGLAND=undefined})))
(library
(name driver_print_tool)
(public_name foo.driver_print_tool)
(kind ppx_rewriter)
(modules ())
(libraries compiler-libs.common)
(ppx.driver (main "\| (fun () ->
"\| Ast_mapper.run_main (fun argv ->
"\| Printf.eprintf "tool name: %s\nargs:%s\n"
"\| (Ast_mapper.tool_name ())
"\| (String.concat " " argv);
"\| Ast_mapper.default_mapper))
)))
(library
(name ppx_with_cookies_print_tool)
(kind (ppx_rewriter
(cookies (italy "%{env:ITALY=undefined}")
(france "%{env:FRANCE=undefined}"))))
(modules ())
(libraries driver_print_tool))
(rule (with-stdout-to test_ppx_staged.ml (echo "")))
(library
(name test_ppx_staged)
(modules test_ppx_staged)
(preprocess
(staged_pps -arg1 driver_print_tool ppx_with_cookies_print_tool -arg2 -arg3=%{env:AMERICA=undefined} --
-foo bar %{env:ENGLAND=undefined})))

View file

@ -0,0 +1,74 @@
Too many drivers
$ dune build foo2.cma
File "dune", line 6, characters 13-28:
6 | (preprocess (pps ppx1 ppx2)))
^^^^^^^^^^^^^^^
Error: Too many incompatible ppx drivers were found: foo.driver1 and
foo.driver2.
[1]
Not compatible with Dune
$ dune build foo3.cma
File "dune", line 13, characters 13-28:
13 | (preprocess (pps ppx_other)))
^^^^^^^^^^^^^^^
Error: No ppx driver were found. It seems that ppx_other is not compatible
with Dune. Examples of ppx rewriters that are compatible with Dune are ones
using ocaml-migrate-parsetree, ppxlib or ppx_driver.
[1]
Incompatible Cookies
$ dune build foo4.cma
File "dune", line 20, characters 13-28:
20 | (preprocess (pps ppx3 ppx4)))
^^^^^^^^^^^^^^^
Error: foo.ppx3 and foo.ppx4 have inconsistent requests for cookie "germany";
foo.ppx3 requests "spritzgeback" and foo.ppx4 requests "lebkuchen"
[1]
Same, but with error pointing to .ppx
$ dune build .ppx/foo.ppx1+foo.ppx2/ppx.exe
Error: invalid ppx key for _build/default/.ppx/foo.ppx1+foo.ppx2/ppx.exe
[1]
$ dune build .ppx/foo.ppx-other/ppx.exe
Error: invalid ppx key for _build/default/.ppx/foo.ppx-other/ppx.exe
[1]
Test the argument syntax
$ dune build test_ppx_args.cma
.ppx/7e2e4f08e99331b1c723b81d3aa870ba/ppx.exe
-arg1
-arg2
-arg3=Oreo
-foo
bar
Snickerdoodle
--cookie
france="Petit Beurre"
--cookie
italy="Biscotti"
--cookie
library-name="test_ppx_args"
-o
test_ppx_args.pp.ml
--impl
test_ppx_args.ml
--as-ppx
File "dune", lines 100-101, characters 2-136:
100 | (pps -arg1 driver_print_args ppx_with_cookies_print_args -arg2 -arg3=%{env:AMERICA=undefined} --
101 | -foo bar %{env:ENGLAND=undefined})))
Error: Rule failed to generate the following targets:
- test_ppx_args.pp.ml
[1]
Test that going through the -ppx option of the compiler works
$ dune build test_ppx_staged.cma
tool name: ocamlc
args:--as-ppx -arg1 -arg2 -arg3=Oreo -foo bar Snickerdoodle --cookie france="Petit Beurre" --cookie italy="Biscotti" --cookie library-name="test_ppx_staged"

View file

@ -0,0 +1,7 @@
(library
(public_name replacesdriver)
(libraries compiler-libs.common)
(modules ())
(ppx.driver
(main "(fun () -> print_endline \"replacesdriver\"; Array.iter print_endline Sys.argv)")
(replaces testdriver)))

View file

@ -0,0 +1,4 @@
(library
(public_name testdriver)
(libraries compiler-libs.common)
(ppx.driver (main "(fun () -> Array.iter print_endline Sys.argv)")))

View file

@ -0,0 +1,5 @@
(rule (with-stdout-to driveruser.ml (echo "")))
(library
(name driveruser)
(preprocess (pps -arg1 replacesdriver -arg2 -- -foo bar)))

View file

@ -0,0 +1,13 @@
(rule (with-stdout-to driveruser.ml (echo "")))
(library
(public_name replacesdriver)
(libraries compiler-libs.common)
(modules ())
(ppx.driver
(main "(fun () -> print_endline \"replacesdriver\"; Array.iter print_endline Sys.argv)")
(replaces testdriver)))
(library
(name driveruser)
(preprocess (pps -arg1 replacesdriver -arg2 -- -foo bar)))

View file

@ -0,0 +1,75 @@
Test using installed drivers
$ dune build --root driver @install
Entering directory 'driver'
Leaving directory 'driver'
$ OCAMLPATH=driver/_build/install/default/lib dune build --root use-external-driver driveruser.cma
Entering directory 'use-external-driver'
.ppx/d1c2c0cf2b61b462226db18f72732070/ppx.exe
-arg1
-arg2
-foo
bar
--cookie
library-name="driveruser"
-o
driveruser.pp.ml
--impl
driveruser.ml
--as-ppx
File "dune", line 6, characters 13-53:
6 | (preprocess (pps -arg1 testdriver -arg2 -- -foo bar)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Rule failed to generate the following targets:
- driveruser.pp.ml
Leaving directory 'use-external-driver'
[1]
$ OCAMLPATH=driver/_build/install/default/lib dune build --root replaces driveruser.cma
Entering directory 'replaces'
replacesdriver
.ppx/5c2c5102d9f019e3d5eee93d6abe2029/ppx.exe
-arg1
-arg2
-foo
bar
--cookie
library-name="driveruser"
-o
driveruser.pp.ml
--impl
driveruser.ml
--as-ppx
File "dune", line 13, characters 13-57:
13 | (preprocess (pps -arg1 replacesdriver -arg2 -- -foo bar)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Rule failed to generate the following targets:
- driveruser.pp.ml
Leaving directory 'replaces'
[1]
$ OCAMLPATH=driver/_build/install/default/lib dune build --root driver-replaces @install
Entering directory 'driver-replaces'
Leaving directory 'driver-replaces'
$ OCAMLPATH=driver/_build/install/default/lib:driver-replaces/_build/install/default/lib dune build --root replaces-external driveruser.cma
Entering directory 'replaces-external'
replacesdriver
.ppx/5c2c5102d9f019e3d5eee93d6abe2029/ppx.exe
-arg1
-arg2
-foo
bar
--cookie
library-name="driveruser"
-o
driveruser.pp.ml
--impl
driveruser.ml
--as-ppx
File "dune", line 5, characters 13-57:
5 | (preprocess (pps -arg1 replacesdriver -arg2 -- -foo bar)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Rule failed to generate the following targets:
- driveruser.pp.ml
Leaving directory 'replaces-external'
[1]

View file

@ -0,0 +1,6 @@
(rule (with-stdout-to driveruser.ml (echo "")))
(library
(name driveruser)
(modules driveruser)
(preprocess (pps -arg1 testdriver -arg2 -- -foo bar)))

View file

@ -0,0 +1,46 @@
(library
(name driver_print_args)
(kind ppx_rewriter)
(modules ())
(ppx.driver (main "(fun () -> Array.iter print_endline Sys.argv)")))
(rule (with-stdout-to test_ppx_args.ml (echo "")))
(library
(name ppx_with_cookies_print_args)
(kind (ppx_rewriter
(cookies (italy "%{env:ITALY=undefined}")
(france "%{env:FRANCE=undefined}"))))
(modules ())
(libraries driver_print_args))
(env (_ (env-vars (ITALY "Biscotti") (FRANCE "Petit Beurre") (AMERICA "Oreo") (ENGLAND "Snickerdoodle"))))
(library
(name test_ppx_args)
(modules test_ppx_args)
(preprocess
(pps -arg1 driver_print_args ppx_with_cookies_print_args -arg2 -arg3=%{env:AMERICA=undefined} --
-foo bar %{env:ENGLAND=undefined} %{read-lines:ppx-args})))
(library
(name driver_print_tool)
(public_name foo.driver_print_tool)
(kind ppx_rewriter)
(modules ())
(libraries compiler-libs.common)
(ppx.driver (main "\| (fun () ->
"\| Ast_mapper.run_main (fun argv ->
"\| Printf.eprintf "tool name: %s\nargs:%s\n"
"\| (Ast_mapper.tool_name ())
"\| (String.concat " " argv);
"\| Ast_mapper.default_mapper))
)))
(library
(name ppx_with_cookies_print_tool)
(kind (ppx_rewriter
(cookies (italy "%{env:ITALY=undefined}")
(france "%{env:FRANCE=undefined}"))))
(modules ())
(libraries driver_print_tool))

View file

@ -0,0 +1,31 @@
Test the argument syntax with list expansion allowed (dune > 3.2)
$ dune build
.ppx/7e2e4f08e99331b1c723b81d3aa870ba/ppx.exe
-arg1
-arg2
-arg3=Oreo
-foo
bar
Snickerdoodle
Args
From
A
File
--cookie
france="Petit Beurre"
--cookie
italy="Biscotti"
--cookie
library-name="test_ppx_args"
-o
test_ppx_args.pp.ml
--impl
test_ppx_args.ml
--as-ppx
File "dune", lines 23-24, characters 3-161:
23 | (pps -arg1 driver_print_args ppx_with_cookies_print_args -arg2 -arg3=%{env:AMERICA=undefined} --
24 | -foo bar %{env:ENGLAND=undefined} %{read-lines:ppx-args})))
Error: Rule failed to generate the following targets:
- test_ppx_args.pp.ml
[1]

View file

@ -0,0 +1,16 @@
No ppx driver found
$ echo '(lang dune 2.8)' > dune-project
$ cat >dune <<EOF
> (library
> (name foo1)
> (public_name foo.1)
> (modules foo1)
> (preprocess (pps)))
> EOF
$ dune build
File "dune", line 5, characters 13-18:
5 | (preprocess (pps)))
^^^^^
Error: You must specify at least one ppx rewriter.
[1]

View file

@ -0,0 +1,49 @@
Create ppx1 and exe:
$ cat > dune-project <<EOF
> (lang dune 3.7)
> EOF
$ cat > dune <<EOF
> (library
> (name ppx)
> (kind ppx_rewriter)
> (modules ppx)
> (ppx.driver (main Ppx.main)))
> (executable
> (name the_exe)
> (modules the_exe)
> (preprocess (pps ppx --alert ++foo)))
> EOF
$ cat > ppx.ml <<EOF
> let main () =
> let out = ref "" in
> let args =
> [ ("-o", Arg.Set_string out, "")
> ; ("--impl", Arg.Set_string (ref ""), "")
> ; ("--as-ppx", Arg.Set (ref false), "")
> ; ("--cookie", Arg.Set (ref false), "")
> ; ("--alert", Arg.Set_string (ref ""), "")
> ]
> in
> let anon _ = () in
> Arg.parse (Arg.align args) anon "";
> let out = open_out !out in
> close_out out;
> EOF
$ touch the_exe.ml
$ dune build ./the_exe.exe
File "dune", line 9, characters 30-35:
9 | (preprocess (pps ppx --alert ++foo)))
^^^^^
Error: PPX args starting with `+' cannot be used before version 3.18 of the
dune language
Hint: Upgrade your dune-project to `(lang dune 3.18)'
[1]
Works since Dune 3.18
$ cat > dune-project <<EOF
> (lang dune 3.18)
> EOF
$ dune build ./the_exe.exe

View file

@ -0,0 +1,5 @@
(executable
(name w_omp_driver)
(modules w_omp_driver)
(preprocess
(pps fooppx -- -flag -arg omp)))

View file

@ -0,0 +1,2 @@
(lang dune 1.7)
(name fooppx)

View file

@ -0,0 +1,5 @@
(library
(name fooppx)
(public_name fooppx)
(kind ppx_rewriter)
(libraries ppxlib))

View file

@ -0,0 +1,19 @@
let flag = ref false
let arg = ref ""
let () =
Ppxlib.Driver.add_arg "-flag" (Arg.Set flag) ~doc:"";
Ppxlib.Driver.add_arg "-arg" (Arg.Set_string arg) ~doc:"";
Ppxlib.Driver.register_transformation
"linter"
~impl:(fun s ->
if not !flag then (
Format.eprintf "pass -flag to fooppx@.%!";
exit 1
);
if !arg = "" then (
Format.eprintf "pass -arg to fooppx@.%!"
);
Format.eprintf "-arg: %s@." !arg;
s
)

View file

@ -0,0 +1,8 @@
$ dune build ./w_omp_driver.exe
-arg: omp
This test is broken because ppx_driver doesn't support migrate custom arguments
# $ dune build ./w_ppx_driver_flags.exe
$ dune build && dune exec -- ocamlfind opt -package fooppx -ppxopt "fooppx,-flag" -linkpkg w_omp_driver.ml -o w_omp_driver.exe
pass -arg to fooppx
-arg:

View file

@ -0,0 +1,10 @@
open Ppxlib
let rules =
let extension =
Extension.declare "c" Expression Ast_pattern.__ (fun ~loc ~path:_ _ ->
Ast_builder.Default.eint ~loc [%get_c])
in
[ Context_free.Rule.extension extension ]
let () = Ppxlib.Driver.register_transformation "rules" ~rules

View file

@ -0,0 +1,10 @@
open Ppxlib
let rules =
let extension =
Extension.declare "get_c" Expression Ast_pattern.__ (fun ~loc ~path:_ _ ->
Ast_builder.Default.evar ~loc "C.c")
in
[ Context_free.Rule.extension extension ]
let () = Ppxlib.Driver.register_transformation "rules" ~rules

View file

@ -0,0 +1,168 @@
----------------------------------------------------------------------------------
Handling ppx_runtime_libraries dependencies correctly
$ cat >sdune <<'EOF'
> #!/usr/bin/env bash
> DUNE_SANDBOX=symlink dune "$@"
> EOF
$ chmod +x sdune
----------------------------------------------------------------------------------
* Incorrect cycle detection due to ppx_runtime_libraries (TODO: fix this bug!)
$ cat >dune-project <<EOF
> (lang dune 2.0)
> (implicit_transitive_deps true)
> EOF
$ cat >dune <<EOF
> (library
> (name a)
> (modules a)
> (libraries b))
> (library
> (name b)
> (kind ppx_rewriter)
> (modules b ppx)
> (libraries ppxlib)
> (ppx_runtime_libraries c))
> (library
> (name c)
> (modules c)
> (libraries a))
> EOF
$ mkdir -p bin
$ cat >bin/dune <<EOF
> (executable
> (name main)
> (preprocess (pps b))
> (modules main))
> EOF
$ cat >a.ml <<EOF
> include B
> let a = B.b - 1
> EOF
$ cat >b.ml <<EOF
> let b = 2
> EOF
$ cat >c.ml <<EOF
> include A
> let c = A.a + 2
> EOF
$ cat >bin/main.ml <<EOF
> let () = Printf.printf "Should print 3: %d\n" [%get_c]
> EOF
$ ./sdune exec bin/main.exe
Error: Dependency cycle between:
library "b" in _build/default
-> library "a" in _build/default
-> library "c" in _build/default
-> library "b" in _build/default
[1]
----------------------------------------------------------------------------------
* Ppx rewriters (and their ppx_runtime_libraries information) are collected recursively
In this case we have the following dependency graph:
main --[pps]--> b ---> ppx --[runtime]--> c ---> a
Note the direct dependency b ---> ppx that separates pps and runtime dependencies.
$ cat >dune <<EOF
> (library
> (name a)
> (modules a))
> (library
> (name b)
> (modules b)
> (libraries ppx))
> (library
> (name ppx)
> (modules ppx)
> (kind ppx_rewriter)
> (libraries ppxlib)
> (ppx_runtime_libraries c))
> (library
> (name c)
> (modules c)
> (libraries a))
> EOF
$ cat >a.ml <<EOF
> let a = 2 - 1
> EOF
$ ./sdune exec bin/main.exe
Should print 3: 3
----------------------------------------------------------------------------------
* Since Dune 2.2, it is not allowed to have a pps dependency on a library that has
not been marked with (kind ppx_rewriter).
$ cat >dune-project <<EOF
> (lang dune 2.2)
> (implicit_transitive_deps true)
> EOF
$ ./sdune exec bin/main.exe
File "bin/dune", line 3, characters 18-19:
3 | (preprocess (pps b))
^
Error: Ppx dependency on a non-ppx library "b". If "b" is in fact a ppx
rewriter library, it should have (kind ppx_rewriter) in its dune file.
-> required by _build/default/bin/.main.eobjs/native/dune__exe__Main.cmx
-> required by _build/default/bin/main.exe
[1]
----------------------------------------------------------------------------------
* Dependency cycle between ppx rewriters created via [ppx_runtime_libraries]
In this case we have the following dependency graph:
gen_c --[pps]--> ppx --[runtime]--> c --[pps]--> gen_c
Note that pps dependencies are separated by a runtime dependency.
$ cat >dune <<EOF
> (library
> (name gen_c)
> (modules gen_c)
> (kind ppx_rewriter)
> (libraries ppxlib)
> (preprocess (pps ppx)))
> (library
> (name ppx)
> (modules ppx)
> (kind ppx_rewriter)
> (libraries ppxlib)
> (ppx_runtime_libraries c))
> (library
> (name c)
> (modules c)
> (libraries ppxlib)
> (preprocess (pps gen_c)))
> EOF
$ cat >c.ml <<EOF
> let c = [%c]
> EOF
$ cat >bin/dune <<EOF
> (executable
> (name main)
> (libraries c)
> (modules main))
> EOF
$ cat >bin/main.ml <<EOF
> let () = Printf.printf "Should be impossible: %d\n" C.c
> EOF
$ ./sdune exec bin/main.exe
Error: Dependency cycle between:
library "c" in _build/default
-> library "ppx" in _build/default
-> library "gen_c" in _build/default
-> library "c" in _build/default
[1]

View file

@ -0,0 +1,58 @@
Create ppx1, ppx2 and exe:
- ppx1 is a regular ppx rewriter
- ppx2 is also a ppx rewriter, preprocessed with ppx1
- exe depends on ppx2
$ mkdir -p project/ppx/ppx1 project/ppx/ppx2 project/exe
$ cat > project/ppx/dune-project <<EOF
> (lang dune 3.7)
> (package (name the-ppx))
> EOF
$ cat > project/ppx/ppx1/dune <<EOF
> (library
> (name ppx1)
> (public_name the-ppx.ppx1)
> (kind ppx_rewriter)
> (ppx.driver (main Ppx1.main)))
> EOF
$ cat > project/ppx/ppx1/ppx1.ml <<EOF
> let main () =
> let out = ref "" in
> let args =
> [ ("-o", Arg.Set_string out, "")
> ; ("--impl", Arg.Set_string (ref ""), "")
> ; ("--as-ppx", Arg.Set (ref false), "")
> ; ("--cookie", Arg.Set (ref false), "")
> ]
> in
> let anon _ = () in
> Arg.parse (Arg.align args) anon "";
> let out = open_out !out in
> close_out out;
> EOF
$ cat > project/ppx/ppx2/dune <<EOF
> (library
> (name ppx2)
> (public_name the-ppx.ppx2)
> (preprocess (pps ppx1)) ; changing to the-ppx.ppx1 makes it work
> (kind ppx_rewriter))
> EOF
$ cat > project/ppx/ppx2/ppx2.ml <<EOF
> let main () = ()
> EOF
$ cat > project/dune-project <<EOF
> (lang dune 3.7)
> (package (name the-exe))
> EOF
$ cat > project/exe/dune <<EOF
> (executable
> (name the_exe)
> (public_name the-exe)
> (libraries the-ppx.ppx2))
> EOF
$ touch project/exe/the_exe.ml
$ OCAMLPATH=$PWD/prefix/lib/:$OCAMLPATH dune build --root project ./exe/the_exe.exe
Entering directory 'project'
Leaving directory 'project'

View file

@ -0,0 +1,2 @@
(lang dune 1.7)
(name a)

View file

@ -0,0 +1,5 @@
(library
(name a_kernel)
(public_name a.kernel)
(kind ppx_rewriter)
(libraries ppxlib))

View file

@ -0,0 +1,5 @@
(library
(name a)
(public_name a)
(kind ppx_rewriter)
(libraries a_kernel))

View file

@ -0,0 +1,5 @@
(library
(name b)
(public_name b)
(preprocess
(pps a)))

View file

@ -0,0 +1,2 @@
(lang dune 1.7)
(name b)

View file

@ -0,0 +1,4 @@
This test reproduces a scope resolution bug when resolving the preprocessor of a
library.
$ dune build b/b.cma @install