Skip to content

Commit 485a58e

Browse files
nojbjeremiedimino
andauthored
Add support for (empty_module_interface_if_absent) (ocaml#4955)
Signed-off-by: nojebar <[email protected]> Co-authored-by: Jérémie Dimino <[email protected]>
1 parent 0b5564e commit 485a58e

File tree

9 files changed

+119
-17
lines changed

9 files changed

+119
-17
lines changed

CHANGES.md

+3
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ Unreleased
201201
- Dune no longer reads installed META files for libraries distributed with the
202202
compiler, instead using its own internal database. (#4946, @nojb)
203203

204+
- Add support for `(empty_module_interface_if_absent)` in executable and library
205+
stanzas. (#4955, @nojb)
206+
204207
2.9.1 (07/09/2021)
205208
------------------
206209

doc/dune-files.rst

+10
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,11 @@ to use the :ref:`include_subdirs` stanza.
626626
is useful whenever a library is shadowed by a local module. The library may
627627
then still be accessible via this root module
628628

629+
- ``(empty_module_interface_if_absent)`` causes the generation of empty
630+
interfaces for every module that does not have an interface file already.
631+
Useful when modules are used solely for their side-effects. This field is
632+
available since the 3.0 version of the dune language.
633+
629634
Note that when binding C libraries, dune doesn't provide special support for
630635
tools such as ``pkg-config``, however it integrates easily with
631636
:ref:`configurator` by
@@ -825,6 +830,11 @@ files for executables. See `executables_implicit_empty_intf`_.
825830
flag if some of the libraries listed here are not referenced from any of the
826831
plugin modules.
827832

833+
- ``(empty_module_interface_if_absent)`` causes the generation of empty
834+
interfaces for every module that does not have an interface file already.
835+
Useful when modules are used solely for their side-effects. This field is
836+
available since the 3.0 version of the dune language.
837+
828838
Linking modes
829839
~~~~~~~~~~~~~
830840

src/dune_rules/dune_file.ml

+5
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ module Buildable = struct
152152
{ loc : Loc.t
153153
; modules : Ordered_set_lang.t
154154
; modules_without_implementation : Ordered_set_lang.t
155+
; empty_module_interface_if_absent : bool
155156
; libraries : Lib_dep.t list
156157
; foreign_archives : (Loc.t * Foreign.Archive.t) list
157158
; foreign_stubs : Foreign.Stubs.t list
@@ -263,6 +264,9 @@ module Buildable = struct
263264
and+ root_module =
264265
field_o "root_module"
265266
(Dune_lang.Syntax.since Stanza.syntax (2, 8) >>> Module_name.decode_loc)
267+
and+ empty_module_interface_if_absent =
268+
field_b "empty_module_interface_if_absent"
269+
~check:(Dune_lang.Syntax.since Stanza.syntax (3, 0))
266270
in
267271
let preprocess =
268272
let init =
@@ -312,6 +316,7 @@ module Buildable = struct
312316
; lint
313317
; modules
314318
; modules_without_implementation
319+
; empty_module_interface_if_absent
315320
; foreign_stubs
316321
; foreign_archives
317322
; libraries

src/dune_rules/dune_file.mli

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module Buildable : sig
4242
{ loc : Loc.t
4343
; modules : Ordered_set_lang.t
4444
; modules_without_implementation : Ordered_set_lang.t
45+
; empty_module_interface_if_absent : bool
4546
; libraries : Lib_dep.t list
4647
; foreign_archives : (Loc.t * Foreign.Archive.t) list
4748
; foreign_stubs : Foreign.Stubs.t list

src/dune_rules/exe_rules.ml

+5-16
Original file line numberDiff line numberDiff line change
@@ -91,20 +91,6 @@ let o_files sctx ~dir ~expander ~(exes : Executables.t) ~linkages ~dir_contents
9191
in
9292
List.map o_files ~f:Path.build
9393

94-
let with_empty_intf ~sctx ~dir module_ =
95-
let name =
96-
Module.file module_ ~ml_kind:Impl
97-
|> Option.value_exn
98-
|> Path.set_extension ~ext:".mli"
99-
in
100-
let rule =
101-
Action_builder.write_file
102-
(Path.as_in_build_dir_exn name)
103-
"(* Auto-generated by Dune *)"
104-
in
105-
let+ () = Super_context.add_rule sctx ~dir rule in
106-
Module.add_file module_ Ml_kind.Intf (Module.File.make Dialect.ocaml name)
107-
10894
let executables_rules ~sctx ~dir ~expander ~dir_contents ~scope ~compile_info
10995
~embed_in_plugin_libraries (exes : Dune_file.Executables.t) =
11096
(* Use "eobjs" rather than "objs" to avoid a potential conflict with a library
@@ -138,17 +124,20 @@ let executables_rules ~sctx ~dir ~expander ~dir_contents ~scope ~compile_info
138124
let executable_names =
139125
List.map exes.names ~f:Module_name.of_string_allow_invalid
140126
in
127+
let add_empty_intf = exes.buildable.empty_module_interface_if_absent in
141128
Modules.map_user_written modules ~f:(fun m ->
142129
let name = Module.name m in
143130
let* m = Pp_spec.pp_module_as pp name m in
144131
let add_empty_intf =
132+
(add_empty_intf
133+
||
145134
let project = Scope.project scope in
146135
Dune_project.executables_implicit_empty_intf project
147-
&& List.mem executable_names name ~equal:Module_name.equal
136+
&& List.mem executable_names name ~equal:Module_name.equal)
148137
&& not (Module.has m ~ml_kind:Intf)
149138
in
150139
if add_empty_intf then
151-
with_empty_intf ~sctx ~dir m
140+
Module_compilation.with_empty_intf ~sctx ~dir m
152141
else
153142
Memo.Build.return m)
154143
in

src/dune_rules/lib_rules.ml

+7-1
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,13 @@ let cctx (lib : Library.t) ~sctx ~source_modules ~dir ~expander ~scope
375375
~lib_name:(Some (snd lib.name))
376376
in
377377
let+ modules =
378-
Modules.map_user_written source_modules ~f:(Pp_spec.pp_module pp)
378+
let add_empty_intf = lib.buildable.empty_module_interface_if_absent in
379+
Modules.map_user_written source_modules ~f:(fun m ->
380+
let* m = Pp_spec.pp_module pp m in
381+
if add_empty_intf && not (Module.has m ~ml_kind:Intf) then
382+
Module_compilation.with_empty_intf ~sctx ~dir m
383+
else
384+
Memo.Build.return m)
379385
in
380386
let modules = Vimpl.impl_modules vimpl modules in
381387
let requires_compile = Lib.Compile.direct_requires compile_info in

src/dune_rules/module_compilation.ml

+15
Original file line numberDiff line numberDiff line change
@@ -387,3 +387,18 @@ let build_all cctx ~dep_graphs =
387387
cctx
388388
in
389389
build_module cctx ~dep_graphs m)
390+
391+
let with_empty_intf ~sctx ~dir module_ =
392+
let name =
393+
Module.file module_ ~ml_kind:Impl
394+
|> Option.value_exn
395+
|> Path.set_extension ~ext:".mli"
396+
in
397+
let rule =
398+
Action_builder.write_file
399+
(Path.as_in_build_dir_exn name)
400+
"(* Auto-generated by Dune *)"
401+
in
402+
let open Memo.Build.O in
403+
let+ () = Super_context.add_rule sctx ~dir rule in
404+
Module.add_file module_ Ml_kind.Intf (Module.File.make Dialect.ocaml name)

src/dune_rules/module_compilation.mli

+3
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ val ocamlc_i :
2121

2222
val build_all :
2323
Compilation_context.t -> dep_graphs:Dep_graph.Ml_kind.t -> unit Memo.Build.t
24+
25+
val with_empty_intf :
26+
sctx:Super_context.t -> dir:Path.Build.t -> Module.t -> Module.t Memo.Build.t
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
Generate empty interfaces when using (empty_module_interface_if_absent)
2+
3+
$ cat >dune-project <<EOF
4+
> (lang dune 3.0)
5+
> EOF
6+
7+
Executables
8+
9+
$ cat >main.ml <<EOF
10+
> module D = Dep
11+
> EOF
12+
13+
$ touch dep.ml
14+
15+
$ cat >dune <<EOF
16+
> (executable (name main) (empty_module_interface_if_absent))
17+
> EOF
18+
19+
$ dune build ./main.exe
20+
21+
$ cat _build/default/dep.mli
22+
(* Auto-generated by Dune *)
23+
24+
Libraries
25+
26+
$ cat >dune <<EOF
27+
> (library (name l) (modules a b) (empty_module_interface_if_absent))
28+
> EOF
29+
30+
$ touch a.ml b.ml
31+
32+
$ dune build
33+
34+
$ cat _build/default/a.mli
35+
(* Auto-generated by Dune *)
36+
$ cat _build/default/b.mli
37+
(* Auto-generated by Dune *)
38+
39+
Version check
40+
41+
$ cat >dune-project <<EOF
42+
> (lang dune 2.9)
43+
> EOF
44+
45+
$ dune build
46+
File "dune", line 1, characters 32-66:
47+
1 | (library (name l) (modules a b) (empty_module_interface_if_absent))
48+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
49+
Error: 'empty_module_interface_if_absent' is only available since version 3.0
50+
of the dune language. Please update your dune-project file to have (lang dune
51+
3.0).
52+
[1]
53+
54+
Check that interfaces are *not* generated if field is not used
55+
56+
$ cat >dune <<EOF
57+
> (lang dune 3.0)
58+
> EOF
59+
60+
$ cat >dune <<EOF
61+
> (library (name l) (modules a b))
62+
> EOF
63+
64+
$ touch a.mli
65+
66+
$ dune build
67+
68+
$ test -f _build/default/a.mli
69+
70+
$ test ! -f _build/default/b.mli

0 commit comments

Comments
 (0)