-
Notifications
You must be signed in to change notification settings - Fork 403
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
Add support for -H compiler flag #10644
Conversation
Hi, thanks for this PR and the detailed description. There are a couple things we can land first:
Regarding your question:
Yes. the reason it's painful is that we're only testing a single version of the compiler in CI, but you can add tests that require 5.x and mark them with |
Hello, thanks for the comment.
I have opened a separate PR to land the changes you proposed (see #10645). As you mentioned, those can be merged independently of the feature to be added through this PR. I will remove them from this PR as soon as they get merged successfully. |
src/dune_rules/lib.mli
Outdated
@@ -235,3 +235,5 @@ module Local : sig | |||
|
|||
include Comparable_intf.S with type key := t | |||
end | |||
|
|||
module Tbl : Hashtbl.S with type key := t |
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 don't think you need this since our Table
module is polymorphic. let table = Table.create (module Lib)
should work
src/dune_rules/lib_flags.mli
Outdated
|
||
val include_flags | ||
: ?project:Dune_project.t | ||
-> ?direct:(Lib.t -> bool) |
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.
Why can't we just pass a list: ([ Direct | Indirect] * Lib.t) list
as an argument? I'm not a fan of passing around callbacks.
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.
Hello,
I have removed the passed callback. Instead, now two lists are passed, one containing the direct dependencies (requires_direct
) and the other contains the hidden dependencies (requires_hidden
). The requires_hidden
is also exposed in Compilation_context.t
, and so can be used in other places when needed.
The code have changed a little bit, and the diff in lib_flags.ml
is now smaller.
Review appreciated!
else requires_compile | ||
then Memo.Lazy.force requires_link, Resolve.Memo.return (fun _ -> true) | ||
else if Version.supports_hidden_includes ocaml.version | ||
&& Dune_project.dune_version project >= (3, 17) |
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 think this mode still needs to be guarded behind implicit_transitive_deps
. Once we experiment with it more, we can enable it by default for newer compilers perhaps.
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.
Sorry, but I do not seem to have understood well your point. The new feature is invoked only when implicit_transitive_deps
is false, according to the written logic, or am I missing something?
Thanks for the review.
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.
Yeah, that wasn't very clear at all. Let me explain the behavior I would like to achieve:
When we're using (implicit_transitive_deps false)
and OCaml >= 5.2.0, we should use use your new implementation with the -H
flag.
When we're using (implicit_transitive_deps false)
and OCaml < 5.2.0, we should go back to using the old implementation of hiding -I
flags.
When we're using (implicit_transitive_deps true)
, it should disable using -H
flags completely.
The issue with the current behavior is that users do expect upgrading to 3.17 to be relatively easy. With this change, they will now need to look at all their transitive deps. While I agree that it's a good thing, it's too much of a breakage to introduce in a minor version bump.
Therefore, I suggest that we guard this behind a feature flag. We already have a feature flag though implicit_transitive_deps
, so we can just reuse it.
@voodoos could you review the implementation as well? I think you needed something similar for merlin. It would be good to see what can be shared. |
514d4f3
to
af51c9a
Compare
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 think the way dependencies are exposed right now is too restricting. The Lib.Compile.t
API only provide us with either require_compile
, the list of direct dependencies, or require_link
, the list of all transitive dependencies including the direct ones.
This means that we have to compute the disjunction require_link \ require_compile
to get the actual "hidden" or "extra" dependencies. Right now this is needed in 3 places: rules for indexation (#10422), rules for merlin config (#10535) and this PR.
One issue is that require_compile
and require_link
are lists which prevent an efficient computation of the disjunction. In #10422 and #10535 this is done in a somewhat inefficient way using multiple calls to List.mem
. This PR introduces a more efficient approach by using a set (the (lib, unit) Hashtbl.t) to perform more efficient membership tests.
I think we should update Lib.Compile.t
to either provide an additional requires_extra
list of libs that is computed in an efficient way and share the result, or have requires_compile
and requires_link
be Sets so that consumers can efficiently compute the disjunction without too much code duplication
This would allow the different places using the "extra deps" to do it in a uniform way.
Signed-off-by: HasanA <[email protected]>
Signed-off-by: HasanA <[email protected]>
Signed-off-by: HasanA <[email protected]>
Signed-off-by: HasanA <[email protected]>
…en (to be re-checked) Signed-off-by: HasanA <[email protected]>
Concerning the last two commits, they contain edits based on the reviews and comments (cc @voodoos @rgrinberg), resting the main goal of keeping track of hidden deps unchanged. Those dependencies are now tracked by a new list
|
to_iflags | ||
(include_paths ?project ts_direct { lib_mode = mode; melange_emit = false }) | ||
in | ||
Command.Args.S [ direct_includes; hidden_includes ] |
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.
Isn't this re-ordering the include flags? e.g. if we have (libraries x y)
, we'd have closure(x) closure(y)
, and now we have x y closure(x) closure(y)
?
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.
If I understand things correctly, I don't think anything is being re-ordered; instead some extra -H
flags are being added at the "end" of the command-line when implicit_transitive_deps
is false
:
- If
implicit_transitive_deps
istrue
, thendirect_includes
isrequires_link
(fromCompilation_context
), andhidden_includes
is empty; this is the same as today, and nothing changes. - If
implicit_transitive_deps
isfalse
(and OCaml is recent enough, etc)direct_includes
isrequires_compile
(fromCompilation_context
), andhidden_includes
contains some extra flags relative to today, but the order ofdirect_includes
does not change.
Do you agree with this understanding @MA0010?
src/dune_rules/lib_flags.ml
Outdated
let hidden_includes = | ||
to_hflags | ||
(include_paths ?project ts_hidden { lib_mode = mode; melange_emit = false }) | ||
in | ||
let direct_includes = | ||
to_iflags | ||
(include_paths ?project ts_direct { lib_mode = mode; melange_emit = false }) | ||
in |
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.
Let's share the code for the two definitions.
src/dune_rules/lib_flags.ml
Outdated
let to_hflags dirs = | ||
Command.Args.S | ||
(Path.Set.fold dirs ~init:[] ~f:(fun dir acc -> | ||
Command.Args.Path dir :: A "-H" :: acc) | ||
|> List.rev) | ||
;; | ||
|
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.
Let's share the code with to_iflags
above.
~project | ||
~opaque | ||
~direct_requires | ||
~(hidden_requires : Lib_flags.L.t Resolve.Memo.t) |
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.
~(hidden_requires : Lib_flags.L.t Resolve.Memo.t) | |
~hidden_requires |
Signed-off-by: HasanA <[email protected]>
src/dune_rules/lib_flags.ml
Outdated
@@ -85,10 +85,10 @@ let link_deps sctx t mode = | |||
module L = struct | |||
type nonrec t = Lib.t list | |||
|
|||
let to_iflags dirs = | |||
let to_iflags ?(flag = "-I") dirs = |
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.
In our codebase we often try to avoid default flags, as the defaults are often easy to miss and in this case "to_iflags" which generates -H
flags is somewhat strange.
As such I suggest making this a labelled to_flag
function and
let to_ifags = to_flags "-I"
let to_hflags = to_flags "-H"
This prevents having to change all the code while allowing an easy way to get an -H
flag.
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.
Hello, thanks for the remark. You are right, it is even safer to do as you suggested. I have pushed the edits.
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.
You might not even need to expose to_hflags
in the mli
if nobody outside the module is going to use it. That way the compiler can warn about unused functions in case the code stops using to_hflags
.
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.
You are right for now to_hflags
is not used anywhere outside lib_flags.mli
, but I exposed it for the sake of symmetry (since if to_iflags
is to be used somewhere in some later work, to_hflags
would be also needed).
But maybe it is better for now to remove it from the interface.
Signed-off-by: HasanA <[email protected]>
Signed-off-by: HasanA <[email protected]>
in | ||
let sandbox = Sandbox_config.no_special_requirements in |
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.
Is this change needed?
let open Resolve.Memo.O in | ||
let+ requires = requires_compile | ||
and+ requires_link = Memo.Lazy.force requires_link in | ||
let requires_table = Table.create (module Lib) 500 in |
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.
500
seems too large; I suggest 5
.
doc/changes/10644.md
Outdated
- Add support for the -H flag (introduced in Ocaml compiler 5.2) in dune | ||
(only supported for lang versions 3.17 and higher). This adaptation gives | ||
the correct semantics for `implicit_transitive_deps = false` mode, since then | ||
transitive dependencies are included with -H flag and so are not directly accessible | ||
but visible for the compiler. (#10644, fixes (#9333, #ocsigen/tyxml#274, #2733, #4963), | ||
@MA0100) |
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.
- Add support for the -H flag (introduced in Ocaml compiler 5.2) in dune | |
(only supported for lang versions 3.17 and higher). This adaptation gives | |
the correct semantics for `implicit_transitive_deps = false` mode, since then | |
transitive dependencies are included with -H flag and so are not directly accessible | |
but visible for the compiler. (#10644, fixes (#9333, #ocsigen/tyxml#274, #2733, #4963), | |
@MA0100) | |
- Add support for the -H flag (introduced in OCaml compiler 5.2) in dune | |
(only supported for lang versions 3.17 and higher). This adaptation gives | |
the correct semantics for `(implicit_transitive_deps false)`. | |
(#10644, fixes #9333, ocsigen/tyxml#274, #2733, #4963, @MA0100) |
src/dune_rules/merlin/ocaml_index.ml
Outdated
let* req_link = Compilation_context.requires_link cctx in | ||
let+ req_compile = Compilation_context.requires_compile cctx in | ||
List.filter req_link ~f:(fun l -> not (List.exists req_compile ~f:(Lib.equal l))) | ||
Compilation_context.requires_hidden cctx |
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.
This doesn't seem quite right. For new versions of OCaml (>= 5.2) and version of the Dune language >= 3.17, Dune will pass -H
to the compiler and these flags will be recorded in the .cmt
files and will, in turn, be read by ocaml-index
. Accordingly, in these cases we do not need to do anything special here. It is only for backwards compatibility that we can keep the above logic (ie if OCaml is < 5.2 or if the Dune language veresion if < 3.17). Also, the flags should all be passed with -I
, not -H
in this case.
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.
@voodoos sounds OK?
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.
Yes, that's completely right. If Dune >= 3.17 there is no more additional flags to pass to the indexer. (Note that indexing is not available prior to OCaml 5.2, so there is no need to check that here.)
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.
Thanks for the confirmation! So only the check for OCaml lang version < 3.17 is needed.
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.
Done!
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.
Thanks, the current version looks good to me
Friendly ping @anmonteiro: are you planning to review the PR? I feel this is getting ready to be merged, so do not hesitate to leave a note if you are planning to do so (which would be appreciated, of course!). |
Signed-off-by: HasanA <[email protected]>
let () = | ||
List.iter ~f:(fun lib -> Table.set requires_table lib ()) requires_compile | ||
in |
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.
let () = | |
List.iter ~f:(fun lib -> Table.set requires_table lib ()) requires_compile | |
in | |
List.iter ~f:(fun lib -> Table.set requires_table lib ()) requires_compile; |
(executable | ||
(name run) | ||
(modules run) | ||
(libraries bar)) |
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.
Missing newline at EOF.
@@ -0,0 +1,2 @@ | |||
open Tyxml.Html | |||
let _ = p [ a [ txt "a" ] ] |
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.
Missing newline at EOF.
@@ -0,0 +1,3 @@ | |||
(executable | |||
(name run) | |||
(libraries tyxml)) |
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.
Missing newline at EOF.
@@ -0,0 +1 @@ | |||
let _ = Bar.y |
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.
Missing newline at EOF.
|
||
|
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.
> EOF | ||
$ echo "$(getincludes I)" | ||
.foo.objs/byte.foo.objs/byte.foo.objs/native | ||
|
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.
$ dune build --root=./tyxml | ||
Entering directory 'tyxml' | ||
Leaving directory 'tyxml' | ||
|
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.
|
||
|
||
|
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.
> EOF | ||
$ echo "$(getincludes I)" | ||
.foo.objs/byte.foo.objs/byte.foo.objs/native | ||
|
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.
@MA0010 I left a few cosmetic tweaks to do before merging, otherwise this is almost ready. I would put both tests under a common directory |
Signed-off-by: HasanA <[email protected]>
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
Signed-off-by: Nicolás Ojeda Bär <[email protected]>
Small test cleanup
Signed-off-by: HasanA <[email protected]>
I think all issues have been addressed and the PR is ready to go. @emillon: would you like to do the honours? (as I have been supervising the PR, I prefer not to merge it myself). |
Tha PR should be squash-merged by the way. |
Signed-off-by: HasanA <[email protected]>
@nojb yes, I'll give it a last look, squash and merge. |
I can't push to the original branch but that should be fine. |
the last issues are just cosmetic - newlines etc and the changelog can be fixed at release time. |
Thank you so much for this work! |
This PR adds support to Dune to use the
-H
compiler flag introduced in ocaml/ocaml#12246, which provides good semantics to the(implicit_transitive_deps false)
.This work was carried out by @MA0010 as part of an internship at LexiFi. I am opening this PR but @MA0010 will take the lead during the review process.
The first commit contains the meat of the change: we keep track of which dependencies are "direct" (= mentioned in the Dune file) and which dependencies are purely transitive. For direct dependencies we use
-I
and for transitive dependencies we use-H
(of course, only for OCaml >= 5.2). The use of-H
is also guarded by Dune version 3.17 (this is to minimize the possibility of breakage, also there are some corner cases where the change is not completely backwards-compatible).The second commit adds an explicit
unix
dependency to some vendored libraries. This is needed becauseunix
is mentioned in the source of these libraries, but the library is not mentioned in the Dune stanzas. The libraries are able to build before this PR because OCaml adds-I +unix
implicitly (for recent enough versions of OCaml whereunix
is installed in the separate directory+unix
). However, after this PR, the compiler passes-H +unix
which disables the implicit addition of-I +unix
breaking compilation (again, becauseUnix
is a direct dependency of these libraries).The third commit contains some tests. Writing the tests was a bit involved due to the need to have them pass both with versions of OCaml supporting
-H
and those that do not. Suggestions for improvement welcome.The last commit bumps the Dune version to 3.17. We could consider not bumping the Dune language version since the new mode is almost always a strict improvement for users of
(implicit_transitive_deps false)
(modulo corner cases such as the one mentioned in the second bullet point). Opinions welcome.Future work:
-H
as needed (there is already an open PR for this: Use new hidden deps Merlin support for handling implicit-transitive-deps false #10535).ocaml-index
machinery accordingly (cc @voodoos)Fixes #9333
Fixes ocsigen/tyxml#274
Fixes #2733
Fixes #4963