From 07be7c1a6a9b2010331a7c2ad168224347a53f78 Mon Sep 17 00:00:00 2001 From: Simon Spies Date: Thu, 18 Dec 2025 11:23:25 +0000 Subject: [PATCH 1/2] -bin-annot-cms support Signed-off-by: Simon Spies --- src/dune_lang/dune_env.ml | 14 +- src/dune_lang/dune_env.mli | 1 + src/dune_rules/check_rules.ml | 7 +- src/dune_rules/compilation_context.ml | 10 +- src/dune_rules/compilation_context.mli | 2 + src/dune_rules/env_stanza_db.ml | 14 ++ src/dune_rules/env_stanza_db.mli | 1 + src/dune_rules/install_rules.ml | 17 +- src/dune_rules/module_compilation.ml | 12 ++ src/dune_rules/obj_dir.ml | 11 ++ src/dune_rules/obj_dir.mli | 7 + src/ocaml/ml_kind.ml | 5 + src/ocaml/ml_kind.mli | 1 + .../test-cases/oxcaml/bin-annot-cms.t | 146 ++++++++++++++++++ .../oxcaml/instantiate-parameterised.t | 36 +++++ .../oxcaml/library-field-parameters.t | 7 + .../test-cases/oxcaml/library_parameter.t | 4 + 17 files changed, 288 insertions(+), 7 deletions(-) create mode 100644 test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t diff --git a/src/dune_lang/dune_env.ml b/src/dune_lang/dune_env.ml index aa8ab94aa76..9d8693c5b94 100644 --- a/src/dune_lang/dune_env.ml +++ b/src/dune_lang/dune_env.ml @@ -85,11 +85,13 @@ type config = ; error_on_use : User_message.t option ; warn_on_load : User_message.t option ; bin_annot : bool option + ; bin_annot_cms : bool option } -let dyn_of_config { bin_annot; _ } = +let dyn_of_config { bin_annot; bin_annot_cms; _ } = let open Dyn in - record [ "bin_annot", option bool bin_annot ] + record + [ "bin_annot", option bool bin_annot; "bin_annot_cms", option bool bin_annot_cms ] ;; let equal_config @@ -109,6 +111,7 @@ let equal_config ; error_on_use ; warn_on_load ; bin_annot + ; bin_annot_cms } t = @@ -131,6 +134,7 @@ let equal_config && Option.equal User_message.equal error_on_use t.error_on_use && Option.equal User_message.equal warn_on_load t.warn_on_load && Option.equal Bool.equal bin_annot t.bin_annot + && Option.equal Bool.equal bin_annot_cms t.bin_annot_cms ;; let hash_config = Poly.hash @@ -152,6 +156,7 @@ let empty_config = ; error_on_use = None ; warn_on_load = None ; bin_annot = None + ; bin_annot_cms = None } ;; @@ -231,6 +236,7 @@ let wasm_of_ocaml_field = ;; let bin_annot = field_o "bin_annot" (Syntax.since Stanza.syntax (3, 8) >>> bool) +let bin_annot_cms = field_o "bin_annot_cms" (Syntax.since Oxcaml.syntax (0, 1) >>> bool) let config = let+ flags = Ocaml_flags.Spec.decode @@ -251,7 +257,8 @@ let config = and+ coq = Coq_env.decode and+ rocq = Rocq_env.decode and+ format_config = Format_config.field ~since:(2, 8) - and+ bin_annot = bin_annot in + and+ bin_annot = bin_annot + and+ bin_annot_cms = bin_annot_cms in let menhir = match menhir_flags, menhir with | Some flags, None -> { Menhir_env.empty with flags } @@ -276,6 +283,7 @@ let config = ; error_on_use = None ; warn_on_load = None ; bin_annot + ; bin_annot_cms } ;; diff --git a/src/dune_lang/dune_env.mli b/src/dune_lang/dune_env.mli index 33568d4a50f..ef5d1d089cd 100644 --- a/src/dune_lang/dune_env.mli +++ b/src/dune_lang/dune_env.mli @@ -37,6 +37,7 @@ type config = ; error_on_use : User_message.t option ; warn_on_load : User_message.t option ; bin_annot : bool option + ; bin_annot_cms : bool option } type pattern = diff --git a/src/dune_rules/check_rules.ml b/src/dune_rules/check_rules.ml index dff8be06ca8..0be8f601f06 100644 --- a/src/dune_rules/check_rules.ml +++ b/src/dune_rules/check_rules.ml @@ -1,7 +1,12 @@ open Import let dev_files = - [ Ml_kind.cmt_ext Impl; Ml_kind.cmt_ext Intf; Cm_kind.ext Cmi ] + [ Ml_kind.cmt_ext Impl + ; Ml_kind.cmt_ext Intf + ; Ml_kind.cms_ext Impl + ; Ml_kind.cms_ext Intf + ; Cm_kind.ext Cmi + ] |> List.map ~f:(String.drop_prefix_if_exists ~prefix:".") |> Glob.matching_extensions ;; diff --git a/src/dune_rules/compilation_context.ml b/src/dune_rules/compilation_context.ml index 8a2a3970a44..f0e3a257467 100644 --- a/src/dune_rules/compilation_context.ml +++ b/src/dune_rules/compilation_context.ml @@ -99,6 +99,7 @@ type t = ; melange_package_name : Lib_name.t option ; modes : Lib_mode.Map.Set.t ; bin_annot : bool + ; bin_annot_cms : bool ; loc : Loc.t option ; ocaml : Ocaml_toolchain.t ; for_ : Compilation_mode.t @@ -126,6 +127,7 @@ let melange_package_name t = t.melange_package_name let implements t = t.implements let modes t = t.modes let bin_annot t = t.bin_annot +let bin_annot_cms t = t.bin_annot_cms let context t = Super_context.context t.super_context let dep_graphs t = t.modules.dep_graphs let ocaml t = t.ocaml @@ -160,6 +162,7 @@ let create ?parameters ?modes ?bin_annot + ?bin_annot_cms ?loc ?instances for_ @@ -209,6 +212,10 @@ let create match bin_annot with | Some b -> Memo.return b | None -> Env_stanza_db.bin_annot ~dir:(Obj_dir.dir obj_dir) + and+ bin_annot_cms = + match bin_annot_cms with + | Some b -> Memo.return b + | None -> Env_stanza_db.bin_annot_cms ~dir:(Obj_dir.dir obj_dir) in { super_context ; scope @@ -230,6 +237,7 @@ let create ; melange_package_name ; modes ; bin_annot + ; bin_annot_cms ; loc ; ocaml ; instances @@ -341,7 +349,7 @@ let for_plugin_executable t ~embed_in_plugin_libraries = { t with requires_link } ;; -let without_bin_annot t = { t with bin_annot = false } +let without_bin_annot t = { t with bin_annot = false; bin_annot_cms = false } let set_obj_dir t obj_dir = { t with obj_dir } let set_modes t ~modes = { t with modes } diff --git a/src/dune_rules/compilation_context.mli b/src/dune_rules/compilation_context.mli index 159248bf100..c3ba9511f97 100644 --- a/src/dune_rules/compilation_context.mli +++ b/src/dune_rules/compilation_context.mli @@ -36,6 +36,7 @@ val create -> ?parameters:Lib.t list Resolve.Memo.t -> ?modes:Lib_mode.Map.Set.t -> ?bin_annot:bool + -> ?bin_annot_cms:bool -> ?loc:Loc.t -> ?instances:Parameterised_instances.t Resolve.Memo.t -> Compilation_mode.t @@ -82,6 +83,7 @@ val for_module_generated_at_link_time val for_plugin_executable : t -> embed_in_plugin_libraries:(Loc.t * Lib_name.t) list -> t val bin_annot : t -> bool +val bin_annot_cms : t -> bool val without_bin_annot : t -> t (** The dependency graph for the modules of the library. *) diff --git a/src/dune_rules/env_stanza_db.ml b/src/dune_rules/env_stanza_db.ml index 138f3fa8e47..7168696105d 100644 --- a/src/dune_rules/env_stanza_db.ml +++ b/src/dune_rules/env_stanza_db.ml @@ -95,6 +95,20 @@ let bin_annot ~dir = value ~default:true ~dir ~f:(fun (t : Dune_env.config) -> Memo.return t.bin_annot) ;; +let bin_annot_cms ~dir = + let* explicit = + value ~default:None ~dir ~f:(fun (t : Dune_env.config) -> + Memo.return (Option.map t.bin_annot_cms ~f:Option.some)) + in + match explicit with + | Some b -> Memo.return b + | None -> + (* Enabled by default for OxCaml *) + let* context = Context.DB.by_dir dir in + let+ ocaml = Context.ocaml context in + Ocaml_config.ox ocaml.ocaml_config +;; + let inline_tests ~dir = value ~default:None ~dir ~f:(fun (t : Dune_env.config) -> Memo.return diff --git a/src/dune_rules/env_stanza_db.mli b/src/dune_rules/env_stanza_db.mli index 5a92f0d5cad..64ac6f0c302 100644 --- a/src/dune_rules/env_stanza_db.mli +++ b/src/dune_rules/env_stanza_db.mli @@ -15,6 +15,7 @@ val value_opt val profile : dir:Path.Build.t -> Profile.t Memo.t val bin_annot : dir:Path.Build.t -> bool Memo.t +val bin_annot_cms : dir:Path.Build.t -> bool Memo.t val inline_tests : dir:Path.Build.t -> Dune_env.Inline_tests.t Memo.t (** [inherited ~name ~root ~f] create a function that computes a value derived diff --git a/src/dune_rules/install_rules.ml b/src/dune_rules/install_rules.ml index f6b8c051483..17b82de7bb4 100644 --- a/src/dune_rules/install_rules.ml +++ b/src/dune_rules/install_rules.ml @@ -336,7 +336,8 @@ end = struct in let set_dir m = List.rev_map ~f:(fun (cm_kind, p) -> cm_dir m cm_kind, p) in let+ modules_impl = - let+ bin_annot = Env_stanza_db.bin_annot ~dir in + let+ bin_annot = Env_stanza_db.bin_annot ~dir + and+ bin_annot_cms = Env_stanza_db.bin_annot_cms ~dir in List.rev_concat_map installable_modules.impl ~f:(fun m -> let cmt_files = match bin_annot with @@ -350,7 +351,19 @@ end = struct condition (kind, Obj_dir.Module.cmt_file obj_dir m ~ml_kind ~cm_kind:kind))) in - List.rev_append (common m) cmt_files |> set_dir m) + let cms_files = + match bin_annot_cms with + | false -> [] + | true -> + List.rev_concat_map Ml_kind.all ~f:(fun ml_kind -> + List.rev_concat_map + [ native || byte, Lib_mode.Cm_kind.Ocaml Cmi ] + ~f:(fun (condition, kind) -> + if_ + condition + (kind, Obj_dir.Module.cms_file obj_dir m ~ml_kind ~cm_kind:kind))) + in + List.rev_append (common m) (List.rev_append cmt_files cms_files) |> set_dir m) in let modules_vlib = List.rev_concat_map installable_modules.vlib ~f:(fun m -> diff --git a/src/dune_rules/module_compilation.ml b/src/dune_rules/module_compilation.ml index 0b80e24de3b..bfacd15c137 100644 --- a/src/dune_rules/module_compilation.ml +++ b/src/dune_rules/module_compilation.ml @@ -252,6 +252,17 @@ let build_cm S [ Hidden_targets [ fn ]; As annots ]) else Command.Args.empty in + let cms_args = + match cm_kind with + | Ocaml Cmx | Melange _ -> Command.Args.empty + | Ocaml (Cmi | Cmo) -> + if Compilation_context.bin_annot_cms cctx && Ocaml_config.ox ocaml.ocaml_config + then ( + match Obj_dir.Module.cms_file obj_dir m ~cm_kind ~ml_kind with + | None -> Command.Args.empty + | Some fn -> S [ Hidden_targets [ fn ]; As [ "-bin-annot-cms" ] ]) + else Command.Args.empty + in let opaque_arg : _ Command.Args.t = let intf_only = cm_kind = Ocaml Cmi && not (Module.has m ~ml_kind:Impl) in if opaque || (intf_only && Ocaml.Version.supports_opaque_for_mli ocaml.version) @@ -320,6 +331,7 @@ let build_cm [ flags ; pp_flags ; cmt_args + ; cms_args ; Command.Args.S obj_dirs ; Command.Args.as_any (Lib_mode.Cm_kind.Map.get (Compilation_context.includes cctx) cm_kind) diff --git a/src/dune_rules/obj_dir.ml b/src/dune_rules/obj_dir.ml index 6ea63af1945..9f30e569e1f 100644 --- a/src/dune_rules/obj_dir.ml +++ b/src/dune_rules/obj_dir.ml @@ -594,6 +594,17 @@ module Module = struct obj_file t m ~kind:cmi_kind ~ext ;; + let cms_file t m ~(ml_kind : Ml_kind.t) ~cm_kind = + let file = Module.file m ~ml_kind in + let ext = Ml_kind.cms_ext ml_kind in + (* The .cms files are stored alongside .cmi files (in [byte_dir] for OCaml). + [Lib_mode.Cm_kind.cmi] ensures we look in the right directory: + - [Ocaml Cmi] -> [Ocaml Cmi] -> [byte_dir] + - [Ocaml Cmx] -> [Ocaml Cmi] -> [byte_dir] *) + let cmi_kind = Lib_mode.Cm_kind.cmi cm_kind in + Option.map file ~f:(fun _ -> obj_file t m ~kind:cmi_kind ~ext) + ;; + let odoc t m = let obj_name = Module.obj_name m in let basename = Module_name.Unique.artifact_filename obj_name ~ext:".odoc" in diff --git a/src/dune_rules/obj_dir.mli b/src/dune_rules/obj_dir.mli index d7943f2730a..136ea452fa8 100644 --- a/src/dune_rules/obj_dir.mli +++ b/src/dune_rules/obj_dir.mli @@ -131,6 +131,13 @@ module Module : sig (** Either the .cmti, or .cmt if the module has no interface *) val cmti_file : 'path t -> Module.t -> cm_kind:Lib_mode.Cm_kind.t -> 'path + val cms_file + : 'path t + -> Module.t + -> ml_kind:Ml_kind.t + -> cm_kind:Lib_mode.Cm_kind.t + -> 'path option + val odoc : 'path t -> Module.t -> 'path module L : sig diff --git a/src/ocaml/ml_kind.ml b/src/ocaml/ml_kind.ml index fe57d6827b4..e3dba3883b3 100644 --- a/src/ocaml/ml_kind.ml +++ b/src/ocaml/ml_kind.ml @@ -20,6 +20,11 @@ let cmt_ext = function | Intf -> ".cmti" ;; +let cms_ext = function + | Impl -> ".cms" + | Intf -> ".cmsi" +;; + module Dict = struct type 'a t = { impl : 'a diff --git a/src/ocaml/ml_kind.mli b/src/ocaml/ml_kind.mli index f1b2eac504b..5c86f7de550 100644 --- a/src/ocaml/ml_kind.mli +++ b/src/ocaml/ml_kind.mli @@ -9,6 +9,7 @@ val choose : t -> impl:'a -> intf:'a -> 'a val to_string : t -> string val to_dyn : t -> Dyn.t val cmt_ext : t -> string +val cms_ext : t -> string module Dict : sig type kind := t diff --git a/test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t b/test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t new file mode 100644 index 00000000000..639f8bf5273 --- /dev/null +++ b/test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t @@ -0,0 +1,146 @@ +Test generation of cms/cmsi files using (bin_annot_cms ...) in env stanza and via flags. +On OxCaml compilers (where Ocaml_config.ox is true), bin_annot_cms defaults to true. + +This test compares four configurations: +1. default: same as (env (_ (bin_annot_cms true))) +2. env stanza: (env (_ (bin_annot_cms true))) - explicit enable +3. flags field: (flags (:standard -bin-annot-cms)) - passes flag directly to compiler +4. env stanza: (env (_ (bin_annot_cms false))) - explicit disable + +Key differences between env stanza and flags field: +- env stanza settings apply to ALL modules including auto-generated alias modules +- flags field only applies to user modules, NOT to auto-generated alias modules, +| because Compilation_context.for_alias_module resets flags to defaults. +- env stanza only generates cms/cmsi during byte/cmi compilation (like cmt/cmti), +| whereas flags field passes the flag to ALL compilations including native + + $ . ./helpers.sh + +Create a project with OxCaml language extension (required for bin_annot_cms): + + $ cat >dune-project < (lang dune 3.21) + > (using oxcaml 0.1) + > EOF + +Create four libraries to test different configurations: + + $ mkdir -p oxcaml_default with_env_stanza with_flags_field without_flag + +Default setting, same as (bin_annot_cms true): + + $ cat >oxcaml_default/foo.ml < type t = bool + > let f x = if x then false else true + > EOF + + $ cat >oxcaml_default/foo.mli < type t + > val f : t -> t + > EOF + + $ cat >oxcaml_default/dune < (library (name oxcaml_default)) + > EOF + + +Library using env stanza (bin_annot_cms true): + + $ cat >with_env_stanza/foo.ml < type t = int + > let f x = x + 1 + > EOF + + $ cat >with_env_stanza/foo.mli < type t + > val f : t -> t + > EOF + + $ cat >with_env_stanza/dune < (env (_ (bin_annot_cms true) (bin_annot true))) + > (library (name with_env_stanza)) + > EOF + +Library using flags field (-bin-annot-cms): + + $ cat >with_flags_field/bar.ml < type t = string + > let g x = x ^ "!" + > EOF + + $ cat >with_flags_field/bar.mli < type t + > val g : t -> t + > EOF + + $ cat >with_flags_field/dune < (library + > (name with_flags_field) + > (flags (:standard -bin-annot-cms -bin-annot))) + > EOF + +Library with bin_annot_cms explicitly disabled (should NOT produce cms files): + + $ cat >without_flag/baz.ml < type t = float + > let h x = x +. 1.0 + > EOF + + $ cat >without_flag/baz.mli < type t + > val h : t -> t + > EOF + + $ cat >without_flag/dune < (env (_ (bin_annot_cms false))) + > (library (name without_flag)) + > EOF + + $ dune build + +Verify cms/cmsi files are generated for both env stanza and flags approaches. + +default: .cms/.cmsi files are generated by default in the byte folder. + $ find _build/default/oxcaml_default -name '*.cms' -o -name '*.cmsi' -o -name "*.cmt" -o -name "*.cmti" | sort + _build/default/oxcaml_default/.oxcaml_default.objs/byte/oxcaml_default.cms + _build/default/oxcaml_default/.oxcaml_default.objs/byte/oxcaml_default.cmt + _build/default/oxcaml_default/.oxcaml_default.objs/byte/oxcaml_default__Foo.cms + _build/default/oxcaml_default/.oxcaml_default.objs/byte/oxcaml_default__Foo.cmsi + _build/default/oxcaml_default/.oxcaml_default.objs/byte/oxcaml_default__Foo.cmt + _build/default/oxcaml_default/.oxcaml_default.objs/byte/oxcaml_default__Foo.cmti + + +with_env_stanza: Note that both the alias module (with_env_stanza.cms) and user +module (with_env_stanza__Foo.cms) have cms files. All files are in byte/ only +(like cmt/cmti). + + $ find _build/default/with_env_stanza -name '*.cms' -o -name '*.cmsi' -o -name "*.cmt" -o -name "*.cmti" | sort + _build/default/with_env_stanza/.with_env_stanza.objs/byte/with_env_stanza.cms + _build/default/with_env_stanza/.with_env_stanza.objs/byte/with_env_stanza.cmt + _build/default/with_env_stanza/.with_env_stanza.objs/byte/with_env_stanza__Foo.cms + _build/default/with_env_stanza/.with_env_stanza.objs/byte/with_env_stanza__Foo.cmsi + _build/default/with_env_stanza/.with_env_stanza.objs/byte/with_env_stanza__Foo.cmt + _build/default/with_env_stanza/.with_env_stanza.objs/byte/with_env_stanza__Foo.cmti + +with_flags_field: Note that cms/cmt files appear in native/ because the flags +are passed to all compilations. But the module alias file does not get an entry +in the native directory, because its flags are reset to default (dropping the +user flags). + + $ find _build/default/with_flags_field -name '*.cms' -o -name '*.cmsi' -o -name "*.cmt" -o -name "*.cmti" | sort + _build/default/with_flags_field/.with_flags_field.objs/byte/with_flags_field.cms + _build/default/with_flags_field/.with_flags_field.objs/byte/with_flags_field.cmt + _build/default/with_flags_field/.with_flags_field.objs/byte/with_flags_field__Bar.cms + _build/default/with_flags_field/.with_flags_field.objs/byte/with_flags_field__Bar.cmsi + _build/default/with_flags_field/.with_flags_field.objs/byte/with_flags_field__Bar.cmt + _build/default/with_flags_field/.with_flags_field.objs/byte/with_flags_field__Bar.cmti + _build/default/with_flags_field/.with_flags_field.objs/native/with_flags_field__Bar.cms + _build/default/with_flags_field/.with_flags_field.objs/native/with_flags_field__Bar.cmt + +without_flag: No cms/cmsi files since bin_annot_cms is explicitly disabled. Only cmt/cmti +from the default bin_annot=true setting. + + $ find _build/default/without_flag -name '*.cms' -o -name '*.cmsi' -o -name "*.cmt" -o -name "*.cmti" | sort + _build/default/without_flag/.without_flag.objs/byte/without_flag.cmt + _build/default/without_flag/.without_flag.objs/byte/without_flag__Baz.cmt + _build/default/without_flag/.without_flag.objs/byte/without_flag__Baz.cmti diff --git a/test/blackbox-tests/test-cases/oxcaml/instantiate-parameterised.t b/test/blackbox-tests/test-cases/oxcaml/instantiate-parameterised.t index 2be851efde9..76cdc9d4957 100644 --- a/test/blackbox-tests/test-cases/oxcaml/instantiate-parameterised.t +++ b/test/blackbox-tests/test-cases/oxcaml/instantiate-parameterised.t @@ -419,56 +419,67 @@ The `dune-package` should list the different instantiations in the dependencies: (lib (META a/a.cmi + a/a.cmsi a/a.cmti a/a.mli a_impl/a_impl.a a_impl/a_impl.cma a_impl/a_impl.cmi + a_impl/a_impl.cms a_impl/a_impl.cmt a_impl/a_impl.cmx a_impl/a_impl.cmxa a_impl/a_impl.ml a_impl/a_impl__a_impl__.cmi + a_impl/a_impl__a_impl__.cms a_impl/a_impl__a_impl__.cmt a_impl/a_impl__a_impl__.cmx a_impl/a_impl__a_impl__.ml a_of_b/a_of_b.a a_of_b/a_of_b.cma a_of_b/a_of_b.cmi + a_of_b/a_of_b.cms a_of_b/a_of_b.cmt a_of_b/a_of_b.cmx a_of_b/a_of_b.cmxa a_of_b/a_of_b.ml a_of_b/a_of_b__a_of_b__.cmi + a_of_b/a_of_b__a_of_b__.cms a_of_b/a_of_b__a_of_b__.cmt a_of_b/a_of_b__a_of_b__.cmx a_of_b/a_of_b__a_of_b__.ml b/b.cmi + b/b.cmsi b/b.cmti b/b.mli b_impl/b_dep.ml b_impl/b_impl.a b_impl/b_impl.cma b_impl/b_impl.cmi + b_impl/b_impl.cms b_impl/b_impl.cmt b_impl/b_impl.cmx b_impl/b_impl.cmxa b_impl/b_impl.ml b_impl/b_impl__B_dep.cmi + b_impl/b_impl__B_dep.cms b_impl/b_impl__B_dep.cmt b_impl/b_impl__B_dep.cmx b_impl/b_impl__b_impl__.cmi + b_impl/b_impl__b_impl__.cms b_impl/b_impl__b_impl__.cmt b_impl/b_impl__b_impl__.cmx b_impl/b_impl__b_impl__.ml b_impl2/b_impl2.a b_impl2/b_impl2.cma b_impl2/b_impl2.cmi + b_impl2/b_impl2.cms b_impl2/b_impl2.cmt b_impl2/b_impl2.cmx b_impl2/b_impl2.cmxa b_impl2/b_impl2.ml b_impl2/b_impl2__b_impl2__.cmi + b_impl2/b_impl2__b_impl2__.cms b_impl2/b_impl2__b_impl2__.cmt b_impl2/b_impl2__b_impl2__.cmx b_impl2/b_impl2__b_impl2__.ml @@ -476,6 +487,7 @@ The `dune-package` should list the different instantiations in the dependencies: lib_ab/lib_ab.a lib_ab/lib_ab.cma lib_ab/lib_ab.cmi + lib_ab/lib_ab.cms lib_ab/lib_ab.cmt lib_ab/lib_ab.cmx lib_ab/lib_ab.cmxa @@ -484,24 +496,30 @@ The `dune-package` should list the different instantiations in the dependencies: lib_apply/lib_apply.a lib_apply/lib_apply.cma lib_apply/lib_apply.cmi + lib_apply/lib_apply.cms lib_apply/lib_apply.cmt lib_apply/lib_apply.cmx lib_apply/lib_apply.cmxa lib_apply/lib_apply.ml lib_apply/lib_apply__.cmi + lib_apply/lib_apply__.cms lib_apply/lib_apply__.cmt lib_apply/lib_apply__.cmx lib_apply/lib_apply__.ml lib_apply/lib_apply__F.cmi + lib_apply/lib_apply__F.cms lib_apply/lib_apply__F.cmt lib_apply/lib_apply__F.cmx lib_apply/lib_apply__X.cmi + lib_apply/lib_apply__X.cms lib_apply/lib_apply__X.cmt lib_apply/lib_apply__X.cmx lib_apply/lib_apply__Y.cmi + lib_apply/lib_apply__Y.cms lib_apply/lib_apply__Y.cmt lib_apply/lib_apply__Y.cmx lib_apply/lib_apply__Z.cmi + lib_apply/lib_apply__Z.cms lib_apply/lib_apply__Z.cmt lib_apply/lib_apply__Z.cmx lib_apply/x.ml @@ -721,56 +739,67 @@ And all the required files should be installed: lib: [ "_build/install/default/lib/project/META" "_build/install/default/lib/project/a/a.cmi" {"a/a.cmi"} + "_build/install/default/lib/project/a/a.cmsi" {"a/a.cmsi"} "_build/install/default/lib/project/a/a.cmti" {"a/a.cmti"} "_build/install/default/lib/project/a/a.mli" {"a/a.mli"} "_build/install/default/lib/project/a_impl/a_impl.a" {"a_impl/a_impl.a"} "_build/install/default/lib/project/a_impl/a_impl.cma" {"a_impl/a_impl.cma"} "_build/install/default/lib/project/a_impl/a_impl.cmi" {"a_impl/a_impl.cmi"} + "_build/install/default/lib/project/a_impl/a_impl.cms" {"a_impl/a_impl.cms"} "_build/install/default/lib/project/a_impl/a_impl.cmt" {"a_impl/a_impl.cmt"} "_build/install/default/lib/project/a_impl/a_impl.cmx" {"a_impl/a_impl.cmx"} "_build/install/default/lib/project/a_impl/a_impl.cmxa" {"a_impl/a_impl.cmxa"} "_build/install/default/lib/project/a_impl/a_impl.ml" {"a_impl/a_impl.ml"} "_build/install/default/lib/project/a_impl/a_impl__a_impl__.cmi" {"a_impl/a_impl__a_impl__.cmi"} + "_build/install/default/lib/project/a_impl/a_impl__a_impl__.cms" {"a_impl/a_impl__a_impl__.cms"} "_build/install/default/lib/project/a_impl/a_impl__a_impl__.cmt" {"a_impl/a_impl__a_impl__.cmt"} "_build/install/default/lib/project/a_impl/a_impl__a_impl__.cmx" {"a_impl/a_impl__a_impl__.cmx"} "_build/install/default/lib/project/a_impl/a_impl__a_impl__.ml" {"a_impl/a_impl__a_impl__.ml"} "_build/install/default/lib/project/a_of_b/a_of_b.a" {"a_of_b/a_of_b.a"} "_build/install/default/lib/project/a_of_b/a_of_b.cma" {"a_of_b/a_of_b.cma"} "_build/install/default/lib/project/a_of_b/a_of_b.cmi" {"a_of_b/a_of_b.cmi"} + "_build/install/default/lib/project/a_of_b/a_of_b.cms" {"a_of_b/a_of_b.cms"} "_build/install/default/lib/project/a_of_b/a_of_b.cmt" {"a_of_b/a_of_b.cmt"} "_build/install/default/lib/project/a_of_b/a_of_b.cmx" {"a_of_b/a_of_b.cmx"} "_build/install/default/lib/project/a_of_b/a_of_b.cmxa" {"a_of_b/a_of_b.cmxa"} "_build/install/default/lib/project/a_of_b/a_of_b.ml" {"a_of_b/a_of_b.ml"} "_build/install/default/lib/project/a_of_b/a_of_b__a_of_b__.cmi" {"a_of_b/a_of_b__a_of_b__.cmi"} + "_build/install/default/lib/project/a_of_b/a_of_b__a_of_b__.cms" {"a_of_b/a_of_b__a_of_b__.cms"} "_build/install/default/lib/project/a_of_b/a_of_b__a_of_b__.cmt" {"a_of_b/a_of_b__a_of_b__.cmt"} "_build/install/default/lib/project/a_of_b/a_of_b__a_of_b__.cmx" {"a_of_b/a_of_b__a_of_b__.cmx"} "_build/install/default/lib/project/a_of_b/a_of_b__a_of_b__.ml" {"a_of_b/a_of_b__a_of_b__.ml"} "_build/install/default/lib/project/b/b.cmi" {"b/b.cmi"} + "_build/install/default/lib/project/b/b.cmsi" {"b/b.cmsi"} "_build/install/default/lib/project/b/b.cmti" {"b/b.cmti"} "_build/install/default/lib/project/b/b.mli" {"b/b.mli"} "_build/install/default/lib/project/b_impl/b_dep.ml" {"b_impl/b_dep.ml"} "_build/install/default/lib/project/b_impl/b_impl.a" {"b_impl/b_impl.a"} "_build/install/default/lib/project/b_impl/b_impl.cma" {"b_impl/b_impl.cma"} "_build/install/default/lib/project/b_impl/b_impl.cmi" {"b_impl/b_impl.cmi"} + "_build/install/default/lib/project/b_impl/b_impl.cms" {"b_impl/b_impl.cms"} "_build/install/default/lib/project/b_impl/b_impl.cmt" {"b_impl/b_impl.cmt"} "_build/install/default/lib/project/b_impl/b_impl.cmx" {"b_impl/b_impl.cmx"} "_build/install/default/lib/project/b_impl/b_impl.cmxa" {"b_impl/b_impl.cmxa"} "_build/install/default/lib/project/b_impl/b_impl.ml" {"b_impl/b_impl.ml"} "_build/install/default/lib/project/b_impl/b_impl__B_dep.cmi" {"b_impl/b_impl__B_dep.cmi"} + "_build/install/default/lib/project/b_impl/b_impl__B_dep.cms" {"b_impl/b_impl__B_dep.cms"} "_build/install/default/lib/project/b_impl/b_impl__B_dep.cmt" {"b_impl/b_impl__B_dep.cmt"} "_build/install/default/lib/project/b_impl/b_impl__B_dep.cmx" {"b_impl/b_impl__B_dep.cmx"} "_build/install/default/lib/project/b_impl/b_impl__b_impl__.cmi" {"b_impl/b_impl__b_impl__.cmi"} + "_build/install/default/lib/project/b_impl/b_impl__b_impl__.cms" {"b_impl/b_impl__b_impl__.cms"} "_build/install/default/lib/project/b_impl/b_impl__b_impl__.cmt" {"b_impl/b_impl__b_impl__.cmt"} "_build/install/default/lib/project/b_impl/b_impl__b_impl__.cmx" {"b_impl/b_impl__b_impl__.cmx"} "_build/install/default/lib/project/b_impl/b_impl__b_impl__.ml" {"b_impl/b_impl__b_impl__.ml"} "_build/install/default/lib/project/b_impl2/b_impl2.a" {"b_impl2/b_impl2.a"} "_build/install/default/lib/project/b_impl2/b_impl2.cma" {"b_impl2/b_impl2.cma"} "_build/install/default/lib/project/b_impl2/b_impl2.cmi" {"b_impl2/b_impl2.cmi"} + "_build/install/default/lib/project/b_impl2/b_impl2.cms" {"b_impl2/b_impl2.cms"} "_build/install/default/lib/project/b_impl2/b_impl2.cmt" {"b_impl2/b_impl2.cmt"} "_build/install/default/lib/project/b_impl2/b_impl2.cmx" {"b_impl2/b_impl2.cmx"} "_build/install/default/lib/project/b_impl2/b_impl2.cmxa" {"b_impl2/b_impl2.cmxa"} "_build/install/default/lib/project/b_impl2/b_impl2.ml" {"b_impl2/b_impl2.ml"} "_build/install/default/lib/project/b_impl2/b_impl2__b_impl2__.cmi" {"b_impl2/b_impl2__b_impl2__.cmi"} + "_build/install/default/lib/project/b_impl2/b_impl2__b_impl2__.cms" {"b_impl2/b_impl2__b_impl2__.cms"} "_build/install/default/lib/project/b_impl2/b_impl2__b_impl2__.cmt" {"b_impl2/b_impl2__b_impl2__.cmt"} "_build/install/default/lib/project/b_impl2/b_impl2__b_impl2__.cmx" {"b_impl2/b_impl2__b_impl2__.cmx"} "_build/install/default/lib/project/b_impl2/b_impl2__b_impl2__.ml" {"b_impl2/b_impl2__b_impl2__.ml"} @@ -778,6 +807,7 @@ And all the required files should be installed: "_build/install/default/lib/project/lib_ab/lib_ab.a" {"lib_ab/lib_ab.a"} "_build/install/default/lib/project/lib_ab/lib_ab.cma" {"lib_ab/lib_ab.cma"} "_build/install/default/lib/project/lib_ab/lib_ab.cmi" {"lib_ab/lib_ab.cmi"} + "_build/install/default/lib/project/lib_ab/lib_ab.cms" {"lib_ab/lib_ab.cms"} "_build/install/default/lib/project/lib_ab/lib_ab.cmt" {"lib_ab/lib_ab.cmt"} "_build/install/default/lib/project/lib_ab/lib_ab.cmx" {"lib_ab/lib_ab.cmx"} "_build/install/default/lib/project/lib_ab/lib_ab.cmxa" {"lib_ab/lib_ab.cmxa"} @@ -786,24 +816,30 @@ And all the required files should be installed: "_build/install/default/lib/project/lib_apply/lib_apply.a" {"lib_apply/lib_apply.a"} "_build/install/default/lib/project/lib_apply/lib_apply.cma" {"lib_apply/lib_apply.cma"} "_build/install/default/lib/project/lib_apply/lib_apply.cmi" {"lib_apply/lib_apply.cmi"} + "_build/install/default/lib/project/lib_apply/lib_apply.cms" {"lib_apply/lib_apply.cms"} "_build/install/default/lib/project/lib_apply/lib_apply.cmt" {"lib_apply/lib_apply.cmt"} "_build/install/default/lib/project/lib_apply/lib_apply.cmx" {"lib_apply/lib_apply.cmx"} "_build/install/default/lib/project/lib_apply/lib_apply.cmxa" {"lib_apply/lib_apply.cmxa"} "_build/install/default/lib/project/lib_apply/lib_apply.ml" {"lib_apply/lib_apply.ml"} "_build/install/default/lib/project/lib_apply/lib_apply__.cmi" {"lib_apply/lib_apply__.cmi"} + "_build/install/default/lib/project/lib_apply/lib_apply__.cms" {"lib_apply/lib_apply__.cms"} "_build/install/default/lib/project/lib_apply/lib_apply__.cmt" {"lib_apply/lib_apply__.cmt"} "_build/install/default/lib/project/lib_apply/lib_apply__.cmx" {"lib_apply/lib_apply__.cmx"} "_build/install/default/lib/project/lib_apply/lib_apply__.ml" {"lib_apply/lib_apply__.ml"} "_build/install/default/lib/project/lib_apply/lib_apply__F.cmi" {"lib_apply/lib_apply__F.cmi"} + "_build/install/default/lib/project/lib_apply/lib_apply__F.cms" {"lib_apply/lib_apply__F.cms"} "_build/install/default/lib/project/lib_apply/lib_apply__F.cmt" {"lib_apply/lib_apply__F.cmt"} "_build/install/default/lib/project/lib_apply/lib_apply__F.cmx" {"lib_apply/lib_apply__F.cmx"} "_build/install/default/lib/project/lib_apply/lib_apply__X.cmi" {"lib_apply/lib_apply__X.cmi"} + "_build/install/default/lib/project/lib_apply/lib_apply__X.cms" {"lib_apply/lib_apply__X.cms"} "_build/install/default/lib/project/lib_apply/lib_apply__X.cmt" {"lib_apply/lib_apply__X.cmt"} "_build/install/default/lib/project/lib_apply/lib_apply__X.cmx" {"lib_apply/lib_apply__X.cmx"} "_build/install/default/lib/project/lib_apply/lib_apply__Y.cmi" {"lib_apply/lib_apply__Y.cmi"} + "_build/install/default/lib/project/lib_apply/lib_apply__Y.cms" {"lib_apply/lib_apply__Y.cms"} "_build/install/default/lib/project/lib_apply/lib_apply__Y.cmt" {"lib_apply/lib_apply__Y.cmt"} "_build/install/default/lib/project/lib_apply/lib_apply__Y.cmx" {"lib_apply/lib_apply__Y.cmx"} "_build/install/default/lib/project/lib_apply/lib_apply__Z.cmi" {"lib_apply/lib_apply__Z.cmi"} + "_build/install/default/lib/project/lib_apply/lib_apply__Z.cms" {"lib_apply/lib_apply__Z.cms"} "_build/install/default/lib/project/lib_apply/lib_apply__Z.cmt" {"lib_apply/lib_apply__Z.cmt"} "_build/install/default/lib/project/lib_apply/lib_apply__Z.cmx" {"lib_apply/lib_apply__Z.cmx"} "_build/install/default/lib/project/lib_apply/x.ml" {"lib_apply/x.ml"} diff --git a/test/blackbox-tests/test-cases/oxcaml/library-field-parameters.t b/test/blackbox-tests/test-cases/oxcaml/library-field-parameters.t index 18a296c193e..105e0bbd9c0 100644 --- a/test/blackbox-tests/test-cases/oxcaml/library-field-parameters.t +++ b/test/blackbox-tests/test-cases/oxcaml/library-field-parameters.t @@ -210,30 +210,37 @@ modules: (lib (META a/a.cmi + a/a.cmsi a/a.cmti a/a.mli b/b.cmi + b/b.cmsi b/b.cmti b/b.mli dune-package lib/lib.a lib/lib.cma lib/lib.cmi + lib/lib.cms lib/lib.cmt lib/lib.cmx lib/lib.cmxa lib/lib.ml lib/lib__.cmi + lib/lib__.cms lib/lib__.cmt lib/lib__.cmx lib/lib__.ml lib/lib__Lib_util.cmi + lib/lib__Lib_util.cms lib/lib__Lib_util.cmt lib/lib__Lib_util.cmx lib/lib_util.ml utils/utils.a utils/utils.cma utils/utils.cmi + utils/utils.cms + utils/utils.cmsi utils/utils.cmt utils/utils.cmti utils/utils.cmx diff --git a/test/blackbox-tests/test-cases/oxcaml/library_parameter.t b/test/blackbox-tests/test-cases/oxcaml/library_parameter.t index 7cf3ad30fb2..97c5f3df7d0 100644 --- a/test/blackbox-tests/test-cases/oxcaml/library_parameter.t +++ b/test/blackbox-tests/test-cases/oxcaml/library_parameter.t @@ -146,9 +146,11 @@ parameters. (META dune-package intf/param_intf.cmi + intf/param_intf.cmsi intf/param_intf.cmti intf/param_intf.mli intf2/param_intf2.cmi + intf2/param_intf2.cmsi intf2/param_intf2.cmti intf2/param_intf2.mli))) (library @@ -181,9 +183,11 @@ We verify it will install the data to the correct location. "_build/install/default/lib/param/META" "_build/install/default/lib/param/dune-package" "_build/install/default/lib/param/intf/param_intf.cmi" {"intf/param_intf.cmi"} + "_build/install/default/lib/param/intf/param_intf.cmsi" {"intf/param_intf.cmsi"} "_build/install/default/lib/param/intf/param_intf.cmti" {"intf/param_intf.cmti"} "_build/install/default/lib/param/intf/param_intf.mli" {"intf/param_intf.mli"} "_build/install/default/lib/param/intf2/param_intf2.cmi" {"intf2/param_intf2.cmi"} + "_build/install/default/lib/param/intf2/param_intf2.cmsi" {"intf2/param_intf2.cmsi"} "_build/install/default/lib/param/intf2/param_intf2.cmti" {"intf2/param_intf2.cmti"} "_build/install/default/lib/param/intf2/param_intf2.mli" {"intf2/param_intf2.mli"} ] From 32db9c56d0797e16fe113763bf9e36949f2d4bfb Mon Sep 17 00:00:00 2001 From: Simon Spies Date: Tue, 6 Jan 2026 13:40:08 +0000 Subject: [PATCH 2/2] .cms/.cmt dependency Signed-off-by: Simon Spies --- doc/changes/added/13397.md | 2 + src/dune_rules/compilation_context.ml | 19 ++- src/dune_rules/compilation_context.mli | 2 + src/dune_rules/context.ml | 5 + src/dune_rules/context.mli | 1 + src/dune_rules/module_compilation.ml | 71 ++++++++-- src/source/workspace.ml | 36 +++++ src/source/workspace.mli | 11 ++ .../test-cases/oxcaml/bin-annot-cms.t | 2 - .../test-cases/oxcaml/cms-cmt-dependency.t | 134 ++++++++++++++++++ 10 files changed, 269 insertions(+), 14 deletions(-) create mode 100644 doc/changes/added/13397.md create mode 100644 test/blackbox-tests/test-cases/oxcaml/cms-cmt-dependency.t diff --git a/doc/changes/added/13397.md b/doc/changes/added/13397.md new file mode 100644 index 00000000000..f3eb637582d --- /dev/null +++ b/doc/changes/added/13397.md @@ -0,0 +1,2 @@ +- Add support for generating `.cms` files using oxcaml and adding `.cms` or + `.cmt` files as compilation dependencies (#13397, @spiessimon) diff --git a/src/dune_rules/compilation_context.ml b/src/dune_rules/compilation_context.ml index f0e3a257467..bdb8427a8da 100644 --- a/src/dune_rules/compilation_context.ml +++ b/src/dune_rules/compilation_context.ml @@ -100,6 +100,7 @@ type t = ; modes : Lib_mode.Map.Set.t ; bin_annot : bool ; bin_annot_cms : bool + ; cms_cmt_dependency : Workspace.Context.Cms_cmt_dependency.t ; loc : Loc.t option ; ocaml : Ocaml_toolchain.t ; for_ : Compilation_mode.t @@ -128,6 +129,7 @@ let implements t = t.implements let modes t = t.modes let bin_annot t = t.bin_annot let bin_annot_cms t = t.bin_annot_cms +let cms_cmt_dependency t = t.cms_cmt_dependency let context t = Super_context.context t.super_context let dep_graphs t = t.modules.dep_graphs let ocaml t = t.ocaml @@ -163,6 +165,7 @@ let create ?modes ?bin_annot ?bin_annot_cms + ?cms_cmt_dependency ?loc ?instances for_ @@ -216,6 +219,12 @@ let create match bin_annot_cms with | Some b -> Memo.return b | None -> Env_stanza_db.bin_annot_cms ~dir:(Obj_dir.dir obj_dir) + and+ cms_cmt_dependency = + match cms_cmt_dependency with + | Some v -> Memo.return v + | None -> + let context = Super_context.context super_context in + Memo.return (Context.cms_cmt_dependency context) in { super_context ; scope @@ -238,6 +247,7 @@ let create ; modes ; bin_annot ; bin_annot_cms + ; cms_cmt_dependency ; loc ; ocaml ; instances @@ -349,7 +359,14 @@ let for_plugin_executable t ~embed_in_plugin_libraries = { t with requires_link } ;; -let without_bin_annot t = { t with bin_annot = false; bin_annot_cms = false } +let without_bin_annot t = + { t with + bin_annot = false + ; bin_annot_cms = false + ; cms_cmt_dependency = Workspace.Context.Cms_cmt_dependency.No_dependency + } +;; + let set_obj_dir t obj_dir = { t with obj_dir } let set_modes t ~modes = { t with modes } diff --git a/src/dune_rules/compilation_context.mli b/src/dune_rules/compilation_context.mli index c3ba9511f97..f5ac4cae7ca 100644 --- a/src/dune_rules/compilation_context.mli +++ b/src/dune_rules/compilation_context.mli @@ -37,6 +37,7 @@ val create -> ?modes:Lib_mode.Map.Set.t -> ?bin_annot:bool -> ?bin_annot_cms:bool + -> ?cms_cmt_dependency:Workspace.Context.Cms_cmt_dependency.t -> ?loc:Loc.t -> ?instances:Parameterised_instances.t Resolve.Memo.t -> Compilation_mode.t @@ -84,6 +85,7 @@ val for_module_generated_at_link_time val for_plugin_executable : t -> embed_in_plugin_libraries:(Loc.t * Lib_name.t) list -> t val bin_annot : t -> bool val bin_annot_cms : t -> bool +val cms_cmt_dependency : t -> Workspace.Context.Cms_cmt_dependency.t val without_bin_annot : t -> t (** The dependency graph for the modules of the library. *) diff --git a/src/dune_rules/context.ml b/src/dune_rules/context.ml index 60db15e3b1e..8eff8ea3c23 100644 --- a/src/dune_rules/context.ml +++ b/src/dune_rules/context.ml @@ -79,6 +79,7 @@ type builder = ; target_exec : (string * string list) option ; for_host : (Context_name.t * t Memo.t) option ; path : Path.t list + ; cms_cmt_dependency : Workspace.Context.Cms_cmt_dependency.t } and t = @@ -118,6 +119,7 @@ module Builder = struct ; target_exec = None ; for_host = None ; path = [] + ; cms_cmt_dependency = Workspace.Context.Cms_cmt_dependency.No_dependency } ;; @@ -154,6 +156,7 @@ module Builder = struct ; dynamically_linked_foreign_archives ; instrument_with ; merlin + ; cms_cmt_dependency } = let env = @@ -173,6 +176,7 @@ module Builder = struct ; env = Memo.return env ; findlib_toolchain = toolchain ; target_exec = None + ; cms_cmt_dependency } ;; end @@ -199,6 +203,7 @@ let fdo_target_exe t = t.builder.fdo_target_exe let instrument_with t = t.builder.instrument_with let merlin t = t.builder.merlin let profile t = t.builder.profile +let cms_cmt_dependency t = t.builder.cms_cmt_dependency let equal x y = Context_name.equal x.builder.name y.builder.name let hash t = Context_name.hash t.builder.name let build_context t = t.build_context diff --git a/src/dune_rules/context.mli b/src/dune_rules/context.mli index a7806af76fd..762cc87e2ee 100644 --- a/src/dune_rules/context.mli +++ b/src/dune_rules/context.mli @@ -51,6 +51,7 @@ val findlib_toolchain : t -> Context_name.t option val instrument_with : t -> Lib_name.t list val profile : t -> Profile.t val merlin : t -> bool +val cms_cmt_dependency : t -> Workspace.Context.Cms_cmt_dependency.t val equal : t -> t -> bool val hash : t -> int val to_dyn : t -> Dyn.t diff --git a/src/dune_rules/module_compilation.ml b/src/dune_rules/module_compilation.ml index bfacd15c137..3d9788ccc39 100644 --- a/src/dune_rules/module_compilation.ml +++ b/src/dune_rules/module_compilation.ml @@ -30,19 +30,54 @@ let opens modules m = Command.Args.As (Modules.With_vlib.local_open modules m |> Ocaml_flags.open_flags) ;; -let other_cm_files ~opaque ~cm_kind ~obj_dir = +let other_cm_files + ~opaque + ~cm_kind + ~obj_dir + ~cms_cmt_dependency + ~bin_annot + ~bin_annot_cms + ~is_ox + = List.concat_map ~f:(fun m -> let cmi_kind = Lib_mode.Cm_kind.cmi cm_kind in let deps = [ Path.build (Obj_dir.Module.cm_file_exn obj_dir m ~kind:cmi_kind) ] in - if Module.has m ~ml_kind:Impl && cm_kind = Ocaml Cmx && not opaque - then ( - let cmx = Obj_dir.Module.cm_file_exn obj_dir m ~kind:(Ocaml Cmx) in - Path.build cmx :: deps) - else if Module.has m ~ml_kind:Impl && cm_kind = Melange Cmj - then ( - let cmj = Obj_dir.Module.cm_file_exn obj_dir m ~kind:(Melange Cmj) in - Path.build cmj :: deps) - else deps) + let deps = + if Module.has m ~ml_kind:Impl && cm_kind = Ocaml Cmx && not opaque + then ( + let cmx = Obj_dir.Module.cm_file_exn obj_dir m ~kind:(Ocaml Cmx) in + Path.build cmx :: deps) + else if Module.has m ~ml_kind:Impl && cm_kind = Melange Cmj + then ( + let cmj = Obj_dir.Module.cm_file_exn obj_dir m ~kind:(Melange Cmj) in + Path.build cmj :: deps) + else deps + in + (* Add .cms/.cmt dependencies when enabled. Like .cmx dependencies, these are + skipped when -opaque is used. *) + let cms_cmt_deps = + let open Workspace.Context.Cms_cmt_dependency in + match cms_cmt_dependency with + | No_dependency -> [] + | Depends_on_cms when bin_annot_cms && is_ox && not opaque -> + (* We pass as [cm_kind] [Ocaml Cmx/Cmi] but the specific [cm_kind] + doesn't matter here: .cms/.cmt files are stored in byte_dir + regardless (see [Obj_dir.Module.cms_file]). *) + List.filter_opt + [ Obj_dir.Module.cms_file obj_dir m ~ml_kind:Impl ~cm_kind:(Ocaml Cmx) + ; Obj_dir.Module.cms_file obj_dir m ~ml_kind:Intf ~cm_kind:(Ocaml Cmi) + ] + |> List.map ~f:Path.build + | Depends_on_cms -> [] + | Depends_on_cmt when bin_annot && is_ox && not opaque -> + List.filter_opt + [ Obj_dir.Module.cmt_file obj_dir m ~ml_kind:Impl ~cm_kind:(Ocaml Cmx) + ; Obj_dir.Module.cmt_file obj_dir m ~ml_kind:Intf ~cm_kind:(Ocaml Cmi) + ] + |> List.map ~f:Path.build + | Depends_on_cmt -> [] + in + cms_cmt_deps @ deps) ;; let copy_interface ~sctx ~dir ~obj_dir ~cm_kind m = @@ -230,8 +265,22 @@ let build_cm let other_cm_files = let dep_graph = Ml_kind.Dict.get (Compilation_context.dep_graphs cctx) ml_kind in let module_deps = Dep_graph.deps_of dep_graph m in + let cms_cmt_dependency = Compilation_context.cms_cmt_dependency cctx in + let bin_annot = Compilation_context.bin_annot cctx in + let bin_annot_cms = Compilation_context.bin_annot_cms cctx in + let is_ox = Ocaml_config.ox ocaml.ocaml_config in Action_builder.dyn_paths_unit - (Action_builder.map module_deps ~f:(other_cm_files ~opaque ~cm_kind ~obj_dir)) + (Action_builder.map + module_deps + ~f: + (other_cm_files + ~opaque + ~cm_kind + ~obj_dir + ~cms_cmt_dependency + ~bin_annot + ~bin_annot_cms + ~is_ox)) in let cmt_args = match cm_kind with diff --git a/src/source/workspace.ml b/src/source/workspace.ml index ce6ad196247..609dfd2326c 100644 --- a/src/source/workspace.ml +++ b/src/source/workspace.ml @@ -382,6 +382,31 @@ module Context = struct ;; end + module Cms_cmt_dependency = struct + type t = + | No_dependency + | Depends_on_cms + | Depends_on_cmt + + let equal x y = + match x, y with + | No_dependency, No_dependency + | Depends_on_cms, Depends_on_cms + | Depends_on_cmt, Depends_on_cmt -> true + | (No_dependency | Depends_on_cms | Depends_on_cmt), _ -> false + ;; + + let to_dyn : t -> Dyn.t = function + | No_dependency -> String "none" + | Depends_on_cms -> String "cms" + | Depends_on_cmt -> String "cmt" + ;; + + let decode = + enum [ "none", No_dependency; "cms", Depends_on_cms; "cmt", Depends_on_cmt ] + ;; + end + module Common = struct type t = { loc : Loc.t @@ -396,6 +421,7 @@ module Context = struct ; dynamically_linked_foreign_archives : bool ; instrument_with : Lib_name.t list ; merlin : Merlin.t + ; cms_cmt_dependency : Cms_cmt_dependency.t } let to_dyn { name; targets; host_context; _ } = @@ -419,6 +445,7 @@ module Context = struct ; dynamically_linked_foreign_archives ; instrument_with ; merlin + ; cms_cmt_dependency } t = @@ -435,6 +462,7 @@ module Context = struct t.dynamically_linked_foreign_archives && List.equal Lib_name.equal instrument_with t.instrument_with && Merlin.equal merlin t.merlin + && Cms_cmt_dependency.equal cms_cmt_dependency t.cms_cmt_dependency ;; let fdo_suffix t = @@ -505,6 +533,12 @@ module Context = struct field_b ~check:(Dune_lang.Syntax.since Stanza.syntax (3, 16)) "generate_merlin_rules" + and+ cms_cmt_dependency = + field + "cms_cmt_dependency" + ~default:Cms_cmt_dependency.No_dependency + (Dune_lang.Syntax.since Dune_lang.Oxcaml.syntax (0, 1) + >>> Cms_cmt_dependency.decode) in fun ~profile_default ~instrument_with_default -> let profile = Option.value profile ~default:profile_default in @@ -537,6 +571,7 @@ module Context = struct (match generate_merlin_rules with | true -> Rules_only | false -> Not_selected)) + ; cms_cmt_dependency } ;; end @@ -708,6 +743,7 @@ module Context = struct ; dynamically_linked_foreign_archives = true ; instrument_with = Option.value instrument_with ~default:[] ; merlin = Not_selected + ; cms_cmt_dependency = Cms_cmt_dependency.No_dependency } } ;; diff --git a/src/source/workspace.mli b/src/source/workspace.mli index ace8552d22f..fa4b5914ddc 100644 --- a/src/source/workspace.mli +++ b/src/source/workspace.mli @@ -60,6 +60,16 @@ module Context : sig val to_dyn : t -> Dyn.t end + module Cms_cmt_dependency : sig + type t = + | No_dependency + | Depends_on_cms + | Depends_on_cmt + + val equal : t -> t -> bool + val to_dyn : t -> Dyn.t + end + module Common : sig type t = { loc : Loc.t @@ -80,6 +90,7 @@ module Context : sig ; dynamically_linked_foreign_archives : bool ; instrument_with : Lib_name.t list ; merlin : Merlin.t + ; cms_cmt_dependency : Cms_cmt_dependency.t } end diff --git a/test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t b/test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t index 639f8bf5273..f2689f92e79 100644 --- a/test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t +++ b/test/blackbox-tests/test-cases/oxcaml/bin-annot-cms.t @@ -14,8 +14,6 @@ Key differences between env stanza and flags field: - env stanza only generates cms/cmsi during byte/cmi compilation (like cmt/cmti), | whereas flags field passes the flag to ALL compilations including native - $ . ./helpers.sh - Create a project with OxCaml language extension (required for bin_annot_cms): $ cat >dune-project <dune-project < (lang dune 3.21) + > (using oxcaml 0.1) + > EOF + +Create a library with two modules where one depends on the other: + + $ mkdir -p mylib + + $ cat >mylib/dep.ml < type t = int + > let make () = 42 + > EOF + + $ cat >mylib/dep.mli < type t + > val make : unit -> t + > EOF + + $ cat >mylib/user.ml < let x = Dep.make () + > let () = ignore x + > EOF + + $ cat >mylib/dune < (library (name mylib)) + > EOF + +Test 1: cms_cmt_dependency = none (default) +=========================================== + +With the default setting, neither .cms nor .cmt files should appear as +dependencies of compilation rules. + + $ cat >dune-workspace < (lang dune 3.21) + > (using oxcaml 0.1) + > (context (default + > (name default) + > (profile release) + > (cms_cmt_dependency none))) + > EOF + +Build everything first so rules are generated: + + $ dune build + +Check that User.cmx does NOT depend on Dep's .cms/.cmsi files: + + $ dune rules mylib/.mylib.objs/native/mylib__User.cmx | grep -c 'mylib__Dep\.cms' + 0 + [1] + +Check that User.cmx does NOT depend on Dep's .cmt/.cmti files: + + $ dune rules mylib/.mylib.objs/native/mylib__User.cmx | grep -c 'mylib__Dep\.cmt' + 0 + [1] + +Test 2: cms_cmt_dependency = cms +================================ + +With this setting enabled, .cms files SHOULD appear as dependencies of +compilation rules, but .cmt files should NOT. + + $ dune clean + + $ cat >dune-workspace < (lang dune 3.21) + > (using oxcaml 0.1) + > (context (default + > (name default) + > (profile release) + > (cms_cmt_dependency cms))) + > EOF + +Build everything: + + $ dune build + +Check that User.cmx DOES depend on Dep's .cms/.cmsi files: + + $ dune rules mylib/.mylib.objs/native/mylib__User.cmx | grep -c 'mylib__Dep\.cms' + 2 + +Check that User.cmx does NOT depend on Dep's .cmt/.cmti files: + + $ dune rules mylib/.mylib.objs/native/mylib__User.cmx | grep -c 'mylib__Dep\.cmt' + 0 + [1] + +Test 3: cms_cmt_dependency = cmt +================================ + +With this setting enabled, .cmt files SHOULD appear as dependencies of +compilation rules, but .cms files should NOT. + $ dune clean + + $ cat >dune-workspace < (lang dune 3.21) + > (using oxcaml 0.1) + > (context (default + > (name default) + > (profile release) + > (cms_cmt_dependency cmt))) + > EOF + +Build everything: + + $ dune build + +Check that User.cmx DOES depend on Dep's .cmt/.cmti files: + + $ dune rules mylib/.mylib.objs/native/mylib__User.cmx | grep -c 'mylib__Dep\.cmt' + 2 + +Check that User.cmx does NOT depend on Dep's .cms/.cmsi files: + + $ dune rules mylib/.mylib.objs/native/mylib__User.cmx | grep -c 'mylib__Dep\.cms' + 0 + [1]