diff --git a/doc/changes/11866.md b/doc/changes/11866.md new file mode 100644 index 00000000000..a68686470af --- /dev/null +++ b/doc/changes/11866.md @@ -0,0 +1,5 @@ +- Introduce option `(implicit_transitive_deps + false-if-hidden-includes-supported)` that is equivalent to + `(implicit_transitive_deps false)` when `-H` is supported by the compiler + (OCaml >= 5.2) and equivalent to `(implicit_transitive_deps true)` otherwise. + (#11866, fixes #11212, @nojb) \ No newline at end of file diff --git a/doc/reference/dune-project/implicit_transitive_deps.rst b/doc/reference/dune-project/implicit_transitive_deps.rst index 37ab008b2f8..cf6ec46ef6d 100644 --- a/doc/reference/dune-project/implicit_transitive_deps.rst +++ b/doc/reference/dune-project/implicit_transitive_deps.rst @@ -1,12 +1,25 @@ implicit_transitive_deps ------------------------ -.. describe:: (implicit_transitive_deps ...) +.. describe:: (implicit_transitive_deps ) - Control whether transitive dependencies are made implicitly visible. + Control whether transitive dependencies are made implicitly visible during + compilation. - By default, Dune allows transitive dependencies of dependencies used when - compiling OCaml. However, this can be disabled by specifying: + ```` is one of: + + - ``true`` makes transitive dependencies implicitly visible. This is the + default. + + - ``false`` only listed dependencies are visible. If the ``-H`` flag is + supported by the compiler (OCaml version >= 5.2) and Dune language version + is >= 1.17, Dune will pass the flag to the compiler, which avoids some + corner cases (see below). + + - ``false-if-hidden-includes-supported`` only listed dependencies are visible + if the compiler supports the ``-H`` flag. Otherwise (OCaml version < 5.2), + the setting is ignored and all transitive dependencies are made visible. + Introduced in Dune 3.20. .. code:: dune @@ -17,9 +30,9 @@ implicit_transitive_deps We recommend users experiment with this mode and report any problems. - Note that you must use ``threads.posix`` instead of ``threads`` when using - this mode. This isn't an important limitation, as ``threads.vm`` is - deprecated anyway. + Note that if ``-H`` flag is not being used, you must use ``threads.posix`` + instead of ``threads`` when using this mode. This isn't an important + limitation, as ``threads.vm`` is deprecated anyway. In some situations, it can be desirable to selectively preserve the behavior of transitive dependencies' availability a library's users. For example, if diff --git a/src/dune_lang/dune_project.ml b/src/dune_lang/dune_project.ml index 402d22c4a2f..a41f05015bf 100644 --- a/src/dune_lang/dune_project.ml +++ b/src/dune_lang/dune_project.ml @@ -4,6 +4,68 @@ module Versioned_file = Dune_sexp.Versioned_file module Execution_parameters = Dune_engine.Execution_parameters module Compound_user_error = Dune_engine.Compound_user_error +module Implicit_transitive_deps = struct + type t = + | Enabled + | Disabled + | Disabled_with_hidden_includes + + let to_bool = function + | Enabled -> true + | Disabled | Disabled_with_hidden_includes -> false + ;; + + module Stanza = struct + type mode = t + + type t = + | Enabled + | Disabled + | Disabled_if_hidden_includes_supported + + let default ~lang:_ = Enabled + + let to_dyn = function + | Enabled -> Dyn.variant "Enabled" [] + | Disabled -> Dyn.variant "Disabled" [] + | Disabled_if_hidden_includes_supported -> + Dyn.variant "Disabled_if_hidden_includes_supported" [] + ;; + + let equal = Poly.equal + + let encode = function + | Enabled -> Encoder.bool true + | Disabled -> Encoder.bool false + | Disabled_if_hidden_includes_supported -> + Encoder.string "false-if-hidden-includes-supported" + ;; + + let decode = + let check ver = Syntax.since Stanza.syntax ver in + enum' + [ "true", check (1, 7) >>> return Enabled + ; "false", check (1, 7) >>> return Disabled + ; ( "false-if-hidden-includes-supported" + , check (3, 20) >>> return Disabled_if_hidden_includes_supported ) + ] + ;; + + let mode t ~ocaml_version ~dune_version : mode = + match t with + | Enabled -> Enabled + | Disabled -> + if Ocaml.Version.supports_hidden_includes ocaml_version && dune_version >= (3, 17) + then Disabled_with_hidden_includes + else Disabled + | Disabled_if_hidden_includes_supported -> + if Ocaml.Version.supports_hidden_includes ocaml_version + then Disabled_with_hidden_includes + else Enabled + ;; + end +end + type t = { name : Dune_project_name.t ; root : Path.Source.t @@ -15,7 +77,7 @@ type t = ; project_file : Path.Source.t option ; extension_args : Univ_map.t ; parsing_context : Univ_map.t - ; implicit_transitive_deps : bool + ; implicit_transitive_deps : Implicit_transitive_deps.Stanza.t ; wrapped_executables : bool ; map_workspace_root : bool ; executables_implicit_empty_intf : bool @@ -54,7 +116,14 @@ let version t = t.version let root t = t.root let stanza_parser t = Decoder.set key t t.stanza_parser let file t = t.project_file -let implicit_transitive_deps t = t.implicit_transitive_deps + +let implicit_transitive_deps t ocaml_version = + Implicit_transitive_deps.Stanza.mode + t.implicit_transitive_deps + ~ocaml_version + ~dune_version:t.dune_version +;; + let generate_opam_files t = t.generate_opam_files let warnings t = t.warnings let set_generate_opam_files generate_opam_files t = { t with generate_opam_files } @@ -106,7 +175,8 @@ let to_dyn ; ( "packages" , (list (pair Package.Name.to_dyn Package.to_dyn)) (Package.Name.Map.to_list packages) ) - ; "implicit_transitive_deps", bool implicit_transitive_deps + ; ( "implicit_transitive_deps" + , Implicit_transitive_deps.Stanza.to_dyn implicit_transitive_deps ) ; "wrapped_executables", bool wrapped_executables ; "map_workspace_root", bool map_workspace_root ; "executables_implicit_empty_intf", bool executables_implicit_empty_intf @@ -333,7 +403,6 @@ let interpret_lang_and_extensions ~(lang : Lang.Instance.t) ~explicit_extensions let filename = "dune-project" let opam_file_location_default ~lang:_ = `Relative_to_project -let implicit_transitive_deps_default ~lang:_ = true let wrapped_executables_default ~(lang : Lang.Instance.t) = lang.version >= (2, 0) let map_workspace_root_default ~(lang : Lang.Instance.t) = lang.version >= (3, 0) @@ -396,7 +465,7 @@ let infer ~dir info packages = let parsing_context, stanza_parser, extension_args = interpret_lang_and_extensions ~lang ~explicit_extensions:String.Map.empty in - let implicit_transitive_deps = implicit_transitive_deps_default ~lang in + let implicit_transitive_deps = Implicit_transitive_deps.Stanza.default ~lang in let wrapped_executables = wrapped_executables_default ~lang in let map_workspace_root = map_workspace_root_default ~lang in let executables_implicit_empty_intf = executables_implicit_empty_intf_default ~lang in @@ -493,10 +562,17 @@ let encode : t -> Dune_sexp.t list = List.filter_opt [ flag "generate_opam_files" generate_opam_files (fun ~lang:_ -> not generate_opam_files) - ; flag - "implicit_transitive_deps" - implicit_transitive_deps - implicit_transitive_deps_default + ; (if + Implicit_transitive_deps.Stanza.equal + implicit_transitive_deps + (Implicit_transitive_deps.Stanza.default ~lang) + then None + else + Some + (constr + "implicit_transitive_deps" + Implicit_transitive_deps.Stanza.encode + implicit_transitive_deps)) ; flag "wrapped_executables" wrapped_executables wrapped_executables_default ; flag "map_workspace_root" map_workspace_root map_workspace_root_default ; flag @@ -762,7 +838,7 @@ let parse ~dir ~(lang : Lang.Instance.t) ~file = version of extensions before parsing them. *) Extension.instantiate ~dune_lang_ver:lang.version ~loc ~parse_args name ver) and+ implicit_transitive_deps = - field_o_b "implicit_transitive_deps" ~check:(Syntax.since Stanza.syntax (1, 7)) + field_o "implicit_transitive_deps" Implicit_transitive_deps.Stanza.decode and+ wrapped_executables = field_o_b "wrapped_executables" ~check:(Syntax.since Stanza.syntax (1, 11)) and+ map_workspace_root = @@ -843,7 +919,7 @@ let parse ~dir ~(lang : Lang.Instance.t) ~file = let implicit_transitive_deps = Option.value implicit_transitive_deps - ~default:(implicit_transitive_deps_default ~lang) + ~default:(Implicit_transitive_deps.Stanza.default ~lang) in let wrapped_executables = Option.value wrapped_executables ~default:(wrapped_executables_default ~lang) diff --git a/src/dune_lang/dune_project.mli b/src/dune_lang/dune_project.mli index a9e30701bbc..6663f433f26 100644 --- a/src/dune_lang/dune_project.mli +++ b/src/dune_lang/dune_project.mli @@ -3,6 +3,15 @@ open Dune_config module Pin_stanza := Pin_stanza module Execution_parameters := Dune_engine.Execution_parameters +module Implicit_transitive_deps : sig + type t = + | Enabled + | Disabled + | Disabled_with_hidden_includes + + val to_bool : t -> bool +end + type t val to_dyn : t -> Dyn.t @@ -95,7 +104,7 @@ val find_extension_args : t -> 'a Extension.t -> 'a option val is_extension_set : t -> 'a Extension.t -> bool val set_parsing_context : t -> 'a Decoder.t -> 'a Decoder.t -val implicit_transitive_deps : t -> bool +val implicit_transitive_deps : t -> Ocaml.Version.t -> Implicit_transitive_deps.t val dune_version : t -> Syntax.Version.t val wrapped_executables : t -> bool val map_workspace_root : t -> bool diff --git a/src/dune_rules/compilation_context.ml b/src/dune_rules/compilation_context.ml index 42013a618fd..a239ea5053b 100644 --- a/src/dune_rules/compilation_context.ml +++ b/src/dune_rules/compilation_context.ml @@ -152,12 +152,10 @@ let create let context = Super_context.context super_context 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, Resolve.Memo.return [] - else if - Version.supports_hidden_includes ocaml.version - && Dune_project.dune_version project >= (3, 17) - then ( + match Dune_project.implicit_transitive_deps project ocaml.version with + | Enabled -> Memo.Lazy.force requires_link, Resolve.Memo.return [] + | Disabled -> requires_compile, Resolve.Memo.return [] + | Disabled_with_hidden_includes -> let requires_hidden = let open Resolve.Memo.O in let+ requires_compile = requires_compile @@ -166,8 +164,7 @@ let create List.iter ~f:(fun lib -> Table.set requires_table lib ()) requires_compile; List.filter requires_link ~f:(fun l -> not (Table.mem requires_table l)) in - requires_compile, requires_hidden) - else requires_compile, Resolve.Memo.return [] + requires_compile, requires_hidden in let sandbox = Sandbox_config.no_special_requirements in let modes = diff --git a/src/dune_rules/merlin/merlin.ml b/src/dune_rules/merlin/merlin.ml index 5f3608d94e4..a86d411a063 100644 --- a/src/dune_rules/merlin/merlin.ml +++ b/src/dune_rules/merlin/merlin.ml @@ -687,8 +687,12 @@ module Unprocessed = struct >>= function | Some lib -> let+ libs = - let linking = - Dune_project.implicit_transitive_deps (Scope.project scope) + let* linking = + let+ ocaml = Context.ocaml (Super_context.context sctx) in + Dune_project.Implicit_transitive_deps.to_bool + (Dune_project.implicit_transitive_deps + (Scope.project scope) + ocaml.version) in Lib.closure [ lib ] ~linking |> Resolve.Memo.peek diff --git a/test/blackbox-tests/test-cases/hidden-deps-supported.t/run.t b/test/blackbox-tests/test-cases/hidden-deps-supported.t/run.t index ef2fcae83f0..3d49477562f 100644 --- a/test/blackbox-tests/test-cases/hidden-deps-supported.t/run.t +++ b/test/blackbox-tests/test-cases/hidden-deps-supported.t/run.t @@ -21,9 +21,13 @@ implicit_transitive_deps is set to false. -I .run.eobjs/byte -I .run.eobjs/native +In the following two tests we use "false-if-hidden-includes-supported" for +testing purposes, but since this test is guarded by OCaml version >= 5.2, this +should be equivalent to "false". + $ cat >dune-project < (lang dune 3.17) - > (implicit_transitive_deps false) + > (lang dune 3.20) + > (implicit_transitive_deps false-if-hidden-includes-supported) > EOF $ getincludes @@ -40,8 +44,8 @@ implicit_transitive_deps is set to false. Test transitive deps can not be directly accessed, both for compiler versions supporting -H or not: $ cat >dune-project < (lang dune 3.17) - > (implicit_transitive_deps false) + > (lang dune 3.20) + > (implicit_transitive_deps false-if-hidden-includes-supported) > EOF $ dune build ./runf.exe 2>&1 | grep -v ocamlc diff --git a/test/blackbox-tests/test-cases/hidden-deps-unsupported.t/run.t b/test/blackbox-tests/test-cases/hidden-deps-unsupported.t/run.t index c51b4249eae..3ee93d7568a 100644 --- a/test/blackbox-tests/test-cases/hidden-deps-unsupported.t/run.t +++ b/test/blackbox-tests/test-cases/hidden-deps-unsupported.t/run.t @@ -24,7 +24,14 @@ the new -H feature added. $ cat >dune-project < (lang dune 3.17) - > (implicit_transitive_deps false) + > (implicit_transitive_deps false-if-hidden-includes-supported) + > EOF + + $ dune build + + $ cat >dune-project < (lang dune 3.20) + > (implicit_transitive_deps false-if-hidden-includes-supported) > EOF $ getincludes