diff --git a/src/dune_rules/dune_env.ml b/src/dune_rules/dune_env.ml index b2efc01a5662..73e9f0b8cb5f 100644 --- a/src/dune_rules/dune_env.ml +++ b/src/dune_rules/dune_env.ml @@ -15,13 +15,6 @@ module Stanza = struct and+ cxx = Ordered_set_lang.Unexpanded.field "cxx_flags" ?check in Foreign_language.Dict.make ~c ~cxx - let link_flags ~since = - let check = - Option.map since ~f:(fun since -> - Dune_lang.Syntax.since Stanza.syntax since) - in - Ordered_set_lang.Unexpanded.field "link_flags" ?check - let menhir_flags ~since = let check = Option.map since ~f:(fun since -> @@ -81,7 +74,7 @@ module Stanza = struct type config = { flags : Ocaml_flags.Spec.t ; foreign_flags : Ordered_set_lang.Unexpanded.t Foreign_language.Dict.t - ; link_flags : Ordered_set_lang.Unexpanded.t + ; link_flags : Link_flags.Spec.t ; env_vars : Env.t ; binaries : File_binding.Unexpanded.t list ; inline_tests : Inline_tests.t option @@ -108,7 +101,7 @@ module Stanza = struct Ocaml_flags.Spec.equal flags t.flags && Foreign_language.Dict.equal Ordered_set_lang.Unexpanded.equal foreign_flags t.foreign_flags - && Ordered_set_lang.Unexpanded.equal link_flags t.link_flags + && Link_flags.Spec.equal link_flags t.link_flags && Env.equal env_vars t.env_vars && List.equal File_binding.Unexpanded.equal binaries t.binaries && Option.equal Inline_tests.equal inline_tests t.inline_tests @@ -124,7 +117,7 @@ module Stanza = struct { flags = Ocaml_flags.Spec.standard ; foreign_flags = Foreign_language.Dict.make_both Ordered_set_lang.Unexpanded.standard - ; link_flags = Ordered_set_lang.Unexpanded.standard + ; link_flags = Link_flags.Spec.standard ; env_vars = Env.empty ; binaries = [] ; inline_tests = None @@ -192,7 +185,7 @@ module Stanza = struct let config = let+ flags = Ocaml_flags.Spec.decode and+ foreign_flags = foreign_flags ~since:(Some (1, 7)) - and+ link_flags = link_flags ~since:(Some (3, 0)) + and+ link_flags = Link_flags.Spec.decode ~since:(Some (3, 0)) and+ env_vars = env_vars_field and+ binaries = field ~default:[] "binaries" diff --git a/src/dune_rules/dune_env.mli b/src/dune_rules/dune_env.mli index 6b111329dfde..0f059ca9f8d1 100644 --- a/src/dune_rules/dune_env.mli +++ b/src/dune_rules/dune_env.mli @@ -28,7 +28,7 @@ module Stanza : sig type config = { flags : Ocaml_flags.Spec.t ; foreign_flags : Ordered_set_lang.Unexpanded.t Foreign_language.Dict.t - ; link_flags : Ordered_set_lang.Unexpanded.t + ; link_flags : Link_flags.Spec.t ; env_vars : Env.t ; binaries : File_binding.Unexpanded.t list ; inline_tests : Inline_tests.t option diff --git a/src/dune_rules/dune_file.ml b/src/dune_rules/dune_file.ml index f0d636275a75..b53a71e2a98b 100644 --- a/src/dune_rules/dune_file.ml +++ b/src/dune_rules/dune_file.ml @@ -1381,7 +1381,7 @@ module Executables = struct type t = { names : (Loc.t * string) list - ; link_flags : Ordered_set_lang.Unexpanded.t + ; link_flags : Link_flags.Spec.t ; link_deps : Dep_conf.t list ; modes : Loc.t Link_mode.Map.t ; optional : bool @@ -1411,7 +1411,7 @@ module Executables = struct field "link_executables" ~default:true (Dune_lang.Syntax.deleted_in Stanza.syntax (1, 0) >>> bool) and+ link_deps = field "link_deps" (repeat Dep_conf.decode) ~default:[] - and+ link_flags = Ordered_set_lang.Unexpanded.field "link_flags" + and+ link_flags = Link_flags.Spec.decode ~since:None and+ modes = field "modes" Link_mode.Map.decode ~default:(Link_mode.Map.default_for_exes ~version:dune_version) @@ -1941,7 +1941,7 @@ module Tests = struct String_with_vars.add_user_vars_to_decoding_env (Bindings.var_names deps) (let* dune_version = Dune_lang.Syntax.get_exn Stanza.syntax in let+ buildable = Buildable.decode Executable - and+ link_flags = Ordered_set_lang.Unexpanded.field "link_flags" + and+ link_flags = Link_flags.Spec.decode ~since:None and+ names = names and+ package = field_o "package" Stanza_common.Pkg.decode and+ locks = diff --git a/src/dune_rules/dune_file.mli b/src/dune_rules/dune_file.mli index 5e7d23630e31..a31bd2f2766c 100644 --- a/src/dune_rules/dune_file.mli +++ b/src/dune_rules/dune_file.mli @@ -250,7 +250,7 @@ module Executables : sig type t = { names : (Loc.t * string) list - ; link_flags : Ordered_set_lang.Unexpanded.t + ; link_flags : Link_flags.Spec.t ; link_deps : Dep_conf.t list ; modes : Loc.t Link_mode.Map.t ; optional : bool diff --git a/src/dune_rules/env_node.ml b/src/dune_rules/env_node.ml index 15807f2513cc..faf782de4817 100644 --- a/src/dune_rules/env_node.ml +++ b/src/dune_rules/env_node.ml @@ -18,7 +18,7 @@ type t = ; local_binaries : File_binding.Expanded.t list Memo.Lazy.t ; ocaml_flags : Ocaml_flags.t Memo.Lazy.t ; foreign_flags : string list Action_builder.t Foreign_language.Dict.t - ; link_flags : string list Action_builder.t Memo.Lazy.t + ; link_flags : Link_flags.t Memo.Lazy.t ; external_env : Env.t Memo.Lazy.t ; bin_artifacts : Artifacts.Bin.t Memo.Lazy.t ; inline_tests : Dune_env.Stanza.Inline_tests.t Memo.Lazy.t @@ -37,8 +37,7 @@ let ocaml_flags t = Memo.Lazy.force t.ocaml_flags let foreign_flags t = t.foreign_flags -let link_flags t = - Memo.Lazy.force t.link_flags |> Action_builder.memo_build_join +let link_flags t = Memo.Lazy.force t.link_flags let external_env t = Memo.Lazy.force t.external_env @@ -62,7 +61,7 @@ let coq t = Memo.Lazy.force t.coq let make ~dir ~inherit_from ~scope ~config_stanza ~profile ~expander ~expander_for_artifacts ~default_context_flags ~default_env - ~default_bin_artifacts = + ~default_bin_artifacts ~default_cxx_link_flags = let open Memo.Build.O in let config = Dune_env.Stanza.find config_stanza ~profile in let inherited ~field ~root extend = @@ -172,13 +171,12 @@ let make ~dir ~inherit_from ~scope ~config_stanza ~profile ~expander Foreign_language.Dict.make ~c:(foreign_flags C) ~cxx:(foreign_flags Cxx) in let link_flags = - inherited - ~field:(fun t -> Memo.Build.return (link_flags t)) - ~root:(Action_builder.return []) - (fun flags -> + let default_link_flags = Link_flags.default ~default_cxx_link_flags in + inherited ~field:link_flags ~root:default_link_flags (fun link_flags -> let+ expander = Memo.Lazy.force expander in let expander = Expander.set_dir expander ~dir in - Expander.expand_and_eval_set expander config.link_flags ~standard:flags) + Link_flags.make ~spec:config.link_flags ~default:link_flags + ~eval:(Expander.expand_and_eval_set expander)) in let menhir_flags = inherited diff --git a/src/dune_rules/env_node.mli b/src/dune_rules/env_node.mli index 0fa2827c61b7..664640ea2de4 100644 --- a/src/dune_rules/env_node.mli +++ b/src/dune_rules/env_node.mli @@ -28,6 +28,7 @@ val make : -> default_context_flags:string list Action_builder.t Foreign_language.Dict.t -> default_env:Env.t -> default_bin_artifacts:Artifacts.Bin.t + -> default_cxx_link_flags:string list Action_builder.t -> t val scope : t -> Scope.t @@ -43,7 +44,7 @@ val js_of_ocaml : val foreign_flags : t -> string list Action_builder.t Foreign_language.Dict.t -val link_flags : t -> string list Action_builder.t +val link_flags : t -> Link_flags.t Memo.Build.t val local_binaries : t -> File_binding.Expanded.t list Memo.Build.t diff --git a/src/dune_rules/exe_rules.ml b/src/dune_rules/exe_rules.ml index fec68be271d2..bf244d8bc09d 100644 --- a/src/dune_rules/exe_rules.ml +++ b/src/dune_rules/exe_rules.ml @@ -180,8 +180,12 @@ let executables_rules ~sctx ~dir ~expander ~dir_contents ~scope ~compile_info in let open Action_builder.O in let link_flags = - link_deps - >>> Super_context.link_flags sctx ~dir ~expander ~use_standard_cxx_flags ~flags:exes.link_flags + let* () = link_deps in + let* link_flags = + Action_builder.memo_build + (Super_context.link_flags sctx ~dir exes.link_flags) + in + Link_flags.get ~use_standard_cxx_flags link_flags in let+ flags = link_flags and+ ctypes_cclib_flags = diff --git a/src/dune_rules/link_flags.ml b/src/dune_rules/link_flags.ml new file mode 100644 index 000000000000..eb57fe1bc9e0 --- /dev/null +++ b/src/dune_rules/link_flags.ml @@ -0,0 +1,61 @@ +open! Dune_engine +open! Stdune +open Import +open Action_builder.O + +(* flags are duplicated because we want to have two sets of default (:standard) + flags. [link_flags_cxx] will be used for executable with foreign_cxx when + [use_standard_cxx_flags] is true *) +type 'a t' = + { link_flags : 'a + ; link_flags_cxx : 'a + } + +module Spec = struct + type t = Ordered_set_lang.Unexpanded.t t' + + let standard = + let standard = Ordered_set_lang.Unexpanded.standard in + { link_flags = standard; link_flags_cxx = standard } + + let decode ~since = + let open Dune_lang.Decoder in + let check = + Option.map since ~f:(fun since -> + Dune_lang.Syntax.since Stanza.syntax since) + in + let+ flags = Ordered_set_lang.Unexpanded.field "link_flags" ?check in + { link_flags = flags; link_flags_cxx = flags } + + let equal { link_flags; link_flags_cxx } t = + Ordered_set_lang.Unexpanded.equal link_flags t.link_flags + && Ordered_set_lang.Unexpanded.equal link_flags_cxx t.link_flags_cxx +end + +type t = string list Action_builder.t t' + +let default ~default_cxx_link_flags = + let link_flags_cxx = + let+ flags = default_cxx_link_flags in + List.concat_map flags ~f:(fun f -> [ "-cclib"; f ]) + in + { link_flags = Action_builder.return []; link_flags_cxx } + +let make ~spec ~default ~eval = + let f name x standard = Action_builder.memoize name (eval x ~standard) in + { link_flags = f "link flags" spec.link_flags default.link_flags + ; link_flags_cxx = + f "link flags cxx" spec.link_flags_cxx default.link_flags_cxx + } + +let get ~use_standard_cxx_flags (t : t) = + if use_standard_cxx_flags then + t.link_flags_cxx + else + t.link_flags + +let dump t = + let+ link_flags = t.link_flags in + List.map + ~f:Dune_lang.Encoder.(pair string (list string)) + [ ("link_flags", link_flags) ] diff --git a/src/dune_rules/link_flags.mli b/src/dune_rules/link_flags.mli new file mode 100644 index 000000000000..6f5c7b6b292a --- /dev/null +++ b/src/dune_rules/link_flags.mli @@ -0,0 +1,32 @@ +(** OCaml flags *) +open! Dune_engine + +open! Stdune + +type t + +module Spec : sig + type t + + val equal : t -> t -> bool + + val decode : + since:Dune_lang.Syntax.Version.t option -> t Dune_lang.Decoder.fields_parser + + val standard : t +end + +val make : + spec:Spec.t + -> default:t + -> eval: + ( Ordered_set_lang.Unexpanded.t + -> standard:string list Action_builder.t + -> string list Action_builder.t) + -> t + +val default : default_cxx_link_flags:string list Action_builder.t -> t + +val get : use_standard_cxx_flags:bool -> t -> string list Action_builder.t + +val dump : t -> Dune_lang.t list Action_builder.t diff --git a/src/dune_rules/super_context.ml b/src/dune_rules/super_context.ml index 2021199100b2..94abcca7b47c 100644 --- a/src/dune_rules/super_context.ml +++ b/src/dune_rules/super_context.ml @@ -145,10 +145,11 @@ end = struct in extend_expander t ~dir ~expander_for_artifacts) in + let default_cxx_link_flags = Cxx_flags.get_flags ~for_:Link t.context in Env_node.make ~dir ~scope ~config_stanza ~inherit_from:(Some inherit_from) ~profile:t.context.profile ~expander ~expander_for_artifacts ~default_context_flags ~default_env:t.context_env - ~default_bin_artifacts:t.bin_artifacts + ~default_bin_artifacts:t.bin_artifacts ~default_cxx_link_flags (* Here we jump through some hoops to construct [t] as well as create a memoization table that has access to [t] and is used in [t.get_node]. @@ -402,22 +403,11 @@ let foreign_flags t ~dir ~expander ~flags ~language = in Action_builder.memoize (sprintf "%s flags" name) flags -let link_flags t ~dir ~expander ~use_standard_cxx_flags ~flags = - let env_tree = t.env_tree in - let default = - get_node env_tree ~dir >>| Env_node.link_flags |> Action_builder.memo_build_join - in - let default = - if use_standard_cxx_flags - then - let open Action_builder.O in - let+ default = default - and+ flags = Cxx_flags.get_flags ~for_:Link (context t) in - default @ List.concat_map flags ~f:(fun f -> [ "-cclib"; f ]) - else default - in - Action_builder.memoize "link flags" - (Expander.expand_and_eval_set expander flags ~standard:default) +let link_flags t ~dir (spec : Link_flags.Spec.t) = + let* expander = Env_tree.expander t.env_tree ~dir in + let+ link_flags = get_node t.env_tree ~dir >>= Env_node.link_flags in + Link_flags.make ~spec ~default:link_flags + ~eval:(Expander.expand_and_eval_set expander) let menhir_flags t ~dir ~expander ~flags = let t = t.env_tree in @@ -439,7 +429,7 @@ let dump_env t ~dir = let t = t.env_tree in let ocaml_flags = get_node t ~dir >>= Env_node.ocaml_flags in let foreign_flags = get_node t ~dir >>| Env_node.foreign_flags in - let link_flags = get_node t ~dir >>| Env_node.link_flags in + let link_flags = get_node t ~dir >>= Env_node.link_flags in let menhir_flags = get_node t ~dir >>| Env_node.menhir_flags in let coq_flags = get_node t ~dir >>= Env_node.coq in let js_of_ocaml = get_node t ~dir >>= Env_node.js_of_ocaml in @@ -455,9 +445,8 @@ let dump_env t ~dir = ~f:Dune_lang.Encoder.(pair string (list string)) [ ("c_flags", c_flags); ("cxx_flags", cxx_flags) ] and+ link_flags_dump = - let+ flags = Action_builder.memo_build_join link_flags in - [ ("link_flags", flags) ] - |> List.map ~f:Dune_lang.Encoder.(pair string (list string)) + let* link_flags = Action_builder.memo_build link_flags in + Link_flags.dump link_flags and+ menhir_dump = let+ flags = Action_builder.memo_build_join menhir_flags in [ ("menhir_flags", flags) ] @@ -723,11 +712,12 @@ let create ~(context : Context.t) ~host ~projects ~packages ~stanzas = Code_error.raise "[expander_for_artifacts] in [default_env] is undefined" []) in + let default_cxx_link_flags = Cxx_flags.get_flags ~for_:Link context in let expander = Memo.Lazy.of_val root_expander in Env_node.make ~dir ~scope ~inherit_from ~config_stanza ~profile:context.profile ~expander ~expander_for_artifacts ~default_context_flags ~default_env:context_env - ~default_bin_artifacts:artifacts.bin + ~default_bin_artifacts:artifacts.bin ~default_cxx_link_flags in Memo.Build.return (make ~config_stanza:context.env_nodes.context diff --git a/src/dune_rules/super_context.mli b/src/dune_rules/super_context.mli index 148ce72c7563..2c638e9a8bea 100644 --- a/src/dune_rules/super_context.mli +++ b/src/dune_rules/super_context.mli @@ -87,12 +87,7 @@ val foreign_flags : -> string list Action_builder.t val link_flags : - t - -> dir:Path.Build.t - -> expander:Expander.t - -> use_standard_cxx_flags:bool - -> flags:Ordered_set_lang.Unexpanded.t - -> string list Action_builder.t + t -> dir:Path.Build.t -> Link_flags.Spec.t -> Link_flags.t Memo.Build.t val menhir_flags : t diff --git a/test/blackbox-tests/test-cases/cxx-flags.t/dune b/test/blackbox-tests/test-cases/cxx-flags.t/dune index 6f4cc7ca9042..7804b95f2706 100644 --- a/test/blackbox-tests/test-cases/cxx-flags.t/dune +++ b/test/blackbox-tests/test-cases/cxx-flags.t/dune @@ -7,4 +7,4 @@ (name main) (libraries quad) (foreign_stubs (language cxx) (names bazexe)) - (modules main)) + (modules main)) \ No newline at end of file diff --git a/test/blackbox-tests/test-cases/cxx-flags.t/run.t b/test/blackbox-tests/test-cases/cxx-flags.t/run.t index a1464f6d292c..49b8e0512b97 100644 --- a/test/blackbox-tests/test-cases/cxx-flags.t/run.t +++ b/test/blackbox-tests/test-cases/cxx-flags.t/run.t @@ -106,8 +106,33 @@ With use_standard_c_and_cxx_flags = true > grep -ce "Main.cmx$GCC_LF_LIB)\|Main.cmx$Clang_LF_LIB)\|Main.cmx$Msvc_LF_LIB)" 1 + $ dune clean + $ dune exec ./main.exe 2046 4096 Hello World Baz! Hello World Bazexe! + + $ [ -f _build/default/.dune/ccomp/ccomp ] + + +ccomp is not computed if not required +===================================== + $ dune clean + + $ dune exec ./sub/main_no_stubs.exe + OK + + $ [ -f _build/default/.dune/ccomp/ccomp ] + [1] + + +one can extend link flags in env +================================ + + $ OTHER=" --other-flag --yet-some-other-flag)" + + $ dune rules sub/main.exe --profile some-profile | tr -s '\n' ' ' | + > grep -ce "Main.cmx$GCC_LF_LIB$OTHER\|Main.cmx$Clang_LF_LIB$OTHER\|Main.cmx$Msvc_LF_LIB$OTHER" + 1 diff --git a/test/blackbox-tests/test-cases/cxx-flags.t/sub/bazexe.cpp b/test/blackbox-tests/test-cases/cxx-flags.t/sub/bazexe.cpp new file mode 100644 index 000000000000..c44efd5cc3e4 --- /dev/null +++ b/test/blackbox-tests/test-cases/cxx-flags.t/sub/bazexe.cpp @@ -0,0 +1,11 @@ +#include +#include + +extern "C" value bazexe(value unit) { return Val_int(4096); } + +extern "C" void hello_world_bazexe (); + +void hello_world_bazexe () +{ + std::cout << "Hello World Bazexe!"; +} diff --git a/test/blackbox-tests/test-cases/cxx-flags.t/sub/dune b/test/blackbox-tests/test-cases/cxx-flags.t/sub/dune new file mode 100644 index 000000000000..1834f1b88ce5 --- /dev/null +++ b/test/blackbox-tests/test-cases/cxx-flags.t/sub/dune @@ -0,0 +1,12 @@ + +(executable + (name main) + (link_flags (:standard --yet-some-other-flag \ --remove-this-one-later)) + (foreign_stubs (language cxx) (names bazexe)) + (modules main)) + +(executable + (name main_no_stubs) + (modules main_no_stubs)) + +(env (some-profile (link_flags (:standard --other-flag --remove-this-one-later)))) \ No newline at end of file diff --git a/test/blackbox-tests/test-cases/cxx-flags.t/sub/dune-project b/test/blackbox-tests/test-cases/cxx-flags.t/sub/dune-project new file mode 100644 index 000000000000..37f995d64929 --- /dev/null +++ b/test/blackbox-tests/test-cases/cxx-flags.t/sub/dune-project @@ -0,0 +1 @@ +(lang dune 3.0) diff --git a/test/blackbox-tests/test-cases/cxx-flags.t/sub/main.ml b/test/blackbox-tests/test-cases/cxx-flags.t/sub/main.ml new file mode 100644 index 000000000000..414107b15150 --- /dev/null +++ b/test/blackbox-tests/test-cases/cxx-flags.t/sub/main.ml @@ -0,0 +1 @@ +let () = print_endline "OK" diff --git a/test/blackbox-tests/test-cases/cxx-flags.t/sub/main_no_stubs.ml b/test/blackbox-tests/test-cases/cxx-flags.t/sub/main_no_stubs.ml new file mode 100644 index 000000000000..414107b15150 --- /dev/null +++ b/test/blackbox-tests/test-cases/cxx-flags.t/sub/main_no_stubs.ml @@ -0,0 +1 @@ +let () = print_endline "OK"