Skip to content

Commit

Permalink
Map (re_export) to exports field in META files (ocaml#10831)
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
  • Loading branch information
nojb authored Aug 19, 2024
1 parent c04513f commit 2c811fe
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 4 deletions.
6 changes: 6 additions & 0 deletions doc/changes/10831.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Map `(re_export)` library dependencies to the `exports` field in `META` files,
and vice-versa. This field was proposed in to
https://discuss.ocaml.org/t/proposal-a-new-exports-field-in-findlib-meta-files/13947.
The field is included in Dune-generated `META` files only when the Dune lang
version is >= 3.17.
(#10831, @nojb)
5 changes: 5 additions & 0 deletions src/dune_findlib/package0.ml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ let requires t =
|> List.map ~f:(fun s -> Lib_name.parse_string_exn (Loc.none, s))
;;

let exports t =
Vars.get_words t.vars "exports" Ps.empty
|> List.map ~f:(fun s -> Lib_name.parse_string_exn (Loc.none, s))
;;

let ppx_runtime_deps t =
Vars.get_words t.vars "ppx_runtime_deps" preds
|> List.map ~f:(fun s -> Lib_name.parse_string_exn (Loc.none, s))
Expand Down
1 change: 1 addition & 0 deletions src/dune_findlib/package0.mli
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ val version : t -> string option
val description : t -> string option
val jsoo_runtime : t -> Path.t list
val requires : t -> Lib_name.t list
val exports : t -> Lib_name.t list
val ppx_runtime_deps : t -> Lib_name.t list
val kind : t -> Lib_kind.t
val archives : t -> Path.t list Mode.Dict.t
Expand Down
7 changes: 6 additions & 1 deletion src/dune_rules/findlib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,13 @@ let to_dune_library (t : Findlib.Package.t) ~dir_contents ~ext_lib ~external_loc
let main_module_name : Lib_info.Main_module_name.t = This None in
let enabled = Memo.return Lib_info.Enabled_status.Normal in
let requires =
let exports = Lib_name.Set.of_list (Findlib.Package.exports t) in
Findlib.Package.requires t
|> List.map ~f:(fun name -> Lib_dep.direct (add_loc name))
|> List.map ~f:(fun name ->
let lib_dep =
if Lib_name.Set.mem exports name then Lib_dep.re_export else Lib_dep.direct
in
lib_dep (add_loc name))
in
let ppx_runtime_deps = List.map ~f:add_loc (Findlib.Package.ppx_runtime_deps t) in
let special_builtin_support : (Loc.t * Lib_info.Special_builtin_support.t) option =
Expand Down
11 changes: 10 additions & 1 deletion src/dune_rules/gen_meta.ml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ end
let string_of_deps deps = Lib_name.Set.to_string_list deps |> String.concat ~sep:" "
let rule var predicates action value = Rule { var; predicates; action; value }
let requires ?(preds = []) pkgs = rule "requires" preds Set (string_of_deps pkgs)
let exports pkgs = rule "exports" [] Set (string_of_deps pkgs)

let ppx_runtime_deps ?(preds = []) pkgs =
rule "ppx_runtime_deps" preds Set (string_of_deps pkgs)
Expand Down Expand Up @@ -87,6 +88,7 @@ let gen_lib pub_name lib ~version =
in
let to_names = Lib_name.Set.of_list_map ~f:name in
let* lib_deps = Resolve.Memo.read_memo (Lib.requires lib) >>| to_names in
let* lib_re_exports = Resolve.Memo.read_memo (Lib.re_exports lib) >>| to_names in
let* ppx_rt_deps =
Lib.ppx_runtime_deps lib |> Memo.bind ~f:Resolve.read_memo |> Memo.map ~f:to_names
in
Expand All @@ -109,6 +111,12 @@ let gen_lib pub_name lib ~version =
List.concat
[ version
; [ description desc; requires ~preds lib_deps ]
; (if (match Lib.project lib with
| None -> true
| Some project -> Dune_project.dune_version project < (3, 17))
|| Lib_name.Set.is_empty lib_re_exports
then []
else [ exports lib_re_exports ])
; archives ~preds lib
; (if Lib_name.Set.is_empty ppx_rt_deps
then []
Expand Down Expand Up @@ -195,9 +203,10 @@ let gen ~(package : Package.t) ~add_directory_entry entries =
pub_name, entries)
| Deprecated_library_name
{ old_name = old_public_name, _; new_public_name = _, new_public_name; _ } ->
let deps = Lib_name.Set.singleton new_public_name in
Memo.return
( Pub_name.of_lib_name (Public_lib.name old_public_name)
, version @ [ requires (Lib_name.Set.singleton new_public_name) ] ))
, version @ [ requires deps; exports deps ] ))
in
let pkgs =
List.map pkgs ~f:(fun (pn, meta) ->
Expand Down
1 change: 1 addition & 0 deletions src/dune_rules/lib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ let info t = t.info
let project t = t.project
let implements t = Option.map ~f:Memo.return t.implements
let requires t = Memo.return t.requires
let re_exports t = Memo.return t.re_exports
let ppx_runtime_deps t = Memo.return t.ppx_runtime_deps
let pps t = Memo.return t.pps

Expand Down
1 change: 1 addition & 0 deletions src/dune_rules/lib.mli
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ val wrapped : t -> Wrapped.t option Resolve.Memo.t
(** Direct library dependencies of this library *)
val requires : t -> t list Resolve.Memo.t

val re_exports : t -> t list Resolve.Memo.t
val ppx_runtime_deps : t -> t list Resolve.Memo.t
val pps : t -> t list Resolve.Memo.t

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ tests that the "old_public_name" field is evaluated lazily

$ dune_cmd cat $PWD/_install/lib/a/META
requires = "b"
exports = "b"

$ dune_cmd cat $PWD/_install/lib/a/dune-package | sed "s/(lang dune .*)/(lang dune <version>)/"
(lang dune <version>)
Expand Down Expand Up @@ -144,8 +145,10 @@ First the motivating case.

$ dune_cmd cat d/_build/install/default/lib/menhirLib/META
requires = "menhir.lib"
exports = "menhir.lib"
$ dune_cmd cat d/_build/install/default/lib/menhirSdk/META
requires = "menhir.sdk"
exports = "menhir.sdk"

$ find d/_build/install/default -name 'dune-package' | sort
d/_build/install/default/lib/dummy/dune-package
Expand Down Expand Up @@ -208,6 +211,7 @@ Checks that we can migrate top-level libraries across packages.

$ dune_cmd cat d/_build/install/default/lib/top1/META
requires = "q.bar"
exports = "q.bar"

Check that we can do it when the name of the new library is the same as the
old public name:
Expand All @@ -227,6 +231,7 @@ old public name:

$ dune_cmd cat d/_build/install/default/lib/top2/META
requires = "q.top2"
exports = "q.top2"

We check that there is an error when there is an actual ambiguity:

Expand Down Expand Up @@ -304,6 +309,7 @@ Qualified, deprecated old_public_name:
$ dune_cmd cat d/_build/install/default/lib/q/META
package "foo" (
requires = "p"
exports = "p"
)

$ find d/_build/install/default -name 'dune-package' | sort
Expand Down Expand Up @@ -345,9 +351,11 @@ Two libraries redirecting to the same library:
$ dune_cmd cat d/_build/install/default/lib/q/META
package "bar" (
requires = "p"
exports = "p"
)
package "foo" (
requires = "p"
exports = "p"
)

$ find d/_build/install/default -name 'dune-package' | sort
Expand Down
118 changes: 118 additions & 0 deletions test/blackbox-tests/test-cases/meta-exports.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
Check our handling of `exports` in META files. We begin with a test showing that
we can *consume* META files containing an `exports` field.

To do this, first we create a Findlib hierarchy containing two installed
packages, `foo` and `bar`. The package `foo` is empty and only exists to
re-export `bar`. The package `bar` consists of a bytecode library, `bar.cma`.

$ mkdir -p _install/foo
$ cat >_install/foo/META <<EOF
> requires = "bar"
> EOF

$ mkdir -p _install/bar
$ cat >_install/bar/META <<EOF
> archive(byte) = "bar.cma"
> EOF
$ cat >_install/bar/bar.ml <<EOF
> let x = 42
> EOF
$ ocamlc -a -o _install/bar/bar.cma _install/bar/bar.ml

We now define a Dune project that will consume `foo`.

$ cat >dune-project <<EOF
> (lang dune 3.0)
> EOF

$ cat >dune <<EOF
> (executable
> (name main)
> (modes byte)
> (libraries foo))
> EOF
$ cat >main.ml <<EOF
> let () = print_int Bar.x; print_newline ()
> EOF

Compilation works with `(implicit_transitive_deps)`:

$ OCAMLPATH=$(pwd)/_install dune exec ./main.exe
42

However, the compilation without `(implicit_transitive_deps)` fails:

$ cat >dune-project <<EOF
> (lang dune 3.0)
> (implicit_transitive_deps false)
> EOF

$ OCAMLPATH=$(pwd)/_install dune exec ./main.exe
File "main.ml", line 1, characters 19-24:
1 | let () = print_int Bar.x; print_newline ()
^^^^^
Error: Unbound module Bar
[1]

Next, we add the `exports` field to `foo`'s `META` file:
$ cat >_install/foo/META <<EOF
> requires = "bar"
> exports = "bar"
> EOF
and compilation now works again:
$ OCAMLPATH=$(pwd)/_install dune exec ./main.exe
42
----------------------------------------------------------------
Next, we check that we can *produce* META files with the `export` field. To do
this, we define two Dune libraries `foo` and `bar`, where `foo` depends on `bar`
using `(re_export)`.
$ cat >dune-project.gen <<'EOF'
> cat <<EOF2
> (lang dune $VERSION)
> (package (name foo))
> (package (name bar))
> EOF2
> EOF

$ cat >dune <<EOF
> (library
> (public_name bar)
> (modules bar))
> (library
> (public_name foo)
> (libraries (re_export bar))
> (modules foo))
> EOF
$ true >bar.ml
$ true >foo.ml

First we try with dune version 3.16 (it should not generate the `exports` field):

$ VERSION=3.16 sh dune-project.gen >dune-project
$ dune build && dune install --libdir $(pwd)/_local
$ cat _local/foo/META
description = ""
requires = "bar"
archive(byte) = "foo.cma"
archive(native) = "foo.cmxa"
plugin(byte) = "foo.cma"
plugin(native) = "foo.cmxs"

Now with dune version 3.17 (it should generate the `exports` field):

$ VERSION=3.17 sh dune-project.gen >dune-project
$ dune build && dune install --libdir $(pwd)/_local
$ cat _local/foo/META
description = ""
requires = "bar"
exports = "bar"
archive(byte) = "foo.cma"
archive(native) = "foo.cmxa"
plugin(byte) = "foo.cma"
plugin(native) = "foo.cmxs"
2 changes: 1 addition & 1 deletion test/expect-tests/findlib_tests.ml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ let%expect_test _ =
let dyn = Dyn.list Lib_dep.to_dyn requires in
let pp = Dyn.pp dyn in
Format.printf "%a@." Pp.to_fmt pp;
[%expect {|[ "baz" ]|}]
[%expect {|[ re_export "baz"; "xyz" ]|}]
;;

(* Meta parsing/simplification *)
Expand Down
3 changes: 2 additions & 1 deletion test/unit-tests/findlib-db/foo/META
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
requires = "bar"
requires(ppx_driver) = "baz"
requires(ppx_driver) = "baz xyz"
exports = "baz wrong"

0 comments on commit 2c811fe

Please sign in to comment.