Skip to content
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

Merged
merged 22 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 59 additions & 21 deletions src/dune_rules/compilation_context.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,51 @@ open Import
module Includes = struct
type t = Command.Args.without_targets Command.Args.t Lib_mode.Cm_kind.Map.t

let make ~project ~opaque ~requires : _ Lib_mode.Cm_kind.Map.t =
let make
~project
~opaque
~direct_requires
~(hidden_requires : Lib_flags.L.t Resolve.Memo.t)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
~(hidden_requires : Lib_flags.L.t Resolve.Memo.t)
~hidden_requires

: _ Lib_mode.Cm_kind.Map.t
=
(* TODO : some of the requires can filtered out using [ocamldep] info *)
let open Resolve.Memo.O in
let iflags libs mode = Lib_flags.L.include_flags ~project libs mode in
let iflags direct_libs hidden_libs mode =
Lib_flags.L.include_flags ~project direct_libs hidden_libs mode
in
let make_includes_args ~mode groups =
Command.Args.memo
(Resolve.Memo.args
(let+ libs = requires in
(let+ direct_libs = direct_requires
and+ hidden_libs = hidden_requires in
Command.Args.S
[ iflags libs mode; Hidden_deps (Lib_file_deps.deps libs ~groups) ]))
[ iflags direct_libs hidden_libs mode
; Hidden_deps
(Lib_file_deps.deps (List.concat [ direct_libs; hidden_libs ]) ~groups)
]))
in
let cmi_includes = make_includes_args ~mode:(Ocaml Byte) [ Ocaml Cmi ] in
let cmx_includes =
Command.Args.memo
(Resolve.Memo.args
(let+ libs = requires in
(let+ direct_libs = direct_requires
and+ hidden_libs = hidden_requires in
Command.Args.S
[ iflags libs (Ocaml Native)
[ iflags direct_libs hidden_libs (Ocaml Native)
; Hidden_deps
(if opaque
then
List.map libs ~f:(fun lib ->
( lib
, if Lib.is_local lib
then [ Lib_file_deps.Group.Ocaml Cmi ]
else [ Ocaml Cmi; Ocaml Cmx ] ))
List.map
(List.concat [ direct_libs; hidden_libs ])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(List.concat [ direct_libs; hidden_libs ])
(direct_libs @ hidden_libs)

~f:(fun lib ->
( lib
, if Lib.is_local lib
then [ Lib_file_deps.Group.Ocaml Cmi ]
else [ Ocaml Cmi; Ocaml Cmx ] ))
|> Lib_file_deps.deps_with_exts
else
Lib_file_deps.deps
libs
(List.concat [ direct_libs; hidden_libs ])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(List.concat [ direct_libs; hidden_libs ])
(direct_libs @ hidden_libs)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MA0010 this can be committed, right?

~groups:[ Lib_file_deps.Group.Ocaml Cmi; Ocaml Cmx ])
]))
in
Expand Down Expand Up @@ -74,6 +89,7 @@ type t =
; modules : modules
; flags : Ocaml_flags.t
; requires_compile : Lib.t list Resolve.Memo.t
; requires_hidden : Lib.t list Resolve.Memo.t
; requires_link : Lib.t list Resolve.t Memo.Lazy.t
; includes : Includes.t
; preprocessing : Pp_spec.t
Expand All @@ -99,6 +115,7 @@ let obj_dir t = t.obj_dir
let modules t = t.modules.modules
let flags t = t.flags
let requires_compile t = t.requires_compile
let requires_hidden t = t.requires_hidden
let requires_link t = Memo.Lazy.force t.requires_link
let includes t = t.includes
let preprocessing t = t.preprocessing
Expand Down Expand Up @@ -139,12 +156,26 @@ let create
=
let open Memo.O in
let project = Scope.project scope in
let requires_compile =
let context = Super_context.context super_context in
let sandbox = Sandbox_config.no_special_requirements in
let* ocaml = Context.ocaml context in
let direct_requires, hidden_requires =
if Dune_project.implicit_transitive_deps project
then Memo.Lazy.force requires_link
else requires_compile
then Memo.Lazy.force requires_link, Resolve.Memo.return []
else if Version.supports_hidden_includes ocaml.version
&& Dune_project.dune_version project >= (3, 17)
Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member

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.

then (
let requires_hidden =
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
Copy link
Collaborator Author

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.

let () = List.iter ~f:(fun lib -> Table.set requires_table lib ()) requires in
List.filter requires_link ~f:(fun l -> not (Table.mem requires_table l))
in
requires_compile, requires_hidden)
else requires_compile, Resolve.Memo.return []
in
let sandbox = Sandbox_config.no_special_requirements in
Copy link
Collaborator Author

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 modes =
let default =
{ Lib_mode.Map.ocaml = Mode.Dict.make_both (Some Mode_conf.Kind.Inherited)
Expand All @@ -153,8 +184,6 @@ let create
in
Option.value ~default modes |> Lib_mode.Map.map ~f:Option.is_some
in
let context = Super_context.context super_context in
let* ocaml = Context.ocaml context in
let opaque =
let profile = Context.profile context in
eval_opaque ocaml profile opaque
Expand All @@ -180,9 +209,10 @@ let create
; obj_dir
; modules = { modules; dep_graphs }
; flags
; requires_compile
; requires_compile = direct_requires
; requires_hidden = hidden_requires
; requires_link
; includes = Includes.make ~project ~opaque ~requires:requires_compile
; includes = Includes.make ~project ~opaque ~direct_requires ~hidden_requires
; preprocessing
; opaque
; stdlib
Expand Down Expand Up @@ -263,8 +293,16 @@ let for_module_generated_at_link_time cctx ~requires ~module_ =
their implementation must also be compiled with -opaque *)
Ocaml.Version.supports_opaque_for_mli cctx.ocaml.version
in
let direct_requires = requires in
let hidden_requires = Resolve.Memo.return [] in
let modules = singleton_modules module_ in
let includes = Includes.make ~project:(Scope.project cctx.scope) ~opaque ~requires in
let includes =
Includes.make
~project:(Scope.project cctx.scope)
~opaque
~direct_requires
~hidden_requires
in
{ cctx with
opaque
; flags = Ocaml_flags.empty
Expand Down
1 change: 1 addition & 0 deletions src/dune_rules/compilation_context.mli
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ val obj_dir : t -> Path.Build.t Obj_dir.t
val modules : t -> Modules.With_vlib.t
val flags : t -> Ocaml_flags.t
val requires_link : t -> Lib.t list Resolve.Memo.t
val requires_hidden : t -> Lib.t list Resolve.Memo.t
val requires_compile : t -> Lib.t list Resolve.Memo.t
val includes : t -> Command.Args.without_targets Command.Args.t Lib_mode.Cm_kind.Map.t
val preprocessing : t -> Pp_spec.t
Expand Down
19 changes: 17 additions & 2 deletions src/dune_rules/lib_flags.ml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ module L = struct
|> List.rev)
;;

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)
;;

Copy link
Collaborator Author

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.

let remove_stdlib dirs libs =
match libs with
| [] -> dirs
Expand Down Expand Up @@ -155,8 +162,16 @@ module L = struct
remove_stdlib dirs ts
;;

let include_flags ?project ts mode =
to_iflags (include_paths ?project ts { lib_mode = mode; melange_emit = false })
let include_flags ?project ts_direct ts_hidden mode =
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
Copy link
Collaborator Author

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.

Command.Args.S [ direct_includes; hidden_includes ]
Copy link
Member

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)?

