Adapt the design of multi directory libraries
Signed-off-by: Jeremie Dimino <jeremie@dimino.org>
This commit is contained in:
parent
d484f7b0fa
commit
0e6dda2032
|
@ -822,12 +822,14 @@ different directories. It is planned to add a ``qualified`` mode in
|
||||||
the future.
|
the future.
|
||||||
|
|
||||||
Note that sub-directories are included recursively, however the
|
Note that sub-directories are included recursively, however the
|
||||||
recursion will stop when encountering a sub-directory that:
|
recursion will stop when encountering a sub-directory that contains
|
||||||
|
another ``include_subdirs`` stanza. Additionally, it is not allowed
|
||||||
|
for a sub-directory of a directory with ``(include_subdirs <x>)``
|
||||||
|
where ``<x>`` is not ``no`` to contain one of the following stanzas:
|
||||||
|
|
||||||
- is part of a different project (for instance when vendoring projects)
|
- ``library``
|
||||||
- contains ``(include_subdirs unqualified)``
|
- ``executable(s)``
|
||||||
- contains one of the following stanza that consume modules:
|
- ``test(s)``
|
||||||
``library``, ``executable(s)`` or ``test(s)``.
|
|
||||||
|
|
||||||
Common items
|
Common items
|
||||||
============
|
============
|
||||||
|
|
|
@ -486,44 +486,46 @@ let build_mlds_map (d : Super_context.Dir_with_jbuild.t) ~files =
|
||||||
|
|
||||||
module Dir_status = struct
|
module Dir_status = struct
|
||||||
type t =
|
type t =
|
||||||
| Empty_standalone of File_tree.Dir.t option
|
| Standalone of
|
||||||
(* Directory with no libraries or executables that is not part of
|
(File_tree.Dir.t * Super_context.Dir_with_jbuild.t option) option
|
||||||
a multi-directory group *)
|
(* Directory not part of a multi-directory group. The argument is
|
||||||
|
[None] for directory that are not from the source tree, such as
|
||||||
| Is_component_of_a_group_but_not_the_root of
|
generated ones. *)
|
||||||
Super_context.Dir_with_jbuild.t option
|
|
||||||
(* Sub-directory of a directory with [(include_subdirs x)] where
|
|
||||||
[x] is not [no] *)
|
|
||||||
|
|
||||||
| Standalone of File_tree.Dir.t
|
|
||||||
* Super_context.Dir_with_jbuild.t
|
|
||||||
(* Directory with at least one library or executable *)
|
|
||||||
|
|
||||||
| Group_root of File_tree.Dir.t
|
| Group_root of File_tree.Dir.t
|
||||||
* Super_context.Dir_with_jbuild.t
|
* Super_context.Dir_with_jbuild.t
|
||||||
(* Directory with [(include_subdirs x)] where [x] is not [no] *)
|
(* Directory with [(include_subdirs x)] where [x] is not [no] *)
|
||||||
|
|
||||||
|
| Is_component_of_a_group_but_not_the_root of
|
||||||
|
Super_context.Dir_with_jbuild.t option
|
||||||
|
(* Sub-directory of a [Group_root _] *)
|
||||||
|
|
||||||
let is_standalone = function
|
let is_standalone = function
|
||||||
| Standalone _ | Empty_standalone _ -> true
|
| Standalone _ -> true
|
||||||
| _ -> false
|
| _ -> false
|
||||||
|
|
||||||
let cache = Hashtbl.create 32
|
let cache = Hashtbl.create 32
|
||||||
|
|
||||||
let analyze_stanzas stanzas =
|
let get_include_subdirs stanzas =
|
||||||
let is_group_root, has_modules_consumers =
|
List.fold_left stanzas ~init:None ~f:(fun acc stanza ->
|
||||||
List.fold_left stanzas ~init:(None, false) ~f:(fun acc stanza ->
|
match stanza with
|
||||||
let is_group_root, has_modules_consumers = acc in
|
| Include_subdirs (loc, x) ->
|
||||||
match stanza with
|
if Option.is_some acc then
|
||||||
| Include_subdirs (loc, x) ->
|
Loc.fail loc "The 'include_subdirs' stanza cannot appear \
|
||||||
if Option.is_some is_group_root then
|
more than once";
|
||||||
Loc.fail loc "The 'include_subdirs' stanza cannot appear \
|
Some x
|
||||||
more than once";
|
| _ -> acc)
|
||||||
(Some x, has_modules_consumers)
|
|
||||||
| Library _ | Executables _ | Tests _ ->
|
let check_no_module_consumer stanzas =
|
||||||
(is_group_root, true)
|
List.iter stanzas ~f:(fun stanza ->
|
||||||
| _ -> acc)
|
match stanza with
|
||||||
in
|
| Library { buildable; _} | Executables { buildable; _ }
|
||||||
(Option.value is_group_root ~default:No, has_modules_consumers)
|
| Tests { exes = { buildable; _ }; _ } ->
|
||||||
|
Loc.fail buildable.loc
|
||||||
|
"This stanza is not allowed in a sub-directory of directory with \
|
||||||
|
(include_subdirs unqualified).\n\
|
||||||
|
Hint: add (include_subdirs no) to this file."
|
||||||
|
| _ -> ())
|
||||||
|
|
||||||
let rec get sctx ~dir =
|
let rec get sctx ~dir =
|
||||||
match Hashtbl.find cache dir with
|
match Hashtbl.find cache dir with
|
||||||
|
@ -534,29 +536,38 @@ module Dir_status = struct
|
||||||
Option.bind (Path.drop_build_context dir)
|
Option.bind (Path.drop_build_context dir)
|
||||||
~f:(File_tree.find_dir (Super_context.file_tree sctx))
|
~f:(File_tree.find_dir (Super_context.file_tree sctx))
|
||||||
with
|
with
|
||||||
| None -> Empty_standalone None
|
| None -> begin
|
||||||
|
match Path.parent dir with
|
||||||
|
| None -> Standalone None
|
||||||
|
| Some dir ->
|
||||||
|
if is_standalone (get sctx ~dir) then
|
||||||
|
Standalone None
|
||||||
|
else
|
||||||
|
Is_component_of_a_group_but_not_the_root None
|
||||||
|
end
|
||||||
| Some ft_dir ->
|
| Some ft_dir ->
|
||||||
let project_root = Path.of_local (File_tree.Dir.project ft_dir).root in
|
let project_root = Path.of_local (File_tree.Dir.project ft_dir).root in
|
||||||
match Super_context.stanzas_in sctx ~dir with
|
match Super_context.stanzas_in sctx ~dir with
|
||||||
| None ->
|
| None ->
|
||||||
if dir = project_root ||
|
if dir = project_root ||
|
||||||
is_standalone (get sctx ~dir:(Path.parent_exn dir)) then
|
is_standalone (get sctx ~dir:(Path.parent_exn dir)) then
|
||||||
Empty_standalone (Some ft_dir)
|
Standalone (Some (ft_dir, None))
|
||||||
else
|
else
|
||||||
Is_component_of_a_group_but_not_the_root None
|
Is_component_of_a_group_but_not_the_root None
|
||||||
| Some d ->
|
| Some d ->
|
||||||
let is_group_root, has_modules_consumers =
|
match get_include_subdirs d.stanzas with
|
||||||
analyze_stanzas d.stanzas
|
| Some Unqualified ->
|
||||||
in
|
|
||||||
if is_group_root <> No then
|
|
||||||
Group_root (ft_dir, d)
|
Group_root (ft_dir, d)
|
||||||
else if not has_modules_consumers &&
|
| Some No ->
|
||||||
dir <> project_root &&
|
Standalone (Some (ft_dir, Some d))
|
||||||
not (is_standalone (get sctx ~dir:(Path.parent_exn dir)))
|
| None ->
|
||||||
then
|
if dir <> project_root &&
|
||||||
Is_component_of_a_group_but_not_the_root (Some d)
|
not (is_standalone (get sctx ~dir:(Path.parent_exn dir)))
|
||||||
else
|
then begin
|
||||||
Standalone (ft_dir, d)
|
check_no_module_consumer d.stanzas;
|
||||||
|
Is_component_of_a_group_but_not_the_root (Some d)
|
||||||
|
end else
|
||||||
|
Standalone (Some (ft_dir, Some d))
|
||||||
in
|
in
|
||||||
Hashtbl.add cache dir t;
|
Hashtbl.add cache dir t;
|
||||||
t
|
t
|
||||||
|
@ -569,14 +580,13 @@ module Dir_status = struct
|
||||||
match Super_context.stanzas_in sctx ~dir with
|
match Super_context.stanzas_in sctx ~dir with
|
||||||
| None -> Is_component_of_a_group_but_not_the_root None
|
| None -> Is_component_of_a_group_but_not_the_root None
|
||||||
| Some d ->
|
| Some d ->
|
||||||
let is_group_root, has_modules_consumers =
|
match get_include_subdirs d.stanzas with
|
||||||
analyze_stanzas d.stanzas
|
| Some Unqualified ->
|
||||||
in
|
|
||||||
if is_group_root <> No then
|
|
||||||
Group_root (ft_dir, d)
|
Group_root (ft_dir, d)
|
||||||
else if has_modules_consumers then
|
| Some No ->
|
||||||
Standalone (ft_dir, d)
|
Standalone (Some (ft_dir, Some d))
|
||||||
else
|
| None ->
|
||||||
|
check_no_module_consumer d.stanzas;
|
||||||
Is_component_of_a_group_but_not_the_root (Some d)
|
Is_component_of_a_group_but_not_the_root (Some d)
|
||||||
in
|
in
|
||||||
Hashtbl.add cache dir t;
|
Hashtbl.add cache dir t;
|
||||||
|
@ -590,17 +600,25 @@ let rec get sctx ~dir =
|
||||||
| Some t -> t
|
| Some t -> t
|
||||||
| None ->
|
| None ->
|
||||||
match Dir_status.get sctx ~dir with
|
match Dir_status.get sctx ~dir with
|
||||||
| Empty_standalone ft_dir ->
|
| Standalone x ->
|
||||||
let t =
|
let t =
|
||||||
{ kind = Standalone
|
match x with
|
||||||
; dir
|
| Some (ft_dir, Some d) ->
|
||||||
; text_files =
|
let files = load_text_files sctx ft_dir d in
|
||||||
(match ft_dir with
|
{ kind = Standalone
|
||||||
| None -> String.Set.empty
|
; dir
|
||||||
| Some x -> File_tree.Dir.files x)
|
; text_files = files
|
||||||
; modules = lazy empty_modules
|
; modules = lazy (build_modules_map d
|
||||||
; mlds = lazy []
|
~modules:(modules_of_files ~dir:d.ctx_dir ~files))
|
||||||
}
|
; mlds = lazy (build_mlds_map d ~files)
|
||||||
|
}
|
||||||
|
| _ ->
|
||||||
|
{ kind = Standalone
|
||||||
|
; dir
|
||||||
|
; text_files = String.Set.empty
|
||||||
|
; modules = lazy empty_modules
|
||||||
|
; mlds = lazy []
|
||||||
|
}
|
||||||
in
|
in
|
||||||
Hashtbl.add cache dir t;
|
Hashtbl.add cache dir t;
|
||||||
t
|
t
|
||||||
|
@ -612,19 +630,6 @@ let rec get sctx ~dir =
|
||||||
(* Filled while scanning the group root *)
|
(* Filled while scanning the group root *)
|
||||||
Option.value_exn (Hashtbl.find cache dir)
|
Option.value_exn (Hashtbl.find cache dir)
|
||||||
end
|
end
|
||||||
| Standalone (ft_dir, d) ->
|
|
||||||
let files = load_text_files sctx ft_dir d in
|
|
||||||
let t =
|
|
||||||
{ kind = Standalone
|
|
||||||
; dir
|
|
||||||
; text_files = files
|
|
||||||
; modules = lazy (build_modules_map d
|
|
||||||
~modules:(modules_of_files ~dir:d.ctx_dir ~files))
|
|
||||||
; mlds = lazy (build_mlds_map d ~files)
|
|
||||||
}
|
|
||||||
in
|
|
||||||
Hashtbl.add cache dir t;
|
|
||||||
t
|
|
||||||
| Group_root (ft_dir, d) ->
|
| Group_root (ft_dir, d) ->
|
||||||
let rec walk ft_dir ~dir acc =
|
let rec walk ft_dir ~dir acc =
|
||||||
match
|
match
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
(executable
|
||||||
|
(name main)
|
||||||
|
(libraries foo))
|
||||||
|
|
||||||
|
(alias
|
||||||
|
(name default)
|
||||||
|
(action (run ./main.exe)))
|
|
@ -0,0 +1 @@
|
||||||
|
(lang dune 1.1)
|
|
@ -0,0 +1 @@
|
||||||
|
let () = print_endline Foo.x
|
|
@ -0,0 +1 @@
|
||||||
|
let x = "world!"
|
|
@ -0,0 +1,5 @@
|
||||||
|
(library (name foo))
|
||||||
|
|
||||||
|
(rule (with-stdout-to generated.ml (run gen/gen.exe)))
|
||||||
|
|
||||||
|
(include_subdirs unqualified)
|
|
@ -0,0 +1 @@
|
||||||
|
let x = Generated.x ^ Blah.x
|
|
@ -0,0 +1 @@
|
||||||
|
(executable (name gen))
|
|
@ -0,0 +1 @@
|
||||||
|
let x = print_endline {|let x = "Hello, "|}
|
|
@ -6,8 +6,8 @@ Simple test with a multi dir exe
|
||||||
foo alias default
|
foo alias default
|
||||||
Hello, world!
|
Hello, world!
|
||||||
|
|
||||||
Test that executables stop the recursion
|
Test that include_subdirs stop the recursion
|
||||||
----------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
$ dune build --root test2
|
$ dune build --root test2
|
||||||
Entering directory 'test2'
|
Entering directory 'test2'
|
||||||
|
@ -38,3 +38,10 @@ Test some error cases
|
||||||
File "dune", line 2, characters 0-29:
|
File "dune", line 2, characters 0-29:
|
||||||
Error: The 'include_subdirs' stanza cannot appear more than once
|
Error: The 'include_subdirs' stanza cannot appear more than once
|
||||||
[1]
|
[1]
|
||||||
|
|
||||||
|
$ dune build --root error3
|
||||||
|
Entering directory 'error3'
|
||||||
|
File "src/gen/dune", line 1, characters 0-23:
|
||||||
|
Error: This stanza is not allowed in a sub-directory of directory with (include_subdirs unqualified).
|
||||||
|
Hint: add (include_subdirs no) to this file.
|
||||||
|
[1]
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
(executable (name gen))
|
(executable (name gen))
|
||||||
|
(include_subdirs no)
|
Loading…
Reference in New Issue