-
Notifications
You must be signed in to change notification settings - Fork 413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow defining libs with same name in multiple contexts #10179
Changes from 16 commits
86011bc
5704dbc
9e0fa23
55378f3
845ac40
d0a26c2
15dd99c
a17622a
803a6fe
b970f3c
91da01d
c97b26f
ec4dd8f
583255c
f922540
f01cee4
81381b7
44eb127
d333475
531e4d3
1a3558a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -132,6 +132,18 @@ module Error = struct | |
] | ||
;; | ||
|
||
let duplicated ~loc ~name ~dir_a ~dir_b = | ||
User_error.make | ||
~loc | ||
[ Pp.textf | ||
"A library with name %S is defined in two folders: %s and %s. Either change \ | ||
one of the names, or enable them conditionally using the 'enabled_if' field." | ||
(Lib_name.to_string name) | ||
(Path.to_string_maybe_quoted dir_a) | ||
(Path.to_string_maybe_quoted dir_b) | ||
] | ||
;; | ||
|
||
(* diml: it is not very clear what a "default implementation cycle" is *) | ||
let default_implementation_cycle cycle = | ||
make | ||
|
@@ -411,7 +423,9 @@ and resolve_result = | |
| Invalid of User_message.t | ||
| Ignore | ||
| Redirect_in_the_same_db of (Loc.t * Lib_name.t) | ||
| Multiple_results of resolve_result list | ||
| Redirect of db * (Loc.t * Lib_name.t) | ||
| Deprecated_library_name of (Loc.t * Lib_name.t) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding a new variant to distinguish between a regular redirect (public libs) and deprecated libs. Treating them both the same way leads to all the tests in |
||
|
||
let lib_config (t : lib) = t.lib_config | ||
let name t = t.name | ||
|
@@ -1084,7 +1098,10 @@ end = struct | |
module Input = struct | ||
type t = Lib_name.t * Path.t Lib_info.t * string option | ||
|
||
let equal (x, _, _) (y, _, _) = Lib_name.equal x y | ||
let equal (lib_name, info, _) (lib_name', info', _) = | ||
Lib_name.equal lib_name lib_name' && Lib_info.equal info info' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we don't properly distinguish between libraries with different info, the paths will be broken (when getting the info for library |
||
;; | ||
|
||
let hash (x, _, _) = Lib_name.hash x | ||
let to_dyn = Dyn.opaque | ||
end | ||
|
@@ -1135,9 +1152,65 @@ end = struct | |
db.resolve name | ||
>>= function | ||
| Ignore -> Memo.return Status.Ignore | ||
| Deprecated_library_name (_, name') -> find_internal db name' | ||
| Redirect_in_the_same_db (_, name') -> find_internal db name' | ||
| Redirect (db', (_, name')) -> find_internal db' name' | ||
| Found info -> instantiate db name info ~hidden:None | ||
| Multiple_results libs -> | ||
let* libs = | ||
Memo.List.filter_map | ||
~f:(function | ||
Comment on lines
+1161
to
+1162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is roughly the same treatment for each variant as the one in the "non-multiple" case, but we filter out disabled libs in the |
||
| Ignore -> Memo.return (Some Status.Ignore) | ||
| Deprecated_library_name (_, name') -> | ||
find_internal db name' >>| fun f -> Some f | ||
| Redirect_in_the_same_db (_, name') -> | ||
find_internal db name' >>| fun f -> Some f | ||
| Redirect (db', (_, name')) -> find_internal db' name' >>| fun f -> Some f | ||
| Found info -> | ||
let* enabled = Lib_info.enabled info in | ||
(match enabled with | ||
| Disabled_because_of_enabled_if -> Memo.return None | ||
| Normal | Optional -> | ||
instantiate db name info ~hidden:None >>| fun f -> Some f) | ||
| Multiple_results _libs -> | ||
(* There can't be nested Multiple_results *) assert false | ||
| Invalid e -> Memo.return (Some (Status.Invalid e)) | ||
| Not_found -> | ||
(match db.parent with | ||
| None -> Memo.return (Some Status.Not_found) | ||
| Some db -> find_internal db name >>| fun f -> Some f) | ||
| Hidden { lib = info; reason = hidden; path = _ } -> | ||
(match db.parent with | ||
| None -> Memo.return Status.Not_found | ||
| Some db -> find_internal db name) | ||
>>= (function | ||
| Status.Found _ as x -> Memo.return (Some x) | ||
| _ -> instantiate db name info ~hidden:(Some hidden) >>| fun f -> Some f)) | ||
libs | ||
in | ||
(match libs with | ||
| [] -> assert false | ||
| [ status ] -> | ||
(* In case we have 0 or 1 results found, convert to [Status.t] directly. | ||
This allows to provide better errors later on, | ||
e.g. `Library "foo" in _build/default is hidden (unsatisfied 'enabled_if') *) | ||
Memo.return status | ||
| _ :: _ :: _ -> | ||
Memo.return | ||
(List.fold_left libs ~init:Status.Not_found ~f:(fun acc status -> | ||
match acc, status with | ||
| Status.Found a, Status.Found b -> | ||
let a = info a | ||
and b = info b in | ||
let loc = Lib_info.loc b in | ||
let dir_a = Lib_info.best_src_dir a in | ||
let dir_b = Lib_info.best_src_dir b in | ||
Status.Invalid (Error.duplicated ~loc ~name ~dir_a ~dir_b) | ||
| Invalid _, _ -> acc | ||
| (Found _ as lib), (Hidden _ | Ignore | Not_found | Invalid _) | ||
| (Hidden _ | Ignore | Not_found), (Found _ as lib) -> lib | ||
| ( (Hidden _ | Ignore | Not_found) | ||
, (Hidden _ | Ignore | Not_found | Invalid _) ) -> acc))) | ||
| Invalid e -> Memo.return (Status.Invalid e) | ||
| Not_found -> | ||
(match db.parent with | ||
|
@@ -1772,21 +1845,16 @@ end | |
|
||
module DB = struct | ||
module Resolve_result = struct | ||
type t = resolve_result = | ||
| Not_found | ||
| Found of Lib_info.external_ | ||
| Hidden of Lib_info.external_ Hidden.t | ||
| Invalid of User_message.t | ||
| Ignore | ||
| Redirect_in_the_same_db of (Loc.t * Lib_name.t) | ||
| Redirect of db * (Loc.t * Lib_name.t) | ||
type t = resolve_result | ||
|
||
let found f = Found f | ||
let not_found = Not_found | ||
let redirect db lib = Redirect (db, lib) | ||
let redirect_in_the_same_db lib = Redirect_in_the_same_db lib | ||
let multiple_results libs = Multiple_results libs | ||
let deprecated_library_name lib = Deprecated_library_name lib | ||
|
||
let to_dyn x = | ||
let rec to_dyn x = | ||
let open Dyn in | ||
match x with | ||
| Not_found -> variant "Not_found" [] | ||
|
@@ -1797,6 +1865,10 @@ module DB = struct | |
| Redirect (_, (_, name)) -> variant "Redirect" [ Lib_name.to_dyn name ] | ||
| Redirect_in_the_same_db (_, name) -> | ||
variant "Redirect_in_the_same_db" [ Lib_name.to_dyn name ] | ||
| Multiple_results redirects -> | ||
variant "Multiple_results" [ (Dyn.list to_dyn) redirects ] | ||
| Deprecated_library_name (_, name) -> | ||
variant "Deprecated_library_name" [ Lib_name.to_dyn name ] | ||
;; | ||
end | ||
|
||
|
@@ -1829,7 +1901,7 @@ module DB = struct | |
>>| function | ||
| Ok (Library pkg) -> Found (Dune_package.Lib.info pkg) | ||
| Ok (Deprecated_library_name d) -> | ||
Redirect_in_the_same_db (d.loc, d.new_public_name) | ||
Deprecated_library_name (d.loc, d.new_public_name) | ||
| Ok (Hidden_library pkg) -> Hidden (Hidden.unsatisfied_exist_if pkg) | ||
| Error e -> | ||
(match e with | ||
|
@@ -1866,6 +1938,14 @@ module DB = struct | |
| Ignore | Not_found | Invalid _ | Hidden _ -> None | ||
;; | ||
|
||
let find_invalid t name = | ||
let open Memo.O in | ||
Resolve_names.find_internal t name | ||
>>| function | ||
| Invalid err -> Some err | ||
| Found _ | Ignore | Not_found | Hidden _ -> None | ||
;; | ||
|
||
let find_even_when_hidden t name = | ||
let open Memo.O in | ||
Resolve_names.find_internal t name | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -659,3 +659,21 @@ let for_dune_package | |
(let dir = Obj_dir.dir obj_dir in | ||
fun p -> if Path.is_managed p then Path.relative dir (Path.basename p) else p) | ||
;; | ||
|
||
let equal | ||
(type a) | ||
(t : a t) | ||
{ loc; name; kind; src_dir; orig_src_dir; obj_dir; path_kind; _ } | ||
= | ||
let path_equal : a -> a -> bool = | ||
match (path_kind : a path) with | ||
| Local -> Path.Build.equal | ||
| External -> Path.equal | ||
in | ||
Loc.equal t.loc loc | ||
&& Lib_name.equal t.name name | ||
&& Lib_kind.equal t.kind kind | ||
&& path_equal src_dir t.src_dir | ||
&& Option.equal path_equal orig_src_dir t.orig_src_dir | ||
&& Obj_dir.equal obj_dir t.obj_dir | ||
Comment on lines
+673
to
+678
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not as exhaustive as the check it had originally (see #9964), but it seems to get the job done, for the purposes of memoization. |
||
;; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understood that "available" means rather "exists". For inexistent libraries,
enabled_in_context
might returntrue
surprisingly, so we have to keep both conditions in the check.