Copy link
Collaborator Author

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 is true, then direct_includes is requires_link (from Compilation_context), and hidden_includes is empty; this is the same as today, and nothing changes.
  • If implicit_transitive_deps is false (and OCaml is recent enough, etc) direct_includes is requires_compile (from Compilation_context), and hidden_includes contains some extra flags relative to today, but the order of direct_includes does not change.

Do you agree with this understanding @MA0010?

;;

let melange_emission_include_flags ?project ts =
Expand Down
2 changes: 1 addition & 1 deletion src/dune_rules/lib_flags.mli
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module L : sig

val to_iflags : Path.Set.t -> _ Command.Args.t
val include_paths : ?project:Dune_project.t -> t -> Lib_mode.t -> Path.Set.t
val include_flags : ?project:Dune_project.t -> t -> Lib_mode.t -> _ Command.Args.t
val include_flags : ?project:Dune_project.t -> t -> t -> Lib_mode.t -> _ Command.Args.t
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it takes 2 arguments of type t, I think it'd be useful to label these.

val melange_emission_include_flags : ?project:Dune_project.t -> t -> _ Command.Args.t
val c_include_flags : t -> Super_context.t -> _ Command.Args.t
val toplevel_ld_paths : t -> Path.Set.t
Expand Down
6 changes: 2 additions & 4 deletions src/dune_rules/merlin/ocaml_index.ml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ let cctx_rules cctx =

The implicit transitive libs correspond to the set:
(requires_link \ req_compile) *)
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
Copy link
Collaborator Author

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@voodoos sounds OK?

Copy link
Collaborator

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.)

Copy link
Collaborator Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Collaborator

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

in
Lib_flags.L.include_flags non_compile_libs (Lib_mode.Ocaml Byte)
Lib_flags.L.include_flags [] non_compile_libs (Lib_mode.Ocaml Byte)
in
(* Indexing depends (recursively) on [required_compile] libs:
- These libs's cmt files should be built before indexing starts
Expand Down
1 change: 1 addition & 0 deletions src/ocaml/version.ml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ let supports_alerts version = version >= (4, 8, 0)
let has_sandboxed_otherlibs version = version >= (5, 0, 0)
let has_META_files version = version >= (5, 0, 0)
let supports_bin_annot_occurrences version = version >= (5, 2, 0)
let supports_hidden_includes version = version >= (5, 2, 0)
3 changes: 3 additions & 0 deletions src/ocaml/version.mli
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ val has_META_files : t -> bool

(** Whether the compiler supports occurrences indexation *)
val supports_bin_annot_occurrences : t -> bool

(** Whether the compiler supports the -H flag *)
val supports_hidden_includes : t -> bool
3 changes: 3 additions & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/bar.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let x = 5

let y = Foo.v
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the newline at eof in this test (and others) need fixing

18 changes: 18 additions & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
(library
(name foo)
(modules foo))

(library
(name bar)
(modules bar)
(libraries foo))

(executable
(name run)
(modules run)
(libraries bar))

(executable
(name runf)
(modules runf)
(libraries bar))
3 changes: 3 additions & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/foo.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let v = 9

let w = 4
1 change: 1 addition & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/run.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let _ = Bar.y
36 changes: 36 additions & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/run.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
Should include Foo with -H:

$ getincludes () {
> dune build --verbose ./run.exe 2>&1 | grep run.ml | grep -E -o "\-$1\s(.foo)\S*" | sed s/\-$1//g | tr -d '[:space:]'
> }

$ supports_H=$(if ocamlc -H x --help >/dev/null 2>&1; then echo true; else echo false; fi)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend doing that in 2 tests:

  • one that assumes ocaml >= 5.2 (guarded with (enabled_if (>= %{ocaml_version} 5.2)))
  • one that assumes ocaml < 5.2 (similarly guarded)

And then you can use "normal" cram tests, which would consist in just running getincludes and displaying the output.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, thanks for the review.
I am familiar with gaurding executables and libraries with the enabled_if (essentially making them hidden to the test). However, this would not be very useful here.

I have tried to add the enabled_if to a cram stanza in the dune file, hoping that would disable the whole test. This seems to not work since when I try to build the test it still runs irrespective of the condition mentioned with enabled_if.

Do you have any suggestions on how one would use enabled_if in order to disable the whole test?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ fooincludes='.foo.objs/byte.foo.objs/byte.foo.objs/native'
$ cat >dune-project <<EOF
> (lang dune 3.17)
> (implicit_transitive_deps true)
> EOF

$ if $supports_H; then [ "$(getincludes I)" = $fooincludes ]; fi
$ if ! $supports_H; then [ "$(getincludes I)" = $fooincludes ]; fi

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

$ if $supports_H; then [ "$(getincludes H)" = $fooincludes ]; fi
$ if ! $supports_H; then [ "$(getincludes I)" = '' ]; fi


Test transitive deps can not be directly accessed, both for compiler versions supporting -H or not:
$ cat >dune-project <<EOF
> (lang dune 3.17)
> (implicit_transitive_deps false)
> EOF

$ dune build ./runf.exe 2>&1 | grep -v ocamlc
File "runf.ml", line 1, characters 16-21:
1 | let a = Bar.y + Foo.v
^^^^^
Error: Unbound module Foo
1 change: 1 addition & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/runf.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let a = Bar.y + Foo.v
3 changes: 3 additions & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/tyxml/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(name run)
(libraries tyxml))
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
(lang dune 3.17)
(implicit_transitive_deps false)
2 changes: 2 additions & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/tyxml/run.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
open Tyxml.Html
let _ = p [ a [ txt "a" ] ]
5 changes: 5 additions & 0 deletions test/blackbox-tests/test-cases/hidden-deps.t/tyxml/run.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Test if the issue #274 is solved: the tyxml.functor is included with -H flag in
ITD = false case, and thus no type abstraction happens when it is used.

$ ocamlc -H x --help > /dev/null
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, you can guard the test behind (enabled_if).

$ if [ $? = 0 ]; then dune build fi
Loading