From 94ca484f5ea007ef7a2d9e2c69fce8f9017c353f Mon Sep 17 00:00:00 2001 From: Andrey Mokhov Date: Wed, 9 Oct 2019 10:33:00 +0100 Subject: [PATCH] Add support for foreign libraries Signed-off-by: Andrey Mokhov --- bin/arg.ml | 2 +- bin/arg.mli | 2 +- bin/target.ml | 2 +- otherlibs/build-info/test/run.t | 4 +- src/dune/c.ml | 128 -------- src/dune/c.mli | 76 ----- src/dune/c_sources.ml | 143 --------- src/dune/c_sources.mli | 27 -- src/dune/cinaps.ml | 2 +- src/dune/command.ml | 2 + src/dune/command.mli | 5 +- src/dune/compilation_context.ml | 2 +- src/dune/dep_conf.ml | 100 ++++++ src/dune/dep_conf.mli | 23 ++ src/dune/dir_contents.ml | 103 +++--- src/dune/dir_contents.mli | 7 +- src/dune/dune_env.ml | 10 +- src/dune/dune_env.mli | 4 +- src/dune/dune_file.ml | 266 ++++++---------- src/dune/dune_file.mli | 52 ++-- src/dune/env_node.ml | 10 +- src/dune/env_node.mli | 4 +- src/dune/exe_rules.ml | 9 +- src/dune/expander.ml | 9 +- src/dune/expander.mli | 4 +- src/dune/findlib/findlib.ml | 4 +- src/dune/foreign.ml | 256 +++++++++++++++ src/dune/foreign.mli | 172 +++++++++++ src/dune/{c_rules.ml => foreign_rules.ml} | 55 ++-- src/dune/{c_rules.mli => foreign_rules.mli} | 8 +- src/dune/foreign_sources.ml | 150 +++++++++ src/dune/foreign_sources.mli | 15 + src/dune/gen_rules.ml | 3 + src/dune/inline_tests.ml | 3 +- src/dune/install_rules.ml | 9 +- src/dune/lib_archives.ml | 29 +- src/dune/lib_archives.mli | 4 +- src/dune/lib_file_deps.ml | 2 +- src/dune/lib_rules.ml | 149 +++++++-- src/dune/lib_rules.mli | 8 + src/dune/module_compilation.ml | 13 +- src/dune/modules_field_evaluator.ml | 2 +- src/dune/preprocessing.mli | 2 +- src/dune/simple_rules.ml | 2 +- src/dune/super_context.ml | 26 +- src/dune/super_context.mli | 15 +- src/dune/upgrader.ml | 2 +- src/dune/utils.ml | 2 +- src/dune/virtual_rules.ml | 4 +- src/dune_lang/decoder.ml | 2 + src/dune_lang/syntax.ml | 17 +- src/dune_lang/syntax.mli | 4 +- src/stdune/dune | 2 +- src/stdune/list.ml | 7 + src/stdune/list.mli | 4 + test/blackbox-tests/dune.inc | 20 ++ .../test-cases/duplicate-c-cxx-obj/run.t | 4 +- .../test-cases/duplicate-c-cxx/run.t | 3 +- .../test-cases/exes-with-c/run.t | 10 + .../test-cases/foreign-library/run.t | 292 ++++++++++++++++++ .../blackbox-tests/test-cases/github1306/dune | 4 - .../test-cases/github1306/dune-project | 1 - .../test-cases/github1306/run.t | 202 +++++++++++- .../test-cases/github1855/run.t | 1 + 64 files changed, 1718 insertions(+), 786 deletions(-) delete mode 100644 src/dune/c.ml delete mode 100644 src/dune/c.mli delete mode 100644 src/dune/c_sources.ml delete mode 100644 src/dune/c_sources.mli create mode 100644 src/dune/dep_conf.ml create mode 100644 src/dune/dep_conf.mli create mode 100644 src/dune/foreign.ml create mode 100644 src/dune/foreign.mli rename src/dune/{c_rules.ml => foreign_rules.ml} (57%) rename src/dune/{c_rules.mli => foreign_rules.mli} (54%) create mode 100644 src/dune/foreign_sources.ml create mode 100644 src/dune/foreign_sources.mli create mode 100644 test/blackbox-tests/test-cases/foreign-library/run.t delete mode 100644 test/blackbox-tests/test-cases/github1306/dune delete mode 100644 test/blackbox-tests/test-cases/github1306/dune-project diff --git a/bin/arg.ml b/bin/arg.ml index d4bd558ecda3..3ee92c677161 100644 --- a/bin/arg.ml +++ b/bin/arg.ml @@ -23,7 +23,7 @@ let profile = , fun fmt t -> Format.pp_print_string fmt (Profile.to_string t) ) module Dep = struct - module Dep_conf = Dune_file.Dep_conf + module Dep_conf = Dep_conf type t = Dep_conf.t diff --git a/bin/arg.mli b/bin/arg.mli index b9228b246409..ece6edec699b 100644 --- a/bin/arg.mli +++ b/bin/arg.mli @@ -14,7 +14,7 @@ module Path : sig end module Dep : sig - type t = Dune_file.Dep_conf.t + type t = Dep_conf.t val file : string -> t diff --git a/bin/target.ml b/bin/target.ml index ba778dc4e126..b8e4f5b33aba 100644 --- a/bin/target.ml +++ b/bin/target.ml @@ -132,7 +132,7 @@ let resolve_alias common ~recursive sv ~(setup : Dune.Main.build_system) = | None -> Error [ Pp.text "alias cannot contain variables" ] let resolve_target common ~setup = function - | Dune.Dune_file.Dep_conf.Alias sv as dep -> + | Dune.Dep_conf.Alias sv as dep -> Result.map_error ~f:(fun hints -> (dep, hints)) (resolve_alias common ~recursive:false sv ~setup) diff --git a/otherlibs/build-info/test/run.t b/otherlibs/build-info/test/run.t index c64ffeb3cf6e..ddf1673ad15e 100644 --- a/otherlibs/build-info/test/run.t +++ b/otherlibs/build-info/test/run.t @@ -100,10 +100,10 @@ Once installed, we have the version information: lib dune-build-info: XXX $ grep version _install/lib/a/dune-package - (version 1.0+a) + [1] $ grep version _install/lib/a/META - version = "1.0+a" + [1] Check what the generated build info module looks like: diff --git a/src/dune/c.ml b/src/dune/c.ml deleted file mode 100644 index 8a4ae1552826..000000000000 --- a/src/dune/c.ml +++ /dev/null @@ -1,128 +0,0 @@ -open Stdune - -let header_ext = ".h" - -module Kind = struct - type t = - | C - | Cxx - - let to_string = function - | C -> "c" - | Cxx -> "cpp" - - let pp fmt t : unit = Format.pp_print_string fmt (to_string t) - - type split = - | Unrecognized - | Not_allowed_until of Dune_lang.Syntax.Version.t - | Recognized of string * t - - let cxx_version_introduced ~obj ~dune_version ~version_introduced = - if dune_version >= version_introduced then - Recognized (obj, Cxx) - else - Not_allowed_until version_introduced - - let split_extension fn ~dune_version = - match String.rsplit2 fn ~on:'.' with - | Some (obj, "c") -> Recognized (obj, C) - | Some (obj, "cpp") -> Recognized (obj, Cxx) - | Some (obj, "cxx") -> - cxx_version_introduced ~obj ~dune_version ~version_introduced:(1, 8) - | Some (obj, "cc") -> - cxx_version_introduced ~obj ~dune_version ~version_introduced:(1, 10) - | _ -> Unrecognized - - let possible_exts ~dune_version = function - | C -> [ ".c" ] - | Cxx -> - let exts = [ ".cpp" ] in - let exts = - if dune_version >= (1, 10) then - ".cc" :: exts - else - exts - in - if dune_version >= (1, 8) then - ".cxx" :: exts - else - exts - - let possible_fns t fn ~dune_version = - possible_exts t ~dune_version |> List.map ~f:(fun ext -> fn ^ ext) - - module Dict = struct - type 'a t = - { c : 'a - ; cxx : 'a - } - - let c t = t.c - - let cxx t = t.cxx - - let map { c; cxx } ~f = { c = f c; cxx = f cxx } - - let mapi { c; cxx } ~f = { c = f ~kind:C c; cxx = f ~kind:Cxx cxx } - - let make_both a = { c = a; cxx = a } - - let make ~c ~cxx = { c; cxx } - - let get { c; cxx } = function - | C -> c - | Cxx -> cxx - - let add t k v = - match k with - | C -> { t with c = v } - | Cxx -> { t with cxx = v } - - let update t k ~f = - let v = get t k in - add t k (f v) - - let merge t1 t2 ~f = { c = f t1.c t2.c; cxx = f t1.cxx t2.cxx } - end -end - -module Source = struct - type t = - { kind : Kind.t - ; path : Path.Build.t - } - - let kind t = t.kind - - let path t = t.path - - let src_dir t = Path.Build.parent_exn t.path - - let make ~kind ~path = { kind; path } -end - -module Sources = struct - type t = (Loc.t * Source.t) String.Map.t - - let objects (t : t) ~dir ~ext_obj = - String.Map.keys t - |> List.map ~f:(fun c -> Path.Build.relative dir (c ^ ext_obj)) - - let split_by_kind t = - let c, cxx = - String.Map.partition t ~f:(fun (_, s) -> - match (Source.kind s : Kind.t) with - | C -> true - | Cxx -> false) - in - { Kind.Dict.c; cxx } -end - -let all_possible_exts = - let exts = Kind.possible_exts ~dune_version:Stanza.latest_version in - (header_ext :: exts C) @ exts Cxx - -let c_cxx_or_header ~fn = - let ext = Filename.extension fn in - List.mem ~set:all_possible_exts ext diff --git a/src/dune/c.mli b/src/dune/c.mli deleted file mode 100644 index 886ed3cc4c5e..000000000000 --- a/src/dune/c.mli +++ /dev/null @@ -1,76 +0,0 @@ -open Stdune - -val header_ext : string - -module Kind : sig - type t = - | C - | Cxx - - val to_string : t -> string - - val pp : t Fmt.t - - type split = - | Unrecognized - | Not_allowed_until of Dune_lang.Syntax.Version.t - | Recognized of string * t - - val split_extension : - string -> dune_version:Dune_lang.Syntax.Version.t -> split - - (** [possible_fns t s] returns the possible filenames given the - extension-less basenames [s] *) - val possible_fns : - t -> string -> dune_version:Dune_lang.Syntax.Version.t -> string list - - module Dict : sig - type kind - - type 'a t = - { c : 'a - ; cxx : 'a - } - - val c : 'a t -> 'a - - val cxx : 'a t -> 'a - - val map : 'a t -> f:('a -> 'b) -> 'b t - - val mapi : 'a t -> f:(kind:kind -> 'a -> 'b) -> 'b t - - val make_both : 'a -> 'a t - - val make : c:'a -> cxx:'a -> 'a t - - val update : 'a t -> kind -> f:('a -> 'a) -> 'a t - - val merge : 'a t -> 'b t -> f:('a -> 'b -> 'c) -> 'c t - - val get : 'a t -> kind -> 'a - end - with type kind := t -end - -module Source : sig - type t - - val kind : t -> Kind.t - - val path : t -> Path.Build.t - - val src_dir : t -> Path.Build.t - - val make : kind:Kind.t -> path:Path.Build.t -> t -end - -module Sources : sig - type t = (Loc.t * Source.t) String.Map.t - - val objects : t -> dir:Path.Build.t -> ext_obj:string -> Path.Build.t list - - val split_by_kind : t -> t Kind.Dict.t -end - -val c_cxx_or_header : fn:string -> bool diff --git a/src/dune/c_sources.ml b/src/dune/c_sources.ml deleted file mode 100644 index 80719017adda..000000000000 --- a/src/dune/c_sources.ml +++ /dev/null @@ -1,143 +0,0 @@ -open Stdune -open Dune_file -module Library = Dune_file.Library - -type t = - { libraries : C.Sources.t Lib_name.Map.t - ; executables : C.Sources.t String.Map.t - } - -let for_lib t ~name = Lib_name.Map.find_exn t.libraries name - -let for_exes t ~first_exe = String.Map.find_exn t.executables first_exe - -let empty = { libraries = Lib_name.Map.empty; executables = String.Map.empty } - -let c_name, cxx_name = - let make what ~loc s = - if - match s with - | "" - | "." - | ".." -> - true - | _ -> false - then - User_error.raise ~loc [ Pp.textf "%S is not a valid %s name." s what ] - else - s - in - (make "C", make "C++") - -let load_sources ~dune_version ~dir ~files = - let init = C.Kind.Dict.make_both String.Map.empty in - String.Set.fold files ~init ~f:(fun fn acc -> - match C.Kind.split_extension fn ~dune_version with - | Unrecognized -> acc - | Not_allowed_until version -> - let loc = Loc.in_dir (Path.build dir) in - User_error.raise ~loc - [ Pp.textf - "Source file %s with extension %s is not allowed before version \ - %s" - fn (Filename.extension fn) - (Dune_lang.Syntax.Version.to_string version) - ] - | Recognized (obj, kind) -> - let path = Path.Build.relative dir fn in - C.Kind.Dict.update acc kind ~f:(fun v -> - String.Map.set v obj (C.Source.make ~kind ~path))) - -let eval_c_sources (d : _ Dir_with_dune.t) buildable ~c_sources = - let eval (kind : C.Kind.t) (c_sources : C.Source.t String.Map.t) validate osl - = - Ordered_set_lang.Unordered_string.eval_loc osl - ~key:(fun x -> x) - ~parse:(fun ~loc s -> - let s = validate ~loc s in - let s' = Filename.basename s in - if s' <> s then - User_error.raise ~loc - [ Pp.text - "relative part of stub is not necessary and should be \ - removed. To include sources in subdirectories, use the \ - include_subdirs stanza" - ]; - s') - ~standard:String.Map.empty - |> String.Map.map ~f:(fun (loc, s) -> - match String.Map.find c_sources s with - | Some source -> (loc, source) - | None -> - let dune_version = d.dune_version in - User_error.raise ~loc - [ Pp.textf "%s does not exist as a C source. %s must be present" - s - (String.enumerate_one_of - (C.Kind.possible_fns kind s ~dune_version)) - ]) - in - let names = Option.value ~default:Ordered_set_lang.standard in - let c = - eval C.Kind.C c_sources.C.Kind.Dict.c c_name - (names buildable.Buildable.c_names) - in - let cxx = - eval C.Kind.Cxx c_sources.cxx cxx_name (names buildable.cxx_names) - in - String.Map.union c cxx ~f:(fun _ (_loc1, c) (loc2, cxx) -> - User_error.raise ~loc:loc2 - [ Pp.textf - "%s and %s have conflicting names. You must rename one of them." - (Path.to_string_maybe_quoted - (Path.drop_optional_build_context - (Path.build (C.Source.path cxx)))) - (Path.to_string_maybe_quoted - (Path.drop_optional_build_context (Path.build (C.Source.path c)))) - ]) - -let make (d : _ Dir_with_dune.t) - ~(c_sources : C.Source.t String.Map.t C.Kind.Dict.t) = - let libs, exes = - List.filter_partition_map d.data ~f:(fun stanza -> - match (stanza : Stanza.t) with - | Library lib -> - let all = eval_c_sources d lib.buildable ~c_sources in - Left (lib, all) - | Executables exes -> - let all = eval_c_sources d exes.buildable ~c_sources in - Right (exes, all) - | _ -> Skip) - in - let libraries = - match - Lib_name.Map.of_list_map libs ~f:(fun (lib, m) -> - (Library.best_name lib, m)) - with - | Ok x -> x - | Error (name, _, (lib2, _)) -> - User_error.raise ~loc:lib2.buildable.loc - [ Pp.textf "Library %S appears for the second time in this directory" - (Lib_name.to_string name) - ] - in - let executables = - String.Map.of_list_map_exn exes ~f:(fun (exes, m) -> - (snd (List.hd exes.names), m)) - in - let () = - let rev_map = - List.concat_map libs ~f:(fun (_, c_sources) -> - String.Map.values c_sources - |> List.map ~f:(fun (loc, source) -> (C.Source.path source, loc))) - |> Path.Build.Map.of_list - in - match rev_map with - | Ok _ -> () - | Error (_, loc1, loc2) -> - User_error.raise ~loc:loc2 - [ Pp.text "This c stub is already used in another stanza:" - ; Pp.textf "- %s" (Loc.to_file_colon_line loc1) - ] - in - { libraries; executables } diff --git a/src/dune/c_sources.mli b/src/dune/c_sources.mli deleted file mode 100644 index f61f20f80131..000000000000 --- a/src/dune/c_sources.mli +++ /dev/null @@ -1,27 +0,0 @@ -(** This module loads and validates C/C++ sources from directories. *) - -open Stdune - -type t - -val empty : t - -val for_lib : t -> name:Lib_name.t -> C.Sources.t - -val for_exes : t -> first_exe:string -> C.Sources.t - -(** [load_sources dir ~files] will load the C sources in [dir] into a two - double map. The first level will is keyed by C vs. C++ sources. The second - level is keyed by the object name of the source. *) -val load_sources : - dune_version:Dune_lang.Syntax.Version.t - -> dir:Path.Build.t - -> files:String.Set.t - -> C.Source.t String.Map.t C.Kind.Dict.t - -(** [make stanzas ~c_sources] will load and validate C/C++ sources. [c_sources] - should be a two level map such as the one returned by [load_sources] *) -val make : - Stanza.t list Dir_with_dune.t - -> c_sources:C.Source.t String.Map.t C.Kind.Dict.t - -> t diff --git a/src/dune/cinaps.ml b/src/dune/cinaps.ml index 156e309b0540..d026ca5efaf2 100644 --- a/src/dune/cinaps.ml +++ b/src/dune/cinaps.ml @@ -7,7 +7,7 @@ type t = ; files : Predicate_lang.t ; libraries : Lib_dep.t list ; preprocess : Dune_file.Preprocess_map.t - ; preprocessor_deps : Dune_file.Dep_conf.t list + ; preprocessor_deps : Dep_conf.t list ; flags : Ocaml_flags.Spec.t } diff --git a/src/dune/command.ml b/src/dune/command.ml index ef2a9bc4767a..d11317363ceb 100644 --- a/src/dune/command.ml +++ b/src/dune/command.ml @@ -23,6 +23,8 @@ module Args = struct (* TODO: Shall we simply make the constructor [Dyn] to accept a list? *) let dyn args = Dyn (Build.map args ~f:(fun x -> As x)) + + let empty = S [] end open Args diff --git a/src/dune/command.mli b/src/dune/command.mli index 862ec3ba7007..51357f32a40a 100644 --- a/src/dune/command.mli +++ b/src/dune/command.mli @@ -45,8 +45,11 @@ module Args : sig | Dyn : static t Build.t -> dynamic t | Fail : fail -> _ t - (* Create dynamic command line arguments. *) + (** Create dynamic command line arguments. *) val dyn : string list Build.t -> dynamic t + + (** Create an empty command line. *) + val empty : _ t end (* TODO: Using list in [dynamic t list] complicates the API unnecessarily: we diff --git a/src/dune/compilation_context.ml b/src/dune/compilation_context.ml index 9d6ae4cd4aeb..84f466d0d96d 100644 --- a/src/dune/compilation_context.ml +++ b/src/dune/compilation_context.ml @@ -35,7 +35,7 @@ module Includes = struct in { cmi = cmi_includes; cmo = cmi_includes; cmx = cmx_includes } - let empty = Cm_kind.Dict.make_all (Command.Args.As []) + let empty = Cm_kind.Dict.make_all Command.Args.empty end type t = diff --git a/src/dune/dep_conf.ml b/src/dune/dep_conf.ml new file mode 100644 index 000000000000..b18b805df1d2 --- /dev/null +++ b/src/dune/dep_conf.ml @@ -0,0 +1,100 @@ +open! Stdune +open Import +open Dune_lang.Decoder + +type t = + | File of String_with_vars.t + | Alias of String_with_vars.t + | Alias_rec of String_with_vars.t + | Glob_files of String_with_vars.t + | Source_tree of String_with_vars.t + | Package of String_with_vars.t + | Universe + | Env_var of String_with_vars.t + | Sandbox_config of Sandbox_config.t + +let remove_locs = function + | File sw -> File (String_with_vars.remove_locs sw) + | Alias sw -> Alias (String_with_vars.remove_locs sw) + | Alias_rec sw -> Alias_rec (String_with_vars.remove_locs sw) + | Glob_files sw -> Glob_files (String_with_vars.remove_locs sw) + | Source_tree sw -> Source_tree (String_with_vars.remove_locs sw) + | Package sw -> Package (String_with_vars.remove_locs sw) + | Universe -> Universe + | Env_var sw -> Env_var sw + | Sandbox_config s -> Sandbox_config s + +let decode_sandbox_config = + let+ () = Dune_lang.Syntax.since Stanza.syntax (1, 12) + and+ loc, x = + located + (repeat + (sum + [ ("none", return Sandbox_config.Partial.no_sandboxing) + ; ("always", return Sandbox_config.Partial.needs_sandboxing) + ; ( "preserve_file_kind" + , return (Sandbox_config.Partial.disallow Sandbox_mode.symlink) + ) + ])) + in + Sandbox_config.Partial.merge ~loc x + +let decode = + let decode = + let sw = String_with_vars.decode in + sum + [ ("file", sw >>| fun x -> File x) + ; ("alias", sw >>| fun x -> Alias x) + ; ("alias_rec", sw >>| fun x -> Alias_rec x) + ; ("glob_files", sw >>| fun x -> Glob_files x) + ; ("package", sw >>| fun x -> Package x) + ; ("universe", return Universe) + ; ( "files_recursively_in" + , let+ () = + Dune_lang.Syntax.renamed_in Stanza.syntax (1, 0) ~to_:"source_tree" + and+ x = sw in + Source_tree x ) + ; ( "source_tree" + , let+ () = Dune_lang.Syntax.since Stanza.syntax (1, 0) + and+ x = sw in + Source_tree x ) + ; ("env_var", sw >>| fun x -> Env_var x) + ; ("sandbox", decode_sandbox_config >>| fun x -> Sandbox_config x) + ] + in + if_list ~then_:decode ~else_:(String_with_vars.decode >>| fun x -> File x) + +open Dune_lang + +let encode = function + | File t -> + List [ Dune_lang.unsafe_atom_of_string "file"; String_with_vars.encode t ] + | Alias t -> + List [ Dune_lang.unsafe_atom_of_string "alias"; String_with_vars.encode t ] + | Alias_rec t -> + List + [ Dune_lang.unsafe_atom_of_string "alias_rec"; String_with_vars.encode t ] + | Glob_files t -> + List + [ Dune_lang.unsafe_atom_of_string "glob_files" + ; String_with_vars.encode t + ] + | Source_tree t -> + List + [ Dune_lang.unsafe_atom_of_string "source_tree" + ; String_with_vars.encode t + ] + | Package t -> + List + [ Dune_lang.unsafe_atom_of_string "package"; String_with_vars.encode t ] + | Universe -> Dune_lang.unsafe_atom_of_string "universe" + | Env_var t -> + List + [ Dune_lang.unsafe_atom_of_string "env_var"; String_with_vars.encode t ] + | Sandbox_config config -> + if Sandbox_config.equal config Sandbox_config.no_special_requirements then + List [] + else + Code_error.raise "There's no syntax for [Sandbox_config] yet" [] + +let to_dyn t = Dune_lang.to_dyn (encode t) diff --git a/src/dune/dep_conf.mli b/src/dune/dep_conf.mli new file mode 100644 index 000000000000..7f3a0a16e627 --- /dev/null +++ b/src/dune/dep_conf.mli @@ -0,0 +1,23 @@ +open! Stdune +open Import + +type t = + | File of String_with_vars.t + | Alias of String_with_vars.t + | Alias_rec of String_with_vars.t + | Glob_files of String_with_vars.t + | Source_tree of String_with_vars.t + | Package of String_with_vars.t + | Universe + | Env_var of String_with_vars.t + (* [Sandbox_config] is a way to declare that your action also depends on + there being a clean filesystem around its deps. (or, if you require + [no_sandboxing], it's that your action depends on something undeclared + (e.g. absolute path of cwd) and you want to allow it) *) + | Sandbox_config of Sandbox_config.t + +val remove_locs : t -> t + +include Dune_lang.Conv.S with type t := t + +val to_dyn : t Dyn.Encoder.t diff --git a/src/dune/dir_contents.ml b/src/dune/dir_contents.ml index cbd52b11a9b4..ddf1fcc87d4f 100644 --- a/src/dune/dir_contents.ml +++ b/src/dune/dir_contents.ml @@ -38,7 +38,7 @@ type t = ; dir : Path.Build.t ; text_files : String.Set.t ; modules : Dir_modules.t Memo.Lazy.t - ; c_sources : C_sources.t Memo.Lazy.t + ; foreign_sources : Foreign_sources.t Memo.Lazy.t ; mlds : (Dune_file.Documentation.t * Path.Build.t list) list Memo.Lazy.t ; coq_modules : Coq_module.t list Lib_name.Map.t Memo.Lazy.t ; artifacts : Dir_artifacts.t Memo.Lazy.t @@ -77,11 +77,16 @@ let modules_of_executables t ~obj_dir ~first_exe = let src_dir = Path.build (Obj_dir.obj_dir obj_dir) in String.Map.find_exn map first_exe |> Modules.relocate_alias_module ~src_dir -let c_sources_of_executables t ~first_exe = - C_sources.for_exes (Memo.Lazy.force t.c_sources) ~first_exe +let foreign_sources_of_executables t ~first_exe = + Foreign_sources.for_exes (Memo.Lazy.force t.foreign_sources) ~first_exe -let c_sources_of_library t ~name = - C_sources.for_lib (Memo.Lazy.force t.c_sources) ~name +let foreign_sources_of_library t ~name = + Foreign_sources.for_lib (Memo.Lazy.force t.foreign_sources) ~name + +let foreign_sources_of_foreign_library t ~archive_name = + Foreign_sources.for_foreign_lib + (Memo.Lazy.force t.foreign_sources) + ~archive_name let lookup_module t name = Module_name.Map.find (Memo.Lazy.force t.modules).rev_map name @@ -521,12 +526,12 @@ end = struct ; text_files = files ; modules = Memo.Lazy.map ~f:make_modules libs_and_exes ; mlds = Memo.lazy_ (fun () -> build_mlds_map d ~files) - ; c_sources = + ; foreign_sources = Memo.lazy_ (fun () -> let dune_version = d.dune_version in - C_sources.make d - ~c_sources: - (C_sources.load_sources ~dune_version ~dir:d.ctx_dir + Foreign_sources.make d + ~object_map: + (Foreign.Object_map.load ~dune_version ~dir:d.ctx_dir ~files)) ; coq_modules = Memo.lazy_ (fun () -> @@ -547,7 +552,7 @@ end = struct ; text_files = String.Set.empty ; modules = Memo.Lazy.of_val Dir_modules.empty ; mlds = Memo.Lazy.of_val [] - ; c_sources = Memo.Lazy.of_val C_sources.empty + ; foreign_sources = Memo.Lazy.of_val Foreign_sources.empty ; coq_modules = Memo.Lazy.of_val Lib_name.Map.empty ; artifacts = Memo.Lazy.of_val Dir_artifacts.empty } @@ -622,48 +627,54 @@ end = struct in let modules = Memo.Lazy.map ~f:make_modules libs_and_exes in let artifacts = Memo.Lazy.map ~f:(make_artifacts d) libs_and_exes in - let c_sources = + let foreign_sources = Memo.lazy_ (fun () -> check_no_qualified Loc.none qualif_mode; let dune_version = d.dune_version in - let init = C.Kind.Dict.make_both String.Map.empty in - let c_sources = + let init = String.Map.empty in + let object_map = List.fold_left ((dir, [], files) :: subdirs) ~init ~f:(fun acc (dir, _local, files) -> let sources = - C_sources.load_sources ~dir ~dune_version ~files - in - let f acc sources = - String.Map.union acc sources ~f:(fun name x y -> - User_error.raise - ~loc: - (Loc.in_file - (Path.source - ( match File_tree.Dir.dune_file ft_dir with - | None -> - Path.Source.relative - (File_tree.Dir.path ft_dir) - "_unknown_" - | Some d -> File_tree.Dune_file.path d ))) - [ Pp.textf - "%s file %s appears in several directories:" - (C.Kind.to_string (C.Source.kind x)) - name - ; Pp.textf "- %s" - (Path.to_string_maybe_quoted - (Path.drop_optional_build_context - (Path.build (C.Source.src_dir x)))) - ; Pp.textf "- %s" - (Path.to_string_maybe_quoted - (Path.drop_optional_build_context - (Path.build (C.Source.src_dir y)))) - ; Pp.text - "This is not allowed, please rename one of them." - ]) + Foreign.Object_map.load ~dir ~dune_version ~files in - C.Kind.Dict.merge acc sources ~f) + String.Map.union acc sources ~f:(fun name map1 map2 -> + Some + (Foreign.Language.Map.union map1 map2 + ~f:(fun language path1 path2 -> + User_error.raise + ~loc: + (Loc.in_file + (Path.source + ( match + File_tree.Dir.dune_file ft_dir + with + | None -> + Path.Source.relative + (File_tree.Dir.path ft_dir) + "_unknown_" + | Some d -> File_tree.Dune_file.path d + ))) + [ Pp.textf + "%s source file %S appears in more than \ + one directory:" + (Foreign.Language.proper_name language) + name + ; Pp.textf "- %s" + (Path.to_string_maybe_quoted + (Path.drop_optional_build_context + (Path.build + (Path.Build.parent_exn path1)))) + ; Pp.textf "- %s" + (Path.to_string_maybe_quoted + (Path.drop_optional_build_context + (Path.build + (Path.Build.parent_exn path2)))) + ; Pp.text + "This is not allowed; please rename them." + ])))) in - C_sources.make d ~c_sources) + Foreign_sources.make d ~object_map) in let coq_modules = Memo.lazy_ (fun () -> @@ -678,7 +689,7 @@ end = struct ; dir ; text_files = files ; modules - ; c_sources + ; foreign_sources ; mlds = Memo.lazy_ (fun () -> build_mlds_map d ~files) ; coq_modules ; artifacts @@ -689,7 +700,7 @@ end = struct ; dir ; text_files = files ; modules - ; c_sources + ; foreign_sources ; mlds = Memo.lazy_ (fun () -> build_mlds_map d ~files) ; coq_modules ; artifacts diff --git a/src/dune/dir_contents.mli b/src/dune/dir_contents.mli index 3b9414bae16e..56b2b17c0d85 100644 --- a/src/dune/dir_contents.mli +++ b/src/dune/dir_contents.mli @@ -28,13 +28,16 @@ val text_files : t -> String.Set.t (** Modules attached to a library. [name] is the library best name. *) val modules_of_library : t -> name:Lib_name.t -> Modules.t -val c_sources_of_library : t -> name:Lib_name.t -> C.Sources.t +val foreign_sources_of_library : t -> name:Lib_name.t -> Foreign.Sources.t + +val foreign_sources_of_foreign_library : + t -> archive_name:string -> Foreign.Sources.t (** Modules attached to a set of executables. *) val modules_of_executables : t -> obj_dir:Path.Build.t Obj_dir.t -> first_exe:string -> Modules.t -val c_sources_of_executables : t -> first_exe:string -> C.Sources.t +val foreign_sources_of_executables : t -> first_exe:string -> Foreign.Sources.t (** Find out what buildable a module is part of *) val lookup_module : t -> Module_name.t -> Dune_file.Buildable.t option diff --git a/src/dune/dune_env.ml b/src/dune/dune_env.ml index abc00ddca4d7..26c788073b6a 100644 --- a/src/dune/dune_env.ml +++ b/src/dune/dune_env.ml @@ -8,11 +8,12 @@ module Stanza = struct let c_flags ~since = let check = Option.map since ~f:(fun since -> - Dune_lang.Syntax.since Stanza.syntax since) + Dune_lang.Syntax.deprecated_in Stanza.syntax (2, 0) + >>> Dune_lang.Syntax.since Stanza.syntax since) in let+ c = Ordered_set_lang.Unexpanded.field "c_flags" ?check and+ cxx = Ordered_set_lang.Unexpanded.field "cxx_flags" ?check in - C.Kind.Dict.make ~c ~cxx + Foreign.Language.Dict.make ~c ~cxx module Inline_tests = struct type t = @@ -32,7 +33,7 @@ module Stanza = struct type config = { flags : Ocaml_flags.Spec.t - ; c_flags : Ordered_set_lang.Unexpanded.t C.Kind.Dict.t + ; c_flags : Ordered_set_lang.Unexpanded.t Foreign.Language.Dict.t ; env_vars : Env.t ; binaries : File_binding.Unexpanded.t list ; inline_tests : Inline_tests.t option @@ -40,7 +41,8 @@ module Stanza = struct let empty_config = { flags = Ocaml_flags.Spec.standard - ; c_flags = C.Kind.Dict.make_both Ordered_set_lang.Unexpanded.standard + ; c_flags = + Foreign.Language.Dict.make_both Ordered_set_lang.Unexpanded.standard ; env_vars = Env.empty ; binaries = [] ; inline_tests = None diff --git a/src/dune/dune_env.mli b/src/dune/dune_env.mli index a04be47212ce..2c80be9ce956 100644 --- a/src/dune/dune_env.mli +++ b/src/dune/dune_env.mli @@ -16,7 +16,7 @@ module Stanza : sig type config = { flags : Ocaml_flags.Spec.t - ; c_flags : Ordered_set_lang.Unexpanded.t C.Kind.Dict.t + ; c_flags : Ordered_set_lang.Unexpanded.t Foreign.Language.Dict.t ; env_vars : Env.t ; binaries : File_binding.Unexpanded.t list ; inline_tests : Inline_tests.t option @@ -33,7 +33,7 @@ module Stanza : sig val c_flags : since:Dune_lang.Syntax.Version.t option - -> Ordered_set_lang.Unexpanded.t C.Kind.Dict.t + -> Ordered_set_lang.Unexpanded.t Foreign.Language.Dict.t Dune_lang.Decoder.fields_parser val decode : t Dune_lang.Decoder.t diff --git a/src/dune/dune_file.ml b/src/dune/dune_file.ml index 03bfd6a6d609..2f251a0d5f88 100644 --- a/src/dune/dune_file.ml +++ b/src/dune/dune_file.ml @@ -162,110 +162,6 @@ module Pps_and_flags = struct (pps, all_flags) end -module Dep_conf = struct - type t = - | File of String_with_vars.t - | Alias of String_with_vars.t - | Alias_rec of String_with_vars.t - | Glob_files of String_with_vars.t - | Source_tree of String_with_vars.t - | Package of String_with_vars.t - | Universe - | Env_var of String_with_vars.t - | Sandbox_config of Sandbox_config.t - - let remove_locs = function - | File sw -> File (String_with_vars.remove_locs sw) - | Alias sw -> Alias (String_with_vars.remove_locs sw) - | Alias_rec sw -> Alias_rec (String_with_vars.remove_locs sw) - | Glob_files sw -> Glob_files (String_with_vars.remove_locs sw) - | Source_tree sw -> Source_tree (String_with_vars.remove_locs sw) - | Package sw -> Package (String_with_vars.remove_locs sw) - | Universe -> Universe - | Env_var sw -> Env_var sw - | Sandbox_config s -> Sandbox_config s - - let decode_sandbox_config = - let+ () = Dune_lang.Syntax.since Stanza.syntax (1, 12) - and+ loc, x = - located - (repeat - (sum - [ ("none", return Sandbox_config.Partial.no_sandboxing) - ; ("always", return Sandbox_config.Partial.needs_sandboxing) - ; ( "preserve_file_kind" - , return (Sandbox_config.Partial.disallow Sandbox_mode.symlink) - ) - ])) - in - Sandbox_config.Partial.merge ~loc x - - let decode = - let decode = - let sw = String_with_vars.decode in - sum - [ ("file", sw >>| fun x -> File x) - ; ("alias", sw >>| fun x -> Alias x) - ; ("alias_rec", sw >>| fun x -> Alias_rec x) - ; ("glob_files", sw >>| fun x -> Glob_files x) - ; ("package", sw >>| fun x -> Package x) - ; ("universe", return Universe) - ; ( "files_recursively_in" - , let+ () = - Dune_lang.Syntax.renamed_in Stanza.syntax (1, 0) - ~to_:"source_tree" - and+ x = sw in - Source_tree x ) - ; ( "source_tree" - , let+ () = Dune_lang.Syntax.since Stanza.syntax (1, 0) - and+ x = sw in - Source_tree x ) - ; ("env_var", sw >>| fun x -> Env_var x) - ; ("sandbox", decode_sandbox_config >>| fun x -> Sandbox_config x) - ] - in - if_list ~then_:decode ~else_:(String_with_vars.decode >>| fun x -> File x) - - open Dune_lang - - let encode = function - | File t -> - List [ Dune_lang.unsafe_atom_of_string "file"; String_with_vars.encode t ] - | Alias t -> - List - [ Dune_lang.unsafe_atom_of_string "alias"; String_with_vars.encode t ] - | Alias_rec t -> - List - [ Dune_lang.unsafe_atom_of_string "alias_rec" - ; String_with_vars.encode t - ] - | Glob_files t -> - List - [ Dune_lang.unsafe_atom_of_string "glob_files" - ; String_with_vars.encode t - ] - | Source_tree t -> - List - [ Dune_lang.unsafe_atom_of_string "source_tree" - ; String_with_vars.encode t - ] - | Package t -> - List - [ Dune_lang.unsafe_atom_of_string "package"; String_with_vars.encode t ] - | Universe -> Dune_lang.unsafe_atom_of_string "universe" - | Env_var t -> - List - [ Dune_lang.unsafe_atom_of_string "env_var"; String_with_vars.encode t ] - | Sandbox_config config -> - if Sandbox_config.equal config Sandbox_config.no_special_requirements - then - List [] - else - Code_error.raise "There's no syntax for [Sandbox_config] yet" [] - - let to_dyn t = Dune_lang.to_dyn (encode t) -end - module Preprocess = struct module Pps = struct type t = @@ -565,9 +461,8 @@ module Buildable = struct ; modules : Ordered_set_lang.t ; modules_without_implementation : Ordered_set_lang.t ; libraries : Lib_dep.t list - ; c_flags : Ordered_set_lang.Unexpanded.t C.Kind.Dict.t - ; c_names : Ordered_set_lang.t option - ; cxx_names : Ordered_set_lang.t option + ; foreign_archives : (Loc.t * string) list + ; foreign_stubs : Foreign.Stubs.t list ; preprocess : Preprocess_map.t ; preprocessor_deps : Dep_conf.t list ; lint : Preprocess_map.t @@ -577,18 +472,51 @@ module Buildable = struct } let decode ~since_c ~allow_re_export = + let use_foreign = + Dune_lang.Syntax.deprecated_in Stanza.syntax (2, 0) + ~extra_info:"Use the (foreign_stubs ...) stanza instead." + in let check_c t = match since_c with | None -> t | Some v -> Dune_lang.Syntax.since Stanza.syntax v >>> t in + let add_stubs language ~loc ~names ~flags foreign_stubs = + match names with + | None -> foreign_stubs + | Some names -> + Foreign.Stubs.make ~loc ~language ~names ~flags :: foreign_stubs + in let+ loc = loc and+ preprocess, preprocessor_deps = preprocess_fields and+ lint = field "lint" Lint.decode ~default:Lint.default - and+ c_flags = Dune_env.Stanza.c_flags ~since:since_c - and+ c_names = field_o "c_names" (check_c Ordered_set_lang.decode) - and+ cxx_names = field_o "cxx_names" (check_c Ordered_set_lang.decode) + and+ foreign_stubs = + multi_field "foreign_stubs" + (Dune_lang.Syntax.since Stanza.syntax (2, 0) >>> Foreign.Stubs.decode) + and+ foreign_archives = + field_o "foreign_stubs_archives" + ( Dune_lang.Syntax.since Stanza.syntax (2, 0) + >>> repeat (located string) ) + and+ c_flags = + Ordered_set_lang.Unexpanded.field "c_flags" ?check:(Some (use_foreign >>> check_c (return ()))) + and+ cxx_flags = + Ordered_set_lang.Unexpanded.field "cxx_flags" + ?check:(Some (use_foreign >>> check_c (return ()))) + and+ c_names_loc, c_names = + located + (field_o "c_names" (use_foreign >>> check_c Ordered_set_lang.decode)) + and+ cxx_names_loc, cxx_names = + located + (field_o "cxx_names" (use_foreign >>> check_c Ordered_set_lang.decode)) and+ modules = modules_field "modules" + and+ loc_sbsa, self_build_stubs_archive = + located + (field "self_build_stubs_archive" + ( Dune_lang.Syntax.deprecated_in Stanza.syntax (2, 0) + ~extra_info: + "Use the (foreign_stubs_archives ...) stanza instead." + >>> option string ) + ~default:None) and+ modules_without_implementation = modules_field "modules_without_implementation" and+ libraries = @@ -598,6 +526,33 @@ module Buildable = struct field "js_of_ocaml" Js_of_ocaml.decode ~default:Js_of_ocaml.default and+ allow_overlapping_dependencies = field_b "allow_overlapping_dependencies" + and+ version = Dune_lang.Syntax.get_exn Stanza.syntax in + let foreign_stubs = + foreign_stubs + |> add_stubs C ~loc:c_names_loc ~names:c_names ~flags:c_flags + |> add_stubs Cxx ~loc:cxx_names_loc ~names:cxx_names ~flags:cxx_flags + in + let foreign_archives = Option.value ~default:[] foreign_archives in + let foreign_archives = + if + version < (2, 0) + && List.is_non_empty foreign_stubs + && Option.is_some self_build_stubs_archive + then + User_error.raise ~loc:loc_sbsa + [ Pp.concat + [ Pp.textf "A library cannot use " + ; Pp.hbox (Pp.textf "(self_build_stubs_archive ...)") + ; Pp.textf " and " + ; Pp.hbox (Pp.textf "(c_names ...)") + ; Pp.textf " simultaneously. This is supported starting from " + ; Pp.hbox (Pp.textf "Dune 2.0.") + ] + ] + else + match self_build_stubs_archive with + | None -> foreign_archives + | Some name -> (loc, name ^ "_stubs") :: foreign_archives in { loc ; preprocess @@ -605,9 +560,8 @@ module Buildable = struct ; lint ; modules ; modules_without_implementation - ; c_flags - ; c_names - ; cxx_names + ; foreign_stubs + ; foreign_archives ; libraries ; flags ; js_of_ocaml @@ -817,7 +771,6 @@ module Library = struct ; kind : Lib_kind.t ; library_flags : Ordered_set_lang.Unexpanded.t ; c_library_flags : Ordered_set_lang.Unexpanded.t - ; self_build_stubs_archive : string option ; virtual_deps : (Loc.t * Lib_name.t) list ; wrapped : Wrapped.t Lib_info.Inherited.t ; optional : bool @@ -859,9 +812,6 @@ module Library = struct and+ kind = field "kind" Lib_kind.decode ~default:Lib_kind.Normal and+ wrapped = Wrapped.field and+ optional = field_b "optional" - and+ self_build_stubs_archive = - located - (field "self_build_stubs_archive" (option string) ~default:None) and+ no_dynlink = field_b "no_dynlink" and+ no_keep_locs = field_b "no_keep_locs" @@ -958,29 +908,6 @@ module Library = struct | _ -> (); let variant = Option.map variant ~f:(fun (_, v) -> v) in - let self_build_stubs_archive = - let loc, self_build_stubs_archive = self_build_stubs_archive in - let err = - match - ( buildable.c_names - , buildable.cxx_names - , self_build_stubs_archive ) - with - | _, _, None -> None - | Some _, _, Some _ -> Some "c_names" - | _, Some _, Some _ -> Some "cxx_names" - | None, None, _ -> None - in - match err with - | None -> self_build_stubs_archive - | Some name -> - User_error.raise ~loc - [ Pp.textf - "A library cannot use (self_build_stubs_archive ...) and \ - (%s ...) simultaneously." - name - ] - in Blang.fold_vars enabled_if ~init:() ~f:(fun var () -> match ( String_with_vars.Var.name var @@ -1006,7 +933,6 @@ module Library = struct ; kind ; library_flags ; c_library_flags - ; self_build_stubs_archive ; virtual_deps ; wrapped ; optional @@ -1027,27 +953,35 @@ module Library = struct } )) let has_stubs t = - match - (t.buildable.c_names, t.buildable.cxx_names, t.self_build_stubs_archive) - with - | None, None, None -> false - | _ -> true + List.is_non_empty t.buildable.foreign_stubs + || List.is_non_empty t.buildable.foreign_archives - let stubs_name t = - let name = - match t.self_build_stubs_archive with - | None -> Lib_name.Local.to_string (snd t.name) - | Some s -> s - in - name ^ "_stubs" + let default_archive_name t = Lib_name.Local.to_string (snd t.name) ^ "_stubs" + + let default_lib_file t ~dir ~ext_lib = + Path.Build.relative dir + (sprintf "lib%s%s" (default_archive_name t) ext_lib) - let stubs t ~dir = Path.Build.relative dir (stubs_name t) + let default_dll_file t ~dir ~ext_dll = + Path.Build.relative dir + (sprintf "dll%s%s" (default_archive_name t) ext_dll) - let stubs_archive t ~dir ~ext_lib = - Path.Build.relative dir (sprintf "lib%s%s" (stubs_name t) ext_lib) + let archive_names t = + ( if List.is_empty t.buildable.foreign_stubs then + [] + else + [ default_archive_name t ] ) + @ List.map ~f:snd t.buildable.foreign_archives - let dll t ~dir ~ext_dll = - Path.Build.relative dir (sprintf "dll%s%s" (stubs_name t) ext_dll) + let lib_files t ~dir ~ext_lib = + List.map (archive_names t) ~f:(fun name -> + Path.Build.relative dir (sprintf "lib%s%s" name ext_lib)) + + let dll_files t ~dir ~ext_dll = + List.map (archive_names t) ~f:(fun name -> + Path.Build.relative dir (sprintf "dll%s%s" name ext_dll)) + + let stubs_path t ~dir = Path.Build.relative dir (default_archive_name t) let archive t ~dir ~ext = Path.Build.relative dir (Lib_name.Local.to_string (snd t.name) ^ ext) @@ -1099,12 +1033,7 @@ module Library = struct in let virtual_library = is_virtual conf in let foreign_archives = - let stubs = - if has_stubs conf then - [ stubs_archive conf ~dir ~ext_lib ] - else - [] - in + let stubs = lib_files conf ~dir ~ext_lib in { Mode.Dict.byte = stubs ; native = Path.Build.relative dir (Lib_name.Local.to_string lib_name ^ ext_lib) @@ -1644,8 +1573,8 @@ module Executables = struct (make false, make true) let has_stubs t = - match (t.buildable.c_names, t.buildable.cxx_names) with - | None, None -> false + match t.buildable.foreign_stubs with + | [] -> false | _ -> true let obj_dir t ~dir = Obj_dir.make_exe ~dir ~name:(snd (List.hd t.names)) @@ -2212,6 +2141,7 @@ end type Stanza.t += | Library of Library.t + | Foreign_library of Foreign.Library.t | Executables of Executables.t | Rule of Rule.t | Install of File_binding.Unexpanded.t Install_conf.t @@ -2243,6 +2173,10 @@ module Stanzas = struct [ ( "library" , let+ x = Library.decode in [ Library x ] ) + ; ( "foreign_library" + , let+ () = Dune_lang.Syntax.since Stanza.syntax (2, 0) + and+ x = Foreign.Library.decode in + [ Foreign_library x ] ) ; ("executable", Executables.single >>| execs) ; ("executables", Executables.multi >>| execs) ; ( "rule" diff --git a/src/dune/dune_file.mli b/src/dune/dune_file.mli index b55730a4e451..063a5ae92226 100644 --- a/src/dune/dune_file.mli +++ b/src/dune/dune_file.mli @@ -1,4 +1,4 @@ -(** Representation and parsing of jbuild files *) +(** Representation and parsing of dune files *) open! Stdune open Import @@ -83,29 +83,6 @@ module Lib_deps : sig val decode : allow_re_export:bool -> t Dune_lang.Decoder.t end -module Dep_conf : sig - type t = - | File of String_with_vars.t - | Alias of String_with_vars.t - | Alias_rec of String_with_vars.t - | Glob_files of String_with_vars.t - | Source_tree of String_with_vars.t - | Package of String_with_vars.t - | Universe - | Env_var of String_with_vars.t - (* [Sandbox_config] is a way to declare that your action also depends on - there being a clean filesystem around its deps. (or, if you require - [no_sandboxing], it's that your action depends on something undeclared - (e.g. absolute path of cwd) and you want to allow it) *) - | Sandbox_config of Sandbox_config.t - - val remove_locs : t -> t - - include Dune_lang.Conv.S with type t := t - - val to_dyn : t Dyn.Encoder.t -end - (** [preprocess] and [preprocessor_deps] fields *) val preprocess_fields : (Preprocess_map.t * Dep_conf.t list) Dune_lang.Decoder.fields_parser @@ -116,9 +93,8 @@ module Buildable : sig ; modules : Ordered_set_lang.t ; modules_without_implementation : Ordered_set_lang.t ; libraries : Lib_dep.t list - ; c_flags : Ordered_set_lang.Unexpanded.t C.Kind.Dict.t - ; c_names : Ordered_set_lang.t option - ; cxx_names : Ordered_set_lang.t option + ; foreign_archives : (Loc.t * string) list + ; foreign_stubs : Foreign.Stubs.t list ; preprocess : Preprocess_map.t ; preprocessor_deps : Dep_conf.t list ; lint : Lint.t @@ -200,8 +176,11 @@ module Library : sig ; modes : Mode_conf.Set.t ; kind : Lib_kind.t ; library_flags : Ordered_set_lang.Unexpanded.t + (* TODO_AM: Maybe [c_library_flags] should be a part of + [foreign_library] declaration? This is used to pass a flag like + [-lgzip] when linking with the [gzip_stubs.a] C library. *) + (* TODO_AM: rename to "foreign_library_flags". *) ; c_library_flags : Ordered_set_lang.Unexpanded.t - ; self_build_stubs_archive : string option ; virtual_deps : (Loc.t * Lib_name.t) list ; wrapped : Wrapped.t Lib_info.Inherited.t ; optional : bool @@ -223,13 +202,21 @@ module Library : sig val has_stubs : t -> bool - val stubs_name : t -> string + val default_archive_name : t -> string + + val default_lib_file : + t -> dir:Path.Build.t -> ext_lib:string -> Path.Build.t + + val default_dll_file : + t -> dir:Path.Build.t -> ext_dll:string -> Path.Build.t + + val archive_names : t -> string list - val stubs : t -> dir:Path.Build.t -> Path.Build.t + val lib_files : t -> dir:Path.Build.t -> ext_lib:string -> Path.Build.t list - val stubs_archive : t -> dir:Path.Build.t -> ext_lib:string -> Path.Build.t + val dll_files : t -> dir:Path.Build.t -> ext_dll:string -> Path.Build.t list - val dll : t -> dir:Path.Build.t -> ext_dll:string -> Path.Build.t + val stubs_path : t -> dir:Path.Build.t -> Path.Build.t val archive : t -> dir:Path.Build.t -> ext:string -> Path.Build.t @@ -482,6 +469,7 @@ end type Stanza.t += | Library of Library.t + | Foreign_library of Foreign.Library.t | Executables of Executables.t | Rule of Rule.t | Install of File_binding.Unexpanded.t Install_conf.t diff --git a/src/dune/env_node.ml b/src/dune/env_node.ml index e482b9537771..fd59c1745abc 100644 --- a/src/dune/env_node.ml +++ b/src/dune/env_node.ml @@ -7,7 +7,7 @@ type t = ; config : Dune_env.Stanza.t ; mutable local_binaries : File_binding.Expanded.t list option ; mutable ocaml_flags : Ocaml_flags.t option - ; mutable c_flags : string list Build.t C.Kind.Dict.t option + ; mutable c_flags : string list Build.t Foreign.Language.Dict.t option ; mutable external_ : Env.t option ; mutable bin_artifacts : Artifacts.Bin.t option ; mutable inline_tests : Dune_env.Stanza.Inline_tests.t option @@ -61,7 +61,7 @@ let rec external_ t ~profile ~default = in let env, have_binaries = let cfg = find_config t ~profile in - (Env.extend_env default cfg.env_vars, not (List.is_empty cfg.binaries)) + (Env.extend_env default cfg.env_vars, List.is_non_empty cfg.binaries) in let env = if have_binaries then @@ -135,14 +135,14 @@ let rec c_flags t ~profile ~expander ~default_context_flags = | None -> let default = match t.inherit_from with - | None -> C.Kind.Dict.map ~f:Build.return default_context_flags + | None -> Foreign.Language.Dict.map ~f:Build.return default_context_flags | Some (lazy t) -> c_flags t ~profile ~expander ~default_context_flags in let flags = let cfg = find_config t ~profile in let expander = Expander.set_dir expander ~dir:t.dir in - C.Kind.Dict.mapi cfg.c_flags ~f:(fun ~kind f -> - let default = C.Kind.Dict.get default kind in + Foreign.Language.Dict.mapi cfg.c_flags ~f:(fun ~language f -> + let default = Foreign.Language.Dict.get default language in Expander.expand_and_eval_set expander f ~standard:default) in t.c_flags <- Some flags; diff --git a/src/dune/env_node.mli b/src/dune/env_node.mli index 8183b62bd8eb..c73dc75765dc 100644 --- a/src/dune/env_node.mli +++ b/src/dune/env_node.mli @@ -24,8 +24,8 @@ val c_flags : t -> profile:Profile.t -> expander:Expander.t - -> default_context_flags:string list C.Kind.Dict.t - -> string list Build.t C.Kind.Dict.t + -> default_context_flags:string list Foreign.Language.Dict.t + -> string list Build.t Foreign.Language.Dict.t val local_binaries : t -> profile:Profile.t -> expander:Expander.t -> File_binding.Expanded.t list diff --git a/src/dune/exe_rules.ml b/src/dune/exe_rules.ml index 59fec846488f..1407dfe7c2e6 100644 --- a/src/dune/exe_rules.ml +++ b/src/dune/exe_rules.ml @@ -115,12 +115,13 @@ let executables_rules ~sctx ~dir ~expander ~dir_contents ~scope ~compile_info [ Pp.textf "Pure bytecode executables cannot contain C stubs." ; Pp.textf "Did you forget to add `(modes exe)'?" ]; - let c_sources = - Dir_contents.c_sources_of_executables dir_contents ~first_exe + let foreign_sources = + Dir_contents.foreign_sources_of_executables dir_contents ~first_exe in let o_files = - C_rules.build_o_files exes.buildable ~sctx ~dir ~expander - ~requires:requires_compile ~dir_contents ~c_sources + Foreign_rules.build_o_files ~sctx ~dir ~expander + ~requires:requires_compile ~dir_contents ~foreign_sources + ~extra_flags:Command.Args.empty ~extra_deps:[] |> List.map ~f:Path.build in Check_rules.add_files sctx ~dir o_files; diff --git a/src/dune/expander.ml b/src/dune/expander.ml index e3da046b7556..9457a85d3dd5 100644 --- a/src/dune/expander.ml +++ b/src/dune/expander.ml @@ -307,9 +307,9 @@ type expansion_kind = | Dynamic | Static -let cc_of_c_flags t (cc : string list Build.t C.Kind.Dict.t) = +let cc_of_c_flags t (cc : string list Build.t Foreign.Language.Dict.t) = let open Build.O in - C.Kind.Dict.map cc ~f:(fun cc -> + Foreign.Language.Dict.map cc ~f:(fun cc -> let+ flags = cc in Value.L.strings (t.c_compiler :: flags)) @@ -325,7 +325,7 @@ let cannot_be_used_here pform = let expand_and_record acc ~map_exe ~dep_kind ~expansion_kind ~(dir : Path.Build.t) ~pform t expansion - ~(cc : dir:Path.Build.t -> Value.t list Build.t C.Kind.Dict.t) = + ~(cc : dir:Path.Build.t -> Value.t list Build.t Foreign.Language.Dict.t) = let key = String_with_vars.Var.full_name pform in let loc = String_with_vars.Var.loc pform in let relative d s = Path.build (Path.Build.relative ~error_loc:loc d s) in @@ -504,7 +504,8 @@ let expand_no_ddeps acc ~dir ~dep_kind ~map_exe ~expand_var ~cc t pform Option.map res ~f:static let gen_with_record_deps ~expand t resolved_forms ~dep_kind ~map_exe - ~(c_flags : dir:Path.Build.t -> string list Build.t C.Kind.Dict.t) = + ~(c_flags : + dir:Path.Build.t -> string list Build.t Foreign.Language.Dict.t) = let cc ~dir = cc_of_c_flags t (c_flags ~dir) in let expand_var = expand diff --git a/src/dune/expander.mli b/src/dune/expander.mli index 242dcfd1916c..bf0b0c1817cb 100644 --- a/src/dune/expander.mli +++ b/src/dune/expander.mli @@ -108,7 +108,7 @@ val with_record_deps : -> targets_written_by_user:Targets.t -> dep_kind:Lib_deps_info.Kind.t -> map_exe:(Path.t -> Path.t) - -> c_flags:(dir:Path.Build.t -> string list Build.t C.Kind.Dict.t) + -> c_flags:(dir:Path.Build.t -> string list Build.t Foreign.Language.Dict.t) -> t val with_record_no_ddeps : @@ -116,7 +116,7 @@ val with_record_no_ddeps : -> Resolved_forms.t -> dep_kind:Lib_deps_info.Kind.t -> map_exe:(Path.t -> Path.t) - -> c_flags:(dir:Path.Build.t -> string list Build.t C.Kind.Dict.t) + -> c_flags:(dir:Path.Build.t -> string list Build.t Foreign.Language.Dict.t) -> t val add_ddeps_and_bindings : diff --git a/src/dune/findlib/findlib.ml b/src/dune/findlib/findlib.ml index 581fd14047e7..f15b839dfb13 100644 --- a/src/dune/findlib/findlib.ml +++ b/src/dune/findlib/findlib.ml @@ -235,9 +235,7 @@ module Package = struct let modes : Mode.Dict.Set.t = (* libraries without archives are compatible with all modes. mainly a hack for compiler-libs which doesn't have any archives *) - let discovered = - Mode.Dict.map ~f:(fun x -> not (List.is_empty x)) archives - in + let discovered = Mode.Dict.map ~f:List.is_non_empty archives in if Mode.Dict.Set.is_empty discovered then Mode.Dict.Set.all else diff --git a/src/dune/foreign.ml b/src/dune/foreign.ml new file mode 100644 index 000000000000..dc3557e8327b --- /dev/null +++ b/src/dune/foreign.ml @@ -0,0 +1,256 @@ +open Stdune + +let header_ext = ".h" + +module Language = struct + module T = struct + type t = + | C + | Cxx + + let compare x y = + match (x, y) with + | C, C -> Eq + | C, _ -> Lt + | _, C -> Gt + | Cxx, Cxx -> Eq + + let equal x y = + match (x, y) with + | C, C -> true + | Cxx, Cxx -> true + | _, _ -> false + + let to_dyn = function + | C -> Dyn.Variant ("C", []) + | Cxx -> Dyn.Variant ("Cxx", []) + end + + include T + + let proper_name = function + | C -> "C" + | Cxx -> "C++" + + let encode = function + | C -> "c" + | Cxx -> "cxx" + + let decode = Dune_lang.Decoder.enum [ (encode C, C); (encode Cxx, Cxx) ] + + type split = + | Unrecognized + | Not_allowed_until of Dune_lang.Syntax.Version.t + | Recognized of string * t + + let cxx_version_introduced ~obj ~dune_version ~version_introduced = + if dune_version >= version_introduced then + Recognized (obj, Cxx) + else + Not_allowed_until version_introduced + + let split_extension fn ~dune_version = + match String.rsplit2 fn ~on:'.' with + | Some (obj, "c") -> Recognized (obj, C) + | Some (obj, "cpp") -> Recognized (obj, Cxx) + | Some (obj, "cxx") -> + cxx_version_introduced ~obj ~dune_version ~version_introduced:(1, 8) + | Some (obj, "cc") -> + cxx_version_introduced ~obj ~dune_version ~version_introduced:(1, 10) + | _ -> Unrecognized + + let possible_exts ~dune_version = function + | C -> [ ".c" ] + | Cxx -> + let exts = [ ".cpp" ] in + let exts = + if dune_version >= (1, 10) then + ".cc" :: exts + else + exts + in + if dune_version >= (1, 8) then + ".cxx" :: exts + else + exts + + let possible_fns t fn ~dune_version = + possible_exts t ~dune_version |> List.map ~f:(fun ext -> fn ^ ext) + + include Comparable.Make (T) + + module Dict = struct + type 'a t = + { c : 'a + ; cxx : 'a + } + + let c t = t.c + + let cxx t = t.cxx + + let map { c; cxx } ~f = { c = f c; cxx = f cxx } + + let mapi { c; cxx } ~f = { c = f ~language:C c; cxx = f ~language:Cxx cxx } + + let make_both a = { c = a; cxx = a } + + let make ~c ~cxx = { c; cxx } + + let get { c; cxx } = function + | C -> c + | Cxx -> cxx + + let add t k v = + match k with + | C -> { t with c = v } + | Cxx -> { t with cxx = v } + + let update t k ~f = + let v = get t k in + add t k (f v) + + let merge t1 t2 ~f = { c = f t1.c t2.c; cxx = f t1.cxx t2.cxx } + end +end + +let all_possible_exts = + let exts = Language.possible_exts ~dune_version:Stanza.latest_version in + (header_ext :: exts C) @ exts Cxx + +let c_cxx_or_header ~fn = + let ext = Filename.extension fn in + List.mem ~set:all_possible_exts ext + +module Stubs = struct + type t = + { loc : Loc.t + ; language : Language.t + ; names : Ordered_set_lang.t + ; flags : Ordered_set_lang.Unexpanded.t + ; include_dirs : Loc.t * Ordered_set_lang.Unexpanded.t + ; extra_deps : Dep_conf.t list + } + + let make ~loc ~language ~names ~flags = + { loc + ; language + ; names + ; flags + ; include_dirs = (Loc.none, Ordered_set_lang.Unexpanded.standard) + ; extra_deps = [] + } + + let decode_stubs = + let open Dune_lang.Decoder in + let+ loc = loc + and+ loc_archive_name, archive_name = + located (field_o "archive_name" string) + and+ language = field "language" Language.decode + and+ names = field "names" Ordered_set_lang.decode + and+ flags = Ordered_set_lang.Unexpanded.field "flags" + and+ include_dirs = + located (Ordered_set_lang.Unexpanded.field "include_dirs") + and+ extra_deps = field_o "extra_deps" (repeat Dep_conf.decode) in + let extra_deps = Option.value ~default:[] extra_deps in + let () = + match archive_name with + | None -> () + | Some _ -> + User_error.raise ~loc:loc_archive_name + [ Pp.textf + "The field \"archive_name\" is disallowed in the (foreign_stubs \ + ...) stanza. For named foreign archives use the \ + (foreign_library ...) stanza." + ] + in + { loc; language; names; flags; include_dirs; extra_deps } + + let decode = Dune_lang.Decoder.fields decode_stubs +end + +module Library = struct + type t = + { archive_name : string + ; stubs : Stubs.t + } + + let decode = + let open Dune_lang.Decoder in + fields + (let+ archive_name = field "archive_name" string + and+ stubs = Stubs.decode_stubs in + { archive_name; stubs }) +end + +let lib_file ~archive_name ~dir ~ext_lib = + Path.Build.relative dir (sprintf "lib%s%s" archive_name ext_lib) + +let dll_file ~archive_name ~dir ~ext_dll = + Path.Build.relative dir (sprintf "dll%s%s" archive_name ext_dll) + +module Source = struct + type t = + { stubs : Stubs.t + ; path : Path.Build.t + } + + let language t = t.stubs.language + + let flags t = t.stubs.flags + + let path t = t.path + + let make ~stubs ~path = { stubs; path } +end + +module Object_map = struct + type t = Path.Build.t Language.Map.t String.Map.t + + let to_dyn t = String.Map.to_dyn (Language.Map.to_dyn Path.Build.to_dyn) t + + let load ~dune_version ~dir ~files = + let init = String.Map.empty in + String.Set.fold files ~init ~f:(fun fn acc -> + match Language.split_extension fn ~dune_version with + | Unrecognized -> acc + | Not_allowed_until version -> + let loc = Loc.in_dir (Path.build dir) in + User_error.raise ~loc + [ Pp.textf + "The extension %s of the source file %S is not supported \ + until version %s." + (Filename.extension fn) fn + (Dune_lang.Syntax.Version.to_string version) + ] + | Recognized (obj, language) -> + let path = Path.Build.relative dir fn in + String.Map.update acc obj ~f:(function + | None -> Some (Language.Map.singleton language path) + | Some map -> + Some + (Language.Map.update map language ~f:(function + | None -> Some path + | Some existing_path -> + let loc = Loc.in_dir (Path.build dir) in + User_error.raise ~loc + [ Pp.textf + "Multiple %s sources for the same object name %S:" + (Language.proper_name language) + obj + ; Pp.enumerate [ existing_path; path ] ~f:(fun path -> + Pp.text + (Path.to_string_maybe_quoted + (Path.drop_optional_build_context + (Path.build path)))) + ; Pp.text "This is not allowed; please rename them." + ])))) +end + +module Sources = struct + type t = (Loc.t * Source.t) String.Map.t + + let objects t ~dir ~ext_obj = + String.Map.keys t + |> List.map ~f:(fun c -> Path.Build.relative dir (c ^ ext_obj)) +end diff --git a/src/dune/foreign.mli b/src/dune/foreign.mli new file mode 100644 index 000000000000..2e3aedb0dc7f --- /dev/null +++ b/src/dune/foreign.mli @@ -0,0 +1,172 @@ +open Stdune + +val header_ext : string + +val c_cxx_or_header : fn:string -> bool + +module Language : sig + type t = + | C + | Cxx + + val compare : t -> t -> ordering + + val equal : t -> t -> bool + + val to_dyn : t -> Dyn.t + + (** The proper name of a language, e.g. "C++" for [Cxx]. Useful for + diagnostic messages. *) + val proper_name : t -> string + + (** The string used to encode a language in Dune files, e.g. "cxx" for [Cxx]. *) + val encode : t -> string + + val decode : t Dune_lang.Decoder.t + + type split = + | Unrecognized + | Not_allowed_until of Dune_lang.Syntax.Version.t + | Recognized of string * t + + val split_extension : + string -> dune_version:Dune_lang.Syntax.Version.t -> split + + (** [possible_fns t s] returns the possible filenames given the + extension-less basenames [s] *) + val possible_fns : + t -> string -> dune_version:Dune_lang.Syntax.Version.t -> string list + + module Map : sig + include Map.S with type key = t + end + + module Dict : sig + type language + + type 'a t = + { c : 'a + ; cxx : 'a + } + + val c : 'a t -> 'a + + val cxx : 'a t -> 'a + + val map : 'a t -> f:('a -> 'b) -> 'b t + + val mapi : 'a t -> f:(language:language -> 'a -> 'b) -> 'b t + + val make_both : 'a -> 'a t + + val make : c:'a -> cxx:'a -> 'a t + + val update : 'a t -> language -> f:('a -> 'a) -> 'a t + + val merge : 'a t -> 'b t -> f:('a -> 'b -> 'c) -> 'c t + + val get : 'a t -> language -> 'a + end + with type language := t +end + +(** A type of foreign library "stubs", which is almost like the [Library] type + but doesn't include the [archive_name] field. The type is parsed as an + optional [foreign_stubs] section of the [library] stanza, or as part of the + top-level [foreign_library] declaration. *) +module Stubs : sig + type t = + { loc : Loc.t + ; language : Language.t + ; names : Ordered_set_lang.t + ; flags : Ordered_set_lang.Unexpanded.t + ; include_dirs : Loc.t * Ordered_set_lang.Unexpanded.t + ; extra_deps : Dep_conf.t list + } + + (** Construct foreign library stubs with some fields set to default values. *) + val make : + loc:Loc.t + -> language:Language.t + -> names:Ordered_set_lang.t + -> flags:Ordered_set_lang.Unexpanded.t + -> t + + val decode : t Dune_lang.Decoder.t +end + +(** Foreign libraries. + + This data type represents the contents of the top-level stanza + [foreign_library]. + + The fields have the following semantics. + + [language] selects the compiler. At the moment, we support only [c] and + [cxx] settings, but in future other languages/compilers could be supported, + e.g. Rust and Clang. + + [archive_name] is the name of the resulting [.a] archive file. This field + can be omitted in [foreign_stubs] in which case a valid name is generated + automatically. To support mixing different languages in the same foreign + library, we allow the same [archive_name] in different [foreign_library] + stanzas, as long as there are no conflicting object names. + + [names] are names of source files. The full paths to the files are + determined by scanning package directories. Duplicate file names are + disallowed to avoid conflicting object names in the resulting archive file. + + [flags] are passed when compiling source files. + + [include_dirs] are tracked as dependencies and passed to the compiler via + the "-I" flag. + + [extra_deps] are tracked as dependencies. *) +module Library : sig + type t = + { archive_name : string + ; stubs : Stubs.t + } + + val decode : t Dune_lang.Decoder.t +end + +val lib_file : + archive_name:string -> dir:Path.Build.t -> ext_lib:string -> Path.Build.t + +val dll_file : + archive_name:string -> dir:Path.Build.t -> ext_dll:string -> Path.Build.t + +module Source : sig + type t + + val language : t -> Language.t + + val flags : t -> Ordered_set_lang.Unexpanded.t + + val path : t -> Path.Build.t + + val make : stubs:Stubs.t -> path:Path.Build.t -> t +end + +(** A map from object names to the corresponding sources. *) +module Object_map : sig + type t = Path.Build.t Language.Map.t String.Map.t + + val to_dyn : t -> Dyn.t + + (** [load ~dir ~files] loads foreign sources in [dir] into a two-layer map + whose first layer is keyed by the object name and the second layer is + keyed by the language of the source. *) + val load : + dune_version:Dune_lang.Syntax.Version.t + -> dir:Path.Build.t + -> files:String.Set.t + -> t +end + +module Sources : sig + type t = (Loc.t * Source.t) String.Map.t + + val objects : t -> dir:Path.Build.t -> ext_obj:string -> Path.Build.t list +end diff --git a/src/dune/c_rules.ml b/src/dune/foreign_rules.ml similarity index 57% rename from src/dune/c_rules.ml rename to src/dune/foreign_rules.ml index 2db48d78d28e..9ed978d44df2 100644 --- a/src/dune/c_rules.ml +++ b/src/dune/foreign_rules.ml @@ -1,24 +1,24 @@ open! Stdune -open Dune_file -let build_c_file (buildable : Buildable.t) ~sctx ~dir ~expander ~includes - (loc, src, dst) = +let build_c_file ~sctx ~dir ~expander ~include_flags (loc, src, dst) = + let flags = Foreign.Source.flags src in let ctx = Super_context.context sctx in let c_flags = - (Super_context.c_flags sctx ~dir ~expander ~flags:buildable.c_flags).c + Super_context.foreign_flags sctx ~dir ~expander ~flags + ~language:Foreign.Language.C in Super_context.add_rule sctx ~loc ~dir (* With sandboxing we get errors like: bar.c:2:19: fatal error: foo.cxx: No such file or directory #include "foo.cxx" *) ~sandbox:Sandbox_config.no_sandboxing - (let src = Path.build (C.Source.path src) in + (let src = Path.build (Foreign.Source.path src) in Command.run (* We have to execute the rule in the library directory as the .o is produced in the current directory *) ~dir:(Path.build dir) (Ok ctx.ocamlc) [ A "-g" - ; includes + ; include_flags ; Dyn (Build.map c_flags ~f:(fun x -> Command.quote_args "-ccopt" x)) ; A "-o" ; Target dst @@ -26,8 +26,8 @@ let build_c_file (buildable : Buildable.t) ~sctx ~dir ~expander ~includes ]); dst -let build_cxx_file (buildable : Buildable.t) ~sctx ~dir ~expander ~includes - (loc, src, dst) = +let build_cxx_file ~sctx ~dir ~expander ~include_flags (loc, src, dst) = + let flags = Foreign.Source.flags src in let ctx = Super_context.context sctx in let output_param = if ctx.ccomp_type = "msvc" then @@ -36,34 +36,38 @@ let build_cxx_file (buildable : Buildable.t) ~sctx ~dir ~expander ~includes [ A "-o"; Target dst ] in let cxx_flags = - (Super_context.c_flags sctx ~dir ~expander ~flags:buildable.c_flags).cxx + Super_context.foreign_flags sctx ~dir ~expander ~flags + ~language:Foreign.Language.Cxx in Super_context.add_rule sctx ~loc ~dir (* this seems to work with sandboxing, but for symmetry with [build_c_file] disabling that here too *) ~sandbox:Sandbox_config.no_sandboxing - (let src = Path.build (C.Source.path src) in + (let src = Path.build (Foreign.Source.path src) in Command.run (* We have to execute the rule in the library directory as the .o is produced in the current directory *) ~dir:(Path.build dir) (Super_context.resolve_program ~loc:None ~dir sctx ctx.c_compiler) ( [ Command.Args.S [ A "-I"; Path ctx.stdlib_dir ] - ; includes + ; include_flags ; Command.Args.dyn cxx_flags ] @ output_param @ [ A "-c"; Dep src ] )); dst -let build_o_files buildable ~sctx ~(c_sources : C.Sources.t) ~dir ~expander - ~requires ~dir_contents = +(* TODO: [requires] is a confusing name, probably because it's too general: it + looks like it's a list of libraries we depend on. *) +let build_o_files ~sctx ~foreign_sources ~(dir : Path.Build.t) ~expander + ~requires ~dir_contents ?extra_flags ~(extra_deps : Dep_conf.t list) = + let extra_flags = Option.value ~default:Command.Args.empty extra_flags in let ctx = Super_context.context sctx in let all_dirs = Dir_contents.dirs dir_contents in let h_files = List.fold_left all_dirs ~init:[] ~f:(fun acc dc -> String.Set.fold (Dir_contents.text_files dc) ~init:acc ~f:(fun fn acc -> - if String.is_suffix fn ~suffix:C.header_ext then + if String.is_suffix fn ~suffix:Foreign.header_ext then Path.relative (Path.build (Dir_contents.dir dc)) fn :: acc else acc)) @@ -80,11 +84,20 @@ let build_o_files buildable ~sctx ~(c_sources : C.Sources.t) ~dir ~expander ]) ] in - let build_x_files build_x files = - String.Map.to_list files - |> List.map ~f:(fun (obj, (loc, src)) -> - let dst = Path.Build.relative dir (obj ^ ctx.lib_config.ext_obj) in - build_x buildable ~sctx ~dir ~expander ~includes (loc, src, dst)) + let extra_deps = + let open Build.O in + let+ () = Super_context.Deps.interpret sctx extra_deps ~expander in + Command.Args.empty in - let { C.Kind.Dict.c; cxx } = C.Sources.split_by_kind c_sources in - build_x_files build_c_file c @ build_x_files build_cxx_file cxx + let include_flags = + Command.Args.S [ includes; extra_flags; Dyn extra_deps ] + in + String.Map.to_list foreign_sources + |> List.map ~f:(fun (obj, (loc, src)) -> + let dst = Path.Build.relative dir (obj ^ ctx.lib_config.ext_obj) in + let build_file = + match Foreign.Source.language src with + | C -> build_c_file + | Cxx -> build_cxx_file + in + build_file ~sctx ~dir ~expander ~include_flags (loc, src, dst)) diff --git a/src/dune/c_rules.mli b/src/dune/foreign_rules.mli similarity index 54% rename from src/dune/c_rules.mli rename to src/dune/foreign_rules.mli index adf5fa2a9b75..df2fcd05ac39 100644 --- a/src/dune/c_rules.mli +++ b/src/dune/foreign_rules.mli @@ -1,13 +1,13 @@ open! Stdune open Import -open Dune_file val build_o_files : - Buildable.t - -> sctx:Super_context.t - -> c_sources:C.Sources.t + sctx:Super_context.t + -> foreign_sources:Foreign.Sources.t -> dir:Path.Build.t -> expander:Expander.t -> requires:Lib.L.t Or_exn.t -> dir_contents:Dir_contents.t + -> ?extra_flags:Command.Args.dynamic Command.Args.t + -> extra_deps:Dep_conf.t list -> Path.Build.t list diff --git a/src/dune/foreign_sources.ml b/src/dune/foreign_sources.ml new file mode 100644 index 000000000000..f6ed3b7eb1d6 --- /dev/null +++ b/src/dune/foreign_sources.ml @@ -0,0 +1,150 @@ +open Stdune +open Dune_file +module Library = Dune_file.Library + +type t = + { libraries : Foreign.Sources.t Lib_name.Map.t + ; foreign_libraries : Foreign.Sources.t String.Map.t + ; executables : Foreign.Sources.t String.Map.t + } + +let for_lib t ~name = Lib_name.Map.find_exn t.libraries name + +let for_foreign_lib t ~archive_name = + String.Map.find_exn t.foreign_libraries archive_name + +let for_exes t ~first_exe = String.Map.find_exn t.executables first_exe + +let empty = + { libraries = Lib_name.Map.empty + ; foreign_libraries = String.Map.empty + ; executables = String.Map.empty + } + +let valid_name language ~loc s = + match s with + | "" + | "." + | ".." -> + User_error.raise ~loc + [ Pp.textf "%S is not a valid %s name." s + (Foreign.Language.proper_name language) + ] + | _ -> s + +let eval_foreign_sources (d : _ Dir_with_dune.t) foreign_stubs + ~(object_map : Foreign.Object_map.t) : Foreign.Sources.t = + let eval (stubs : Foreign.Stubs.t) = + let language = stubs.language in + let osl = stubs.names in + Ordered_set_lang.Unordered_string.eval_loc osl + ~key:(fun x -> x) + ~parse:(fun ~loc s -> + let s = valid_name language ~loc s in + let s' = Filename.basename s in + if s' <> s then + User_error.raise ~loc + [ Pp.text + "relative part of stub is not necessary and should be \ + removed. To include sources in subdirectories, use the \ + include_subdirs stanza" + ]; + s') + ~standard:String.Map.empty + |> String.Map.map ~f:(fun (loc, s) -> + match + let open Option.O in + let* map = String.Map.find object_map s in + let+ path = Foreign.Language.Map.find map language in + (loc, Foreign.Source.make ~stubs ~path) + with + | Some x -> x + | None -> + let dune_version = d.dune_version in + User_error.raise ~loc + [ Pp.textf "Object %S has no source; %s must be present." s + (String.enumerate_one_of + ( Foreign.Language.possible_fns language s ~dune_version + |> List.map ~f:(fun s -> "\"" ^ s ^ "\"") )) + ]) + in + let stub_maps = List.map foreign_stubs ~f:eval in + List.fold_left stub_maps ~init:String.Map.empty ~f:(fun a b -> + String.Map.union a b ~f:(fun name (loc, src) (_, another_src) -> + let path src = + Path.to_string_maybe_quoted + (Path.drop_optional_build_context + (Path.build (Foreign.Source.path src))) + in + User_error.raise ~loc + [ Pp.textf + "%S and %S map to the same object name %S. This is not \ + allowed; please rename them." + (path another_src) (path src) name + ])) + +let make (d : _ Dir_with_dune.t) ~(object_map : Foreign.Object_map.t) = + let libs, exes = + List.filter_partition_map d.data ~f:(fun stanza -> + match (stanza : Stanza.t) with + | Library lib -> + let all = + eval_foreign_sources d lib.buildable.foreign_stubs ~object_map + in + Left (Left (lib, all)) + | Foreign_library library -> + let all = eval_foreign_sources d [ library.stubs ] ~object_map in + Left (Right (library.archive_name, (library.stubs.loc, all))) + | Executables exes -> + let all = + eval_foreign_sources d exes.buildable.foreign_stubs ~object_map + in + Right (exes, all) + | _ -> Skip) + in + let libs, foreign_libs = List.partition_map libs ~f:Fn.id in + let libraries = + match + Lib_name.Map.of_list_map libs ~f:(fun (lib, m) -> + (Library.best_name lib, m)) + with + | Ok x -> x + | Error (name, _, (lib2, _)) -> + User_error.raise ~loc:lib2.buildable.loc + [ Pp.textf "Library %S appears for the second time in this directory" + (Lib_name.to_string name) + ] + in + let foreign_libraries = + String.Map.of_list_reducei foreign_libs + ~f:(fun archive_name (loc1, _) (loc2, _) -> + User_error.raise ~loc:loc2 + [ Pp.textf + "Multiple foreign libraries with the same archive name %S; the \ + name has already been taken in %s." + archive_name + (Loc.to_file_colon_line loc1) + ]) + |> String.Map.map ~f:snd + in + let executables = + String.Map.of_list_map_exn exes ~f:(fun (exes, m) -> + (snd (List.hd exes.names), m)) + in + let () = + let rev_map = + List.concat_map libs ~f:(fun (_, c_sources) -> + String.Map.values c_sources + |> List.map ~f:(fun (loc, source) -> + (Foreign.Source.path source, loc))) + |> Path.Build.Map.of_list + in + match rev_map with + | Ok _ -> () + | Error (_, loc1, loc2) -> + User_error.raise ~loc:loc2 + [ Pp.text "This c stub is already used in another stanza:" + ; Pp.textf "- %s" (Loc.to_file_colon_line loc1) + ] + in + { libraries; foreign_libraries; executables } diff --git a/src/dune/foreign_sources.mli b/src/dune/foreign_sources.mli new file mode 100644 index 000000000000..03d47b54ac67 --- /dev/null +++ b/src/dune/foreign_sources.mli @@ -0,0 +1,15 @@ +(** This module loads and validates foreign sources from directories. *) + +type t + +val empty : t + +val for_lib : t -> name:Lib_name.t -> Foreign.Sources.t + +val for_foreign_lib : t -> archive_name:string -> Foreign.Sources.t + +val for_exes : t -> first_exe:string -> Foreign.Sources.t + +(** [make stanzas ~c_sources] loads and validates foreign sources. *) +val make : + Stanza.t list Dir_with_dune.t -> object_map:Foreign.Object_map.t -> t diff --git a/src/dune/gen_rules.ml b/src/dune/gen_rules.ml index 08cbeedf83f7..59b3230fc3fe 100644 --- a/src/dune/gen_rules.ml +++ b/src/dune/gen_rules.ml @@ -79,6 +79,9 @@ end = struct ; js = None ; source_dirs = None } + | Foreign_library lib -> + Lib_rules.build_foreign_library lib ~sctx ~dir ~dir_contents ~expander; + empty_none | Executables exes -> Option.iter exes.install_conf ~f:files_to_install; let cctx, merlin = diff --git a/src/dune/inline_tests.ml b/src/dune/inline_tests.ml index 4c8470c13e6e..7d4e1d4bfeb5 100644 --- a/src/dune/inline_tests.ml +++ b/src/dune/inline_tests.ml @@ -1,6 +1,5 @@ open! Stdune open Import -open Dune_file open Build.O open! No_io module SC = Super_context @@ -356,7 +355,7 @@ include Sub_system.Register_end_point (struct in let exe, runner_args = match custom_runner with - | None -> (Ok exe, Command.Args.As []) + | None -> (Ok exe, Command.Args.empty) | Some runner -> ( Super_context.resolve_program ~dir sctx ~loc:(Some loc) runner , Dep exe ) diff --git a/src/dune/install_rules.ml b/src/dune/install_rules.ml index 9c537cdb95b2..1a670037a2b2 100644 --- a/src/dune/install_rules.ml +++ b/src/dune/install_rules.ml @@ -119,9 +119,9 @@ end = struct | Private, Some dir -> Some (Filename.concat dir ".private") in make_entry ?sub_dir Lib file) - ; List.map (Lib_archives.files archives) ~f:(make_entry Lib) + ; List.map (Lib_archives.lib_files archives) ~f:(make_entry Lib) ; List.map execs ~f:(make_entry Libexec) - ; List.map (Lib_archives.dlls archives) ~f:(fun a -> + ; List.map (Lib_archives.dll_files archives) ~f:(fun a -> (Some loc, Install.Entry.make Stublibs a)) ] @@ -292,8 +292,9 @@ let gen_dune_package sctx pkg = let name = Lib.name lib in let foreign_objects = let dir = Obj_dir.obj_dir obj_dir in - Dir_contents.c_sources_of_library dir_contents ~name - |> C.Sources.objects ~dir ~ext_obj:ctx.lib_config.ext_obj + Dir_contents.foreign_sources_of_library dir_contents ~name + |> Foreign.Sources.objects ~dir + ~ext_obj:ctx.lib_config.ext_obj |> List.map ~f:Path.build in let modules = diff --git a/src/dune/lib_archives.ml b/src/dune/lib_archives.ml index 98024d745795..a79d66064600 100644 --- a/src/dune/lib_archives.ml +++ b/src/dune/lib_archives.ml @@ -1,13 +1,13 @@ open Stdune type t = - { dlls : Path.Build.t list - ; files : Path.Build.t list + { dll_files : Path.Build.t list + ; lib_files : Path.Build.t list } -let files t = t.files +let lib_files t = t.lib_files -let dlls t = t.dlls +let dll_files t = t.dll_files module Library = Dune_file.Library @@ -24,7 +24,7 @@ let make ~(ctx : Context.t) ~dir ~dir_contents (lib : Library.t) = else [] in - let files = + let lib_files = let virtual_library = Library.is_virtual lib in List.concat [ if_ @@ -32,14 +32,12 @@ let make ~(ctx : Context.t) ~dir ~dir_contents (lib : Library.t) = [ Library.archive ~dir lib ~ext:(Mode.compiled_lib_ext Byte) ] ; ( if virtual_library then let files = - Dir_contents.c_sources_of_library dir_contents + Dir_contents.foreign_sources_of_library dir_contents ~name:(Library.best_name lib) in - C.Sources.objects files ~dir ~ext_obj - else if Library.has_stubs lib then - [ Library.stubs_archive ~dir lib ~ext_lib ] + Foreign.Sources.objects files ~dir ~ext_obj else - [] ) + Library.lib_files lib ~dir ~ext_lib ) ; if_ (native && not virtual_library) (let files = @@ -57,13 +55,12 @@ let make ~(ctx : Context.t) ~dir ~dir_contents (lib : Library.t) = ; List.map lib.buildable.js_of_ocaml.javascript_files ~f:(Path.Build.relative dir) ; List.map lib.install_c_headers ~f:(fun fn -> - Path.Build.relative dir (fn ^ C.header_ext)) + Path.Build.relative dir (fn ^ Foreign.header_ext)) ] in - let dlls = + let dll_files = if_ - ( byte && Library.has_stubs lib - && Dynlink_supported.get lib.dynlink ctx.supports_shared_libraries ) - [ Library.dll ~dir lib ~ext_dll ] + (byte && Dynlink_supported.get lib.dynlink ctx.supports_shared_libraries) + (Library.dll_files lib ~dir ~ext_dll) in - { files; dlls } + { lib_files; dll_files } diff --git a/src/dune/lib_archives.mli b/src/dune/lib_archives.mli index 0ba3f20b2f94..c8508bf9150f 100644 --- a/src/dune/lib_archives.mli +++ b/src/dune/lib_archives.mli @@ -9,6 +9,6 @@ val make : -> Dune_file.Library.t -> t -val files : t -> Path.Build.t list +val lib_files : t -> Path.Build.t list -val dlls : t -> Path.Build.t list +val dll_files : t -> Path.Build.t list diff --git a/src/dune/lib_file_deps.ml b/src/dune/lib_file_deps.ml index 5f77f30ec10a..f794147af616 100644 --- a/src/dune/lib_file_deps.ml +++ b/src/dune/lib_file_deps.ml @@ -11,7 +11,7 @@ module Group = struct let ext = function | Cmi -> Cm_kind.ext Cmi | Cmx -> Cm_kind.ext Cmx - | Header -> C.header_ext + | Header -> Foreign.header_ext let obj_dir t obj_dir = match t with diff --git a/src/dune/lib_rules.ml b/src/dune/lib_rules.ml index 3d01b4caecfa..19b253192487 100644 --- a/src/dune/lib_rules.ml +++ b/src/dune/lib_rules.ml @@ -15,6 +15,7 @@ let msvc_hack_cclibs = in Option.value ~default:lib (String.drop_prefix ~prefix:"-l" lib)) +(* Build an OCaml library. *) let build_lib (lib : Library.t) ~sctx ~expander ~flags ~dir ~mode ~cm_files = let ctx = Super_context.context sctx in let { Lib_config.ext_lib; _ } = ctx.lib_config in @@ -23,13 +24,10 @@ let build_lib (lib : Library.t) ~sctx ~expander ~flags ~dir ~mode ~cm_files = Library.archive lib ~dir ~ext:(Mode.compiled_lib_ext mode) in let stubs_flags = - if not (Library.has_stubs lib) then - [] - else - let stubs_name = Library.stubs_name lib in - match mode with - | Byte -> [ "-dllib"; "-l" ^ stubs_name; "-cclib"; "-l" ^ stubs_name ] - | Native -> [ "-cclib"; "-l" ^ stubs_name ] + List.concat_map (Library.archive_names lib) ~f:(fun name -> + match mode with + | Byte -> [ "-dllib"; "-l" ^ name; "-cclib"; "-l" ^ name ] + | Native -> [ "-cclib"; "-l" ^ name ]) in let map_cclibs = (* https://github.com/ocaml/dune/issues/119 *) @@ -106,11 +104,12 @@ let gen_wrapped_compat_modules (lib : Library.t) cctx = Build.write_file (Path.as_in_build_dir_exn source_path) contents |> Super_context.add_rule sctx ~loc ~dir:(Compilation_context.dir cctx)) -let ocamlmklib (lib : Library.t) ~sctx ~dir ~expander ~o_files ~sandbox ~custom - ~targets = - Super_context.add_rule sctx ~sandbox ~dir ~loc:lib.buildable.loc +(* Add a rule calling [ocamlmklib] to build a library. *) +let ocamlmklib ~path ~loc ~c_library_flags ~sctx ~dir ~expander ~o_files + ~sandbox ~custom ~targets = + Super_context.add_rule sctx ~sandbox ~dir ~loc (let cclibs_args = - Expander.expand_and_eval_set expander lib.c_library_flags + Expander.expand_and_eval_set expander c_library_flags ~standard:(Build.return []) in let ctx = Super_context.context sctx in @@ -119,9 +118,9 @@ let ocamlmklib (lib : Library.t) ~sctx ~dir ~expander ~o_files ~sandbox ~custom ; ( if custom then A "-custom" else - As [] ) + Command.Args.empty ) ; A "-o" - ; Path (Path.build (Library.stubs lib ~dir)) + ; Path path ; Deps o_files ; Dyn (Build.map cclibs_args ~f:(fun cclibs -> @@ -134,14 +133,106 @@ let ocamlmklib (lib : Library.t) ~sctx ~dir ~expander ~o_files ~sandbox ~custom ; Hidden_targets targets ]) +(* Add a rule calling [ocamlmklib] to build an OCaml library. *) +let ocamlmklib_ocaml (lib : Library.t) ~sctx ~dir ~expander ~o_files ~sandbox + ~custom ~targets = + ocamlmklib + ~path:(Path.build (Library.stubs_path lib ~dir)) + ~loc:lib.buildable.loc ~c_library_flags:lib.c_library_flags ~sctx ~dir + ~expander ~o_files ~sandbox ~custom ~targets + +(* Build a static and a dynamic archive for a foreign library. *) +let build_foreign_library (library : Foreign.Library.t) ~sctx ~expander ~dir + ~dir_contents = + let archive_name = library.archive_name in + let o_files = + let include_dirs_loc, include_dirs = library.stubs.include_dirs in + let include_dirs = + Build.map + ~f:(List.map ~f:(Path.relative (Path.build dir))) + (Expander.expand_and_eval_set expander include_dirs + ~standard:(Build.return [])) + in + let extra_flags = + Command.Args.Dyn + (Build.dyn_path_set + (Build.map include_dirs ~f:(fun include_dirs -> + let args, deps = + List.unzip + (List.map include_dirs ~f:(fun include_dir -> + let args = + Command.Args.S [ A "-I"; Path include_dir ] + in + let deps = + match + Path.extract_build_context_dir include_dir + with + | None -> + (* TODO: Track files in external directories. *) + User_warning.emit ~loc:include_dirs_loc + [ Pp.textf + "%S is an external directory; dependencies \ + in external directories are currently not \ + tracked." + (Path.to_string include_dir) + ]; + Path.Set.empty + | Some (build_dir, source_dir) -> ( + match File_tree.find_dir source_dir with + | None -> + User_error.raise ~loc:include_dirs_loc + [ Pp.textf "Include directory %S not found." + (Path.reach ~from:(Path.build dir) + include_dir) + ] + | Some dir -> + File_tree.Dir.fold dir + ~traverse:Sub_dirs.Status.Set.all + ~init:Path.Set.empty ~f:(fun t -> + let paths = + Path.Source.Set.to_list + (File_tree.Dir.file_paths t) + |> List.map + ~f:(Path.append_source build_dir) + in + Path.Set.union (Path.Set.of_list paths)) ) + in + (args, deps))) + in + (Command.Args.S args, Path.Set.union_all deps)))) + in + let foreign_sources = + Dir_contents.foreign_sources_of_foreign_library dir_contents + ~archive_name + in + (* Printf.printf "foreign_sources = %d" (List.length (String.Map.to_list + foreign_sources)); *) + Foreign_rules.build_o_files ~sctx ~dir ~expander ~requires:(Result.ok []) + ~dir_contents ~foreign_sources ~extra_flags + ~extra_deps:library.stubs.extra_deps + |> List.map ~f:Path.build + in + Check_rules.add_files sctx ~dir o_files; + let ctx = Super_context.context sctx in + let { Lib_config.ext_lib; ext_dll; _ } = ctx.lib_config in + let static = Foreign.lib_file ~archive_name ~dir ~ext_lib in + let dynamic = Foreign.dll_file ~archive_name ~dir ~ext_dll in + ocamlmklib + ~path:(Path.build (Path.Build.relative dir archive_name)) + ~loc:library.stubs.loc + ~c_library_flags:Ordered_set_lang.Unexpanded.standard ~sctx ~dir ~expander + ~o_files ~sandbox:Sandbox_config.no_special_requirements ~custom:false + ~targets:[ static; dynamic ] + +(* Build a required set of archives for an OCaml library. *) let build_self_stubs lib ~cctx ~expander ~dir ~o_files = let sctx = Compilation_context.super_context cctx in let ctx = Super_context.context sctx in let { Lib_config.ext_lib; ext_dll; _ } = ctx.lib_config in - let static = Library.stubs_archive lib ~dir ~ext_lib in - let dynamic = Library.dll lib ~dir ~ext_dll in + let static = Library.default_lib_file lib ~dir ~ext_lib in + let dynamic = Library.default_dll_file lib ~dir ~ext_dll in let modes = Compilation_context.modes cctx in - let ocamlmklib = ocamlmklib lib ~sctx ~expander ~dir ~o_files in + let ocamlmklib = ocamlmklib_ocaml lib ~sctx ~expander ~dir ~o_files in if modes.native && modes.byte && Dynlink_supported.get lib.dynlink ctx.supports_shared_libraries @@ -163,16 +254,13 @@ let build_stubs lib ~cctx ~dir ~expander ~requires ~dir_contents ~vlib_stubs_o_files = let sctx = Compilation_context.super_context cctx in let lib_o_files = - if Library.has_stubs lib then - let c_sources = - Dir_contents.c_sources_of_library dir_contents - ~name:(Library.best_name lib) - in - C_rules.build_o_files lib.buildable ~sctx ~dir ~expander ~requires - ~dir_contents ~c_sources - |> List.map ~f:Path.build - else - [] + let foreign_sources = + Dir_contents.foreign_sources_of_library dir_contents + ~name:(Library.best_name lib) + in + Foreign_rules.build_o_files ~sctx ~dir ~expander ~requires ~dir_contents + ~foreign_sources ~extra_flags:Command.Args.empty ~extra_deps:[] + |> List.map ~f:Path.build in Check_rules.add_files sctx ~dir lib_o_files; match vlib_stubs_o_files @ lib_o_files with @@ -205,11 +293,8 @@ let build_shared lib ~sctx ~dir ~flags = ] in let build = - if Library.has_stubs lib then - Build.path (Path.build (Library.stubs_archive ~dir lib ~ext_lib)) - >>> build - else - build + List.fold_left ~init:build (Library.lib_files lib ~dir ~ext_lib) + ~f:(fun build lib -> Build.path (Path.build lib) >>> build) in Super_context.add_rule sctx build ~dir) @@ -328,7 +413,7 @@ let library_rules (lib : Library.t) ~cctx ~source_modules ~dir_contents setup_build_archives lib ~cctx ~dep_graphs ~expander; let () = let vlib_stubs_o_files = Vimpl.vlib_stubs_o_files vimpl in - if Library.has_stubs lib || not (List.is_empty vlib_stubs_o_files) then + if Library.has_stubs lib || List.is_non_empty vlib_stubs_o_files then build_stubs lib ~cctx ~dir ~expander ~requires:requires_compile ~dir_contents ~vlib_stubs_o_files in diff --git a/src/dune/lib_rules.mli b/src/dune/lib_rules.mli index b196dad7e483..01b1814eeb6c 100644 --- a/src/dune/lib_rules.mli +++ b/src/dune/lib_rules.mli @@ -1,6 +1,14 @@ open! Stdune open Dune_file +val build_foreign_library : + Foreign.Library.t + -> sctx:Super_context.t + -> expander:Expander.t + -> dir:Path.Build.t + -> dir_contents:Dir_contents.t + -> unit + val rules : Library.t -> sctx:Super_context.t diff --git a/src/dune/module_compilation.ml b/src/dune/module_compilation.ml index d583255db926..42170418e9f7 100644 --- a/src/dune/module_compilation.ml +++ b/src/dune/module_compilation.ml @@ -20,7 +20,7 @@ let force_read_cmi source_file = [ "-intf-suffix"; Path.extension source_file ] let opens modules m = match Modules.alias_for modules m with - | None -> Command.Args.S [] + | None -> Command.Args.empty | Some (m : Module.t) -> As [ "-open"; Module_name.to_string (Module.name m) ] let build_cm cctx ~dep_graphs ~precompiled_cmi ~cm_kind (m : Module.t) = @@ -122,7 +122,7 @@ let build_cm cctx ~dep_graphs ~precompiled_cmi ~cm_kind (m : Module.t) = in let other_targets, cmt_args = match cm_kind with - | Cmx -> (other_targets, Command.Args.S []) + | Cmx -> (other_targets, Command.Args.empty) | Cmi | Cmo -> let fn = @@ -142,7 +142,7 @@ let build_cm cctx ~dep_graphs ~precompiled_cmi ~cm_kind (m : Module.t) = then Command.Args.A "-opaque" else - As [] + Command.Args.empty in let dir, no_keep_locs = match @@ -152,11 +152,12 @@ let build_cm cctx ~dep_graphs ~precompiled_cmi ~cm_kind (m : Module.t) = with | true, Cmi, true -> (ctx.build_dir, Command.Args.As [ "-no-keep-locs" ]) - | true, Cmi, false -> (Obj_dir.byte_dir obj_dir, As []) + | true, Cmi, false -> + (Obj_dir.byte_dir obj_dir, Command.Args.empty) (* emulated -no-keep-locs *) | true, (Cmo | Cmx), _ | false, _, _ -> - (ctx.build_dir, As []) + (ctx.build_dir, Command.Args.empty) in let flags = let flags = @@ -184,7 +185,7 @@ let build_cm cctx ~dep_graphs ~precompiled_cmi ~cm_kind (m : Module.t) = ; Cm_kind.Dict.get (CC.includes cctx) cm_kind ; As extra_args ; ( if dynlink || cm_kind <> Cmx then - As [] + Command.Args.empty else A "-nodynlink" ) ; A "-no-alias-deps" diff --git a/src/dune/modules_field_evaluator.ml b/src/dune/modules_field_evaluator.ml index 4af1d500ccd1..a50891143e9d 100644 --- a/src/dune/modules_field_evaluator.ml +++ b/src/dune/modules_field_evaluator.ml @@ -134,7 +134,7 @@ let check_invalid_module_listing ~(buildable : Buildable.t) ~intf_only ~modules ~existing_virtual_modules ~allow_new_public_modules in if - (not (List.is_empty errors)) + List.is_non_empty errors || not (Module_name.Set.is_empty unimplemented_virt_modules) then ( let get kind = diff --git a/src/dune/preprocessing.mli b/src/dune/preprocessing.mli index 4400718d94e8..cb0aee28a0ae 100644 --- a/src/dune/preprocessing.mli +++ b/src/dune/preprocessing.mli @@ -15,7 +15,7 @@ val make : -> dep_kind:Lib_deps_info.Kind.t -> lint:Dune_file.Preprocess_map.t -> preprocess:Dune_file.Preprocess_map.t - -> preprocessor_deps:Dune_file.Dep_conf.t list + -> preprocessor_deps:Dep_conf.t list -> lib_name:Lib_name.Local.t option -> scope:Scope.t -> t diff --git a/src/dune/simple_rules.ml b/src/dune/simple_rules.ml index 15e6351b665e..258d070af665 100644 --- a/src/dune/simple_rules.ml +++ b/src/dune/simple_rules.ml @@ -124,7 +124,7 @@ let add_alias sctx ~dir ~name ~stamp ~loc ?(locks = []) build = let alias sctx ?extra_bindings ~dir ~expander (alias_conf : Alias_conf.t) = let stamp = ( "user-alias" - , Bindings.map ~f:Dune_file.Dep_conf.remove_locs alias_conf.deps + , Bindings.map ~f:Dep_conf.remove_locs alias_conf.deps , Option.map ~f:(fun (_loc, a) -> Action_unexpanded.remove_locs a) alias_conf.action diff --git a/src/dune/super_context.ml b/src/dune/super_context.ml index 24ad87899c08..19fdbc430eb2 100644 --- a/src/dune/super_context.ml +++ b/src/dune/super_context.ml @@ -112,7 +112,8 @@ module Env : sig val ocaml_flags : t -> dir:Path.Build.t -> Ocaml_flags.t - val c_flags : t -> dir:Path.Build.t -> string list Build.t C.Kind.Dict.t + val c_flags : + t -> dir:Path.Build.t -> string list Build.t Foreign.Language.Dict.t val external_ : t -> dir:Path.Build.t -> External_env.t @@ -225,7 +226,7 @@ end = struct List.filter ctx.ocamlc_cflags ~f:(fun s -> not (String.is_prefix s ~prefix:"-std=")) in - C.Kind.Dict.make ~c ~cxx + Foreign.Language.Dict.make ~c ~cxx let c_flags t ~dir = let default_context_flags = default_context_flags t.context in @@ -307,20 +308,17 @@ let ocaml_flags t ~dir (x : Dune_file.Buildable.t) = else flags -let c_flags t ~dir ~expander ~flags = +let foreign_flags t ~dir ~expander ~flags ~language = let t = t.env_context in let ccg = Context.cc_g t.context in let default = Env.c_flags t ~dir in - C.Kind.Dict.mapi flags ~f:(fun ~kind flags -> - let name = C.Kind.to_string kind in - Build.memoize (sprintf "%s flags" name) - (let default = C.Kind.Dict.get default kind in - let c = - Expander.expand_and_eval_set expander flags ~standard:default - in - let open Build.O in - let+ l = c in - l @ ccg)) + let name = Foreign.Language.proper_name language in + Build.memoize (sprintf "%s flags" name) + (let default = Foreign.Language.Dict.get default language in + let c = Expander.expand_and_eval_set expander flags ~standard:default in + let open Build.O in + let+ l = c in + l @ ccg) let local_binaries t ~dir = Env.local_binaries t.env_context ~dir @@ -575,7 +573,7 @@ end module Deps = struct open Build.O - open Dune_file.Dep_conf + open Dep_conf let make_alias expander s = let loc = String_with_vars.loc s in diff --git a/src/dune/super_context.mli b/src/dune/super_context.mli index 4cd190581351..d37922546966 100644 --- a/src/dune/super_context.mli +++ b/src/dune/super_context.mli @@ -60,12 +60,13 @@ val internal_lib_names : t -> Lib_name.Set.t stanza *) val ocaml_flags : t -> dir:Path.Build.t -> Buildable.t -> Ocaml_flags.t -val c_flags : +val foreign_flags : t -> dir:Path.Build.t -> expander:Expander.t - -> flags:Ordered_set_lang.Unexpanded.t C.Kind.Dict.t - -> string list Build.t C.Kind.Dict.t + -> flags:Ordered_set_lang.Unexpanded.t + -> language:Foreign.Language.t + -> string list Build.t (** Binaries that are symlinked in the associated .bin directory of [dir]. This associated directory is [Path.relative dir ".bin"] *) @@ -143,8 +144,8 @@ module Libs : sig /!\ WARNING /!\: make sure the last function call inside [f] is fully applied, otherwise the function might end up being executed after this - function has returned. Consider addin a type annotation to make sure this - doesn't happen by mistake. *) + function has returned. Consider adding a type annotation to make sure + this doesn't happen by mistake. *) val with_lib_deps : t -> Lib.Compile.t -> dir:Path.Build.t -> f:(unit -> 'a) -> 'a @@ -152,7 +153,7 @@ module Libs : sig val gen_select_rules : t -> dir:Path.Build.t -> Lib.Compile.t -> unit end -(** Interpret dependencies written in jbuild files *) +(** Interpret dependencies written in Dune files *) module Deps : sig (** Evaluates to the actual list of dependencies, ignoring aliases, and registers them as the action dependencies. *) @@ -169,7 +170,7 @@ module Deps : sig -> Path.t Bindings.t Build.t end -(** Interpret action written in jbuild files *) +(** Interpret action written in Dune files *) module Action : sig (** This function takes as input the list of dependencies written by user, which is used for action expansion. These must be registered with the diff --git a/src/dune/upgrader.ml b/src/dune/upgrader.ml index 66c1a3d6977e..0dc15fb778cc 100644 --- a/src/dune/upgrader.ml +++ b/src/dune/upgrader.ml @@ -329,7 +329,7 @@ let upgrade_opam_file todo fn = in List.iter t.file_contents ~f:scan_item; let substs = List.sort !substs ~compare in - if not (List.is_empty substs) then ( + if List.is_non_empty substs then ( let buf = Buffer.create (String.length s + 128) in let ofs = List.fold_left substs ~init:0 ~f:(fun ofs (start, stop, repl) -> diff --git a/src/dune/utils.ml b/src/dune/utils.ml index fa06239ba53e..841ea276887a 100644 --- a/src/dune/utils.ml +++ b/src/dune/utils.ml @@ -56,7 +56,7 @@ let install_file ~(package : Package.Name.t) ~findlib_toolchain = let line_directive ~filename:fn ~line_number = let directive = - if C.c_cxx_or_header ~fn then + if Foreign.c_cxx_or_header ~fn then "line" else "" diff --git a/src/dune/virtual_rules.ml b/src/dune/virtual_rules.ml index 0ed8377e21fb..98570822d3c2 100644 --- a/src/dune/virtual_rules.ml +++ b/src/dune/virtual_rules.ml @@ -119,8 +119,8 @@ let impl sctx ~(lib : Dune_file.Library.t) ~scope = let foreign_objects = let ext_obj = (Super_context.context sctx).lib_config.ext_obj in let dir = Obj_dir.obj_dir (Lib.Local.obj_dir vlib) in - Dir_contents.c_sources_of_library dir_contents ~name - |> C.Sources.objects ~ext_obj ~dir + Dir_contents.foreign_sources_of_library dir_contents ~name + |> Foreign.Sources.objects ~ext_obj ~dir |> List.map ~f:Path.build in (modules, foreign_objects) diff --git a/src/dune_lang/decoder.ml b/src/dune_lang/decoder.ml index 15846ca74f16..33ddbbf45561 100644 --- a/src/dune_lang/decoder.ml +++ b/src/dune_lang/decoder.ml @@ -436,6 +436,8 @@ let map_validate t ~f ctx state1 = in raise (User_error.E msg) +(** TODO: Improve consistency of error messages, e.g. use %S consistently for + field names: see [field_missing] and [field_present_too_many_times]. *) let field_missing loc name = User_error.raise ~loc [ Pp.textf "field %s missing" name ] [@@inline never] diff --git a/src/dune_lang/syntax.ml b/src/dune_lang/syntax.ml index af74a5d0ac28..721fd133e3ac 100644 --- a/src/dune_lang/syntax.ml +++ b/src/dune_lang/syntax.ml @@ -95,10 +95,17 @@ module Error = struct end module Warning = struct - let deprecated_in loc t ?(repl = []) ver ~what = + let deprecated_in loc ~extra_info t ?(repl = []) ver ~what = User_warning.emit ~loc - ( Pp.textf "%s was deprecated in version %s of %s." what - (Version.to_string ver) t.desc + ( Pp.concat + [ Pp.textf "%s was deprecated in version %s of %s." what + (Version.to_string ver) t.desc + ; ( if extra_info = "" then + Pp.nop + else + Pp.space ) + ; Pp.text extra_info + ] :: repl ) end @@ -164,14 +171,14 @@ let deleted_in t ver = let* loc, what = desc () in Error.deleted_in loc t ver ~what -let deprecated_in t ver = +let deprecated_in ?(extra_info = "") t ver = let open Version.Infix in let* current_ver = get_exn t in if current_ver < ver then return () else let+ loc, what = desc () in - Warning.deprecated_in loc t ver ~what + Warning.deprecated_in ~extra_info loc t ver ~what let renamed_in t ver ~to_ = let open Version.Infix in diff --git a/src/dune_lang/syntax.mli b/src/dune_lang/syntax.mli index 9d5d81e60a5a..7edc8be1e8cb 100644 --- a/src/dune_lang/syntax.mli +++ b/src/dune_lang/syntax.mli @@ -48,6 +48,7 @@ end module Warning : sig val deprecated_in : Loc.t + -> extra_info:string -> t -> ?repl:User_message.Style.t Pp.t list -> Version.t @@ -79,7 +80,8 @@ val deleted_in : t -> Version.t -> (unit, _) Decoder.parser (** Indicate the field/constructor being parsed was deprecated in the given version *) -val deprecated_in : t -> Version.t -> (unit, _) Decoder.parser +val deprecated_in : + ?extra_info:string -> t -> Version.t -> (unit, _) Decoder.parser (** Indicate the field/constructor being parsed was renamed in the given version *) diff --git a/src/stdune/dune b/src/stdune/dune index 75d7c7683be8..b3a36ffa241a 100644 --- a/src/stdune/dune +++ b/src/stdune/dune @@ -4,4 +4,4 @@ (synopsis "[Internal] Standard library of Dune") (preprocess future_syntax) (libraries dune_caml unix) - (c_names fcntl)) + (foreign_stubs (language c) (names fcntl))) diff --git a/src/stdune/list.ml b/src/stdune/list.ml index cb912214d3b6..793c4b4683c2 100644 --- a/src/stdune/list.ml +++ b/src/stdune/list.ml @@ -8,6 +8,10 @@ let is_empty = function | [] -> true | _ -> false +let is_non_empty = function + | [] -> false + | _ -> true + let rec filter_map l ~f = match l with | [] -> [] @@ -180,3 +184,6 @@ let fold_map t ~init ~f = y) in (!acc, result) + +let unzip l = + fold_right ~init:([], []) ~f:(fun (x, y) (xs, ys) -> (x :: xs, y :: ys)) l diff --git a/src/stdune/list.mli b/src/stdune/list.mli index 595ca805d9a3..b59e7e46ca16 100644 --- a/src/stdune/list.mli +++ b/src/stdune/list.mli @@ -8,6 +8,8 @@ type 'a t = 'a list val is_empty : _ t -> bool +val is_non_empty : _ t -> bool + val filter_map : 'a t -> f:('a -> 'b option) -> 'b t val filter_opt : 'a option t -> 'a t @@ -68,3 +70,5 @@ val hash : ('a -> int) -> 'a list -> int val cons : 'a t -> 'a -> 'a t val fold_map : 'a list -> init:'b -> f:('b -> 'a -> 'b * 'c) -> 'b * 'c list + +val unzip : ('a * 'b) t -> 'a t * 'b t diff --git a/test/blackbox-tests/dune.inc b/test/blackbox-tests/dune.inc index 8752dc8550dc..5440a58e2b6b 100644 --- a/test/blackbox-tests/dune.inc +++ b/test/blackbox-tests/dune.inc @@ -586,6 +586,14 @@ test-cases/force-test (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) +(alias + (name foreign-library) + (deps (package dune) (source_tree test-cases/foreign-library)) + (action + (chdir + test-cases/foreign-library + (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) + (alias (name format-dune-file) (deps (package dune) (source_tree test-cases/format-dune-file)) @@ -722,6 +730,14 @@ test-cases/github1811 (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) +(alias + (name github1855) + (deps (package dune) (source_tree test-cases/github1855)) + (action + (chdir + test-cases/github1855 + (progn (run %{exe:cram.exe} -test run.t) (diff? run.t run.t.corrected))))) + (alias (name github1856) (deps (package dune) (source_tree test-cases/github1856)) @@ -1893,6 +1909,7 @@ (alias findlib-error) (alias forbidden_libraries) (alias force-test) + (alias foreign-library) (alias format-dune-file) (alias formatting) (alias gen-opam-install-file) @@ -1910,6 +1927,7 @@ (alias github1560) (alias github1616) (alias github1811) + (alias github1855) (alias github1856) (alias github1946) (alias github20) @@ -2108,6 +2126,7 @@ (alias findlib-error) (alias forbidden_libraries) (alias force-test) + (alias foreign-library) (alias format-dune-file) (alias formatting) (alias github1019) @@ -2123,6 +2142,7 @@ (alias github1560) (alias github1616) (alias github1811) + (alias github1855) (alias github1856) (alias github1946) (alias github20) diff --git a/test/blackbox-tests/test-cases/duplicate-c-cxx-obj/run.t b/test/blackbox-tests/test-cases/duplicate-c-cxx-obj/run.t index 9f819a76193a..1d2a8dfc5fba 100644 --- a/test/blackbox-tests/test-cases/duplicate-c-cxx-obj/run.t +++ b/test/blackbox-tests/test-cases/duplicate-c-cxx-obj/run.t @@ -13,8 +13,8 @@ user intended. $ dune build --root same-stanza @all Entering directory 'same-stanza' File "dune", line 1, characters 0-0: - Error: c file foo appears in several directories: + Error: C source file "foo" appears in more than one directory: - . - sub - This is not allowed, please rename one of them. + This is not allowed; please rename them. [1] diff --git a/test/blackbox-tests/test-cases/duplicate-c-cxx/run.t b/test/blackbox-tests/test-cases/duplicate-c-cxx/run.t index 4d0fa349b35b..a72f79bb28ab 100644 --- a/test/blackbox-tests/test-cases/duplicate-c-cxx/run.t +++ b/test/blackbox-tests/test-cases/duplicate-c-cxx/run.t @@ -4,7 +4,8 @@ c_names and cxx_names with overlapping names in the same stanza File "dune", line 4, characters 12-15: 4 | (cxx_names foo)) ^^^ - Error: foo.cpp and foo.c have conflicting names. You must rename one of them. + Error: "foo.c" and "foo.cpp" map to the same object name "foo". This is not + allowed; please rename them. [1] c_names with overlapping names in different stanzas diff --git a/test/blackbox-tests/test-cases/exes-with-c/run.t b/test/blackbox-tests/test-cases/exes-with-c/run.t index 070ab7542d5a..d15acd9196ff 100644 --- a/test/blackbox-tests/test-cases/exes-with-c/run.t +++ b/test/blackbox-tests/test-cases/exes-with-c/run.t @@ -1,4 +1,9 @@ $ dune build --display short aa.exe bb.exe + File "dune", line 3, characters 1-14: + 3 | (c_names foo) + ^^^^^^^^^^^^^ + Warning: 'c_names' was deprecated in version 2.0 of the dune language. Use + the (foreign_stubs ...) stanza instead. ocamlc .aa.eobjs/byte/dune__exe.{cmi,cmo,cmt} ocamlopt .aa.eobjs/native/dune__exe.{cmx,o} ocamldep .aa.eobjs/aa.ml.d @@ -28,6 +33,11 @@ Entering directory 'err' Info: Creating file dune-project with this contents: | (lang dune 2.0) + File "dune", line 3, characters 1-16: + 3 | (c_names stubs)) + ^^^^^^^^^^^^^^^ + Warning: 'c_names' was deprecated in version 2.0 of the dune language. Use + the (foreign_stubs ...) stanza instead. File "dune", line 1, characters 0-41: 1 | (executable 2 | (name foo) diff --git a/test/blackbox-tests/test-cases/foreign-library/run.t b/test/blackbox-tests/test-cases/foreign-library/run.t new file mode 100644 index 000000000000..d52147c232fe --- /dev/null +++ b/test/blackbox-tests/test-cases/foreign-library/run.t @@ -0,0 +1,292 @@ + $ echo "(lang dune 1.0)" > dune-project + $ mkdir -p lib + + $ cat >lib/dune < (foreign_library + > (language c) + > (names add)) + > EOF + + $ dune build + File "lib/dune", line 1, characters 0-44: + 1 | (foreign_library + 2 | (language c) + 3 | (names add)) + Error: 'foreign_library' is only available since version 2.0 of the dune + language. Please update your dune-project file to have (lang 2.0). + [1] + +---------------------------------------------------------------------- + + $ echo "(lang dune 2.0)" > dune-project + + $ dune build + File "lib/dune", line 1, characters 0-44: + 1 | (foreign_library + 2 | (language c) + 3 | (names add)) + Error: field archive_name missing + [1] + +---------------------------------------------------------------------- + + $ cat >lib/dune < (foreign_library + > (archive_name addmul) + > (language c) + > (names add mul)) + > EOF + + $ cat >lib/add.c < #include + > value add(value x, value y) { return Val_int(Int_val(x) + Int_val(y)); } + > EOF + + $ dune build + File "lib/dune", line 4, characters 12-15: + 4 | (names add mul)) + ^^^ + Error: Object "mul" has no source; "mul.c" must be present. + [1] + +---------------------------------------------------------------------- + + $ cat >lib/mul.c < #include + > value mul(value x, value y) { return Val_int(Int_val(x) * Int_val(y)); } + > EOF + + $ dune build + +---------------------------------------------------------------------- + + $ cat >lib/dune < (foreign_library + > (archive_name addmul) + > (language c) + > (names add mul)) + > (library + > (name calc) + > (modules calc) + > (foreign_stubs_archives addmul config)) + > (executable + > (name main) + > (libraries calc) + > (modules main)) + > (foreign_library + > (archive_name config) + > (language c) + > (flags -DCONFIG_VALUE=2000) + > (names config)) + > EOF + + $ cat >lib/config.c < #include + > value config() { return Val_int(CONFIG_VALUE); } + > EOF + + $ cat >lib/calc.ml < external add : int -> int -> int = "add" + > external mul : int -> int -> int = "mul" + > external config : unit -> int = "config" + > let calc x y z = add (mul (add x y) z) (config ()) + > EOF + + $ cat >lib/calc.mli < val calc : int -> int -> int -> int + > EOF + + $ cat >lib/main.ml < let () = Printf.printf "%d" (Calc.calc 1 2 3) + > EOF + + $ dune build + + $ dune exec lib/main.exe + 2009 + + $ (cd _build/default && ocamlrun -I lib lib/main.bc) + 2009 + +---------------------------------------------------------------------- + + $ cat >lib/dune < (foreign_library + > (archive_name addmul) + > (language c) + > (names add mul)) + > (library + > (name calc) + > (modules calc) + > (foreign_stubs_archives addmul config)) + > (executable + > (name main) + > (libraries calc) + > (modules main)) + > (foreign_library + > (archive_name config) + > (language cxx) + > (include_dirs headers) + > (extra_deps eight.h) + > (flags -DCONFIG_VALUE=2000) + > (names config)) + > EOF + + $ dune build + File "lib/dune", line 19, characters 8-14: + 19 | (names config)) + ^^^^^^ + Error: Object "config" has no source; One of "config.cxx", "config.cc" or + "config.cpp" must be present. + [1] + +---------------------------------------------------------------------- + + $ cat >lib/config.cpp < #include + > #include "ten.h" + > extern "C" value config() { return Val_int(CONFIG_VALUE + TEN); } + > EOF + + $ mkdir -p lib/headers + $ cat >lib/headers/ten.h < #include "../eight.h" + > #include "some/deep/path/one.h" + > #define TEN (1 + EIGHT + ONE) + > EOF + + $ mkdir -p lib/headers/some/deep/path + $ cat >lib/headers/some/deep/path/one.h < #define ONE 1 + > EOF + + $ cat >lib/eight.h < #define EIGHT 8 + > EOF + + $ dune clean + $ dune build --display short + ocamlc lib/add$ext_obj + ocamldep lib/.calc.objs/calc.mli.d + ocamlc lib/.calc.objs/byte/calc.{cmi,cmti} + ocamldep lib/.calc.objs/calc.ml.d + ocamlc lib/.calc.objs/byte/calc.{cmo,cmt} + ocamlc lib/calc.cma + ocamlc lib/mul$ext_obj + ocamlmklib lib/dlladdmul$ext_dll,lib/libaddmul$ext_lib + gcc lib/config$ext_obj + ocamlmklib lib/dllconfig$ext_dll,lib/libconfig$ext_lib + ocamldep lib/.main.eobjs/main.ml.d + ocamlc lib/.main.eobjs/byte/dune__exe__Main.{cmi,cmo,cmt} + ocamlopt lib/.main.eobjs/native/dune__exe__Main.{cmx,o} + ocamlopt lib/.calc.objs/native/calc.{cmx,o} + ocamlopt lib/calc.{a,cmxa} + ocamlopt lib/calc.cmxs + ocamlc lib/main.bc + ocamlopt lib/main.exe + + $ dune exec lib/main.exe + 2019 + + $ (cd _build/default && ocamlrun -I lib lib/main.bc) + 2019 + +---------------------------------------------------------------------- + + $ cat >lib/dune < (foreign_library + > (archive_name addmul) + > (language c) + > (names add mul)) + > (library + > (name calc) + > (modules calc) + > (foreign_stubs_archives addmul config)) + > (executable + > (name main) + > (libraries calc) + > (modules main)) + > (foreign_library + > (archive_name config) + > (language cxx) + > (include_dirs headers another/dir) + > (extra_deps eight.h) + > (flags -DCONFIG_VALUE=2000) + > (names config)) + > EOF + + $ dune build + File "lib/dune", line 16, characters 1-35: + 16 | (include_dirs headers another/dir) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Error: Include directory "another/dir" not found. + [1] + +---------------------------------------------------------------------- + + $ cat >lib/dune < (foreign_library + > (archive_name addmul) + > (language c) + > (names add mul)) + > (library + > (name calc) + > (modules calc) + > (foreign_stubs_archives addmul config)) + > (executable + > (name main) + > (libraries calc) + > (modules main)) + > (foreign_library + > (archive_name config) + > (language cxx) + > (include_dirs headers /absolute/path) + > (extra_deps eight.h) + > (flags -DCONFIG_VALUE=2000) + > (names config)) + > EOF + + $ dune build + File "lib/dune", line 16, characters 1-38: + 16 | (include_dirs headers /absolute/path) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Warning: "/absolute/path" is an external directory; dependencies in external + directories are currently not tracked. + +---------------------------------------------------------------------- + + $ cat >lib/dune < (foreign_library + > (archive_name addmul) + > (language c) + > (names add)) + > (foreign_library + > (archive_name addmul) + > (language c) + > (names mul)) + > (library + > (name calc) + > (modules calc) + > (foreign_stubs_archives addmul config)) + > (executable + > (name main) + > (libraries calc) + > (modules main)) + > (foreign_library + > (archive_name config) + > (language cxx) + > (include_dirs headers /absolute/path) + > (extra_deps eight.h) + > (flags -DCONFIG_VALUE=2000) + > (names config)) + > EOF + + $ dune build + File "lib/dune", line 5, characters 0-67: + 5 | (foreign_library + 6 | (archive_name addmul) + 7 | (language c) + 8 | (names mul)) + Error: Multiple foreign libraries with the same archive name "addmul"; the + name has already been taken in lib/dune:1. + [1] diff --git a/test/blackbox-tests/test-cases/github1306/dune b/test/blackbox-tests/test-cases/github1306/dune deleted file mode 100644 index cc5b3229f60f..000000000000 --- a/test/blackbox-tests/test-cases/github1306/dune +++ /dev/null @@ -1,4 +0,0 @@ -(library - (name foo) - (c_names foo) - (self_build_stubs_archive (bar))) diff --git a/test/blackbox-tests/test-cases/github1306/dune-project b/test/blackbox-tests/test-cases/github1306/dune-project deleted file mode 100644 index de4fc2092005..000000000000 --- a/test/blackbox-tests/test-cases/github1306/dune-project +++ /dev/null @@ -1 +0,0 @@ -(lang dune 1.0) diff --git a/test/blackbox-tests/test-cases/github1306/run.t b/test/blackbox-tests/test-cases/github1306/run.t index d73350c87ad4..b614b74dd525 100644 --- a/test/blackbox-tests/test-cases/github1306/run.t +++ b/test/blackbox-tests/test-cases/github1306/run.t @@ -1,7 +1,207 @@ + $ echo "(lang dune 1.0)" > dune-project + + $ cat >dune < (library + > (name foo) + > (c_names foo) + > (self_build_stubs_archive (bar))) + > EOF + $ dune build File "dune", line 4, characters 1-33: 4 | (self_build_stubs_archive (bar))) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error: A library cannot use (self_build_stubs_archive ...) and (c_names ...) - simultaneously. + simultaneously. This is supported starting from Dune 2.0. [1] + +---------------------------------------------------------------------------------- + + $ echo "(lang dune 2.0)" > dune-project + + $ dune build + File "dune", line 3, characters 1-14: + 3 | (c_names foo) + ^^^^^^^^^^^^^ + Warning: 'c_names' was deprecated in version 2.0 of the dune language. Use + the (foreign_stubs ...) stanza instead. + File "dune", line 4, characters 1-33: + 4 | (self_build_stubs_archive (bar))) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Warning: 'self_build_stubs_archive' was deprecated in version 2.0 of the dune + language. Use the (foreign_stubs_archives ...) stanza instead. + File "dune", line 3, characters 10-13: + 3 | (c_names foo) + ^^^ + Error: Object "foo" has no source; "foo.c" must be present. + [1] + +---------------------------------------------------------------------------------- + + $ cat >foo.c < #include + > value foo() { return Val_int(9); } + > EOF + + $ cat >dune < (library + > (name foo) + > (foreign_stubs (language c) (names foo)) + > (self_build_stubs_archive (bar))) + > EOF + + $ dune build + File "dune", line 4, characters 1-33: + 4 | (self_build_stubs_archive (bar))) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Warning: 'self_build_stubs_archive' was deprecated in version 2.0 of the dune + language. Use the (foreign_stubs_archives ...) stanza instead. + Error: No rule found for libbar_stubs$ext_lib + [1] + +---------------------------------------------------------------------------------- + + $ cat >bar.c < #include + > value bar() { return Val_int(10); } + > EOF + + $ cat >dune < (library + > (name foo) + > (foreign_stubs (language c) (names foo)) + > (self_build_stubs_archive (bar))) + > (rule + > (targets bar%{ext_obj}) + > (deps bar.c) + > (action (run %{ocaml-config:c_compiler} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps}))) + > (rule + > (targets libbar_stubs.a) + > (deps bar%{ext_obj}) + > (action (run ar rcs %{targets} %{deps}))) + > EOF + + $ dune build + File "dune", line 4, characters 1-33: + 4 | (self_build_stubs_archive (bar))) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Warning: 'self_build_stubs_archive' was deprecated in version 2.0 of the dune + language. Use the (foreign_stubs_archives ...) stanza instead. + +---------------------------------------------------------------------------------- + + $ cat >dune < (library + > (name foo) + > (foreign_stubs (archive_name baz) (language c) (names foo)) + > (self_build_stubs_archive (bar))) + > (rule + > (targets bar%{ext_obj}) + > (deps bar.c) + > (action (run %{ocaml-config:c_compiler} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps}))) + > (rule + > (targets libbar_stubs.a) + > (deps bar%{ext_obj}) + > (action (run ar rcs %{targets} %{deps}))) + > EOF + + $ dune build + File "dune", line 3, characters 16-34: + 3 | (foreign_stubs (archive_name baz) (language c) (names foo)) + ^^^^^^^^^^^^^^^^^^ + Error: The field "archive_name" is disallowed in the (foreign_stubs ...) + stanza. For named foreign archives use the (foreign_library ...) stanza. + [1] + +---------------------------------------------------------------------------------- + + $ cat >baz.cpp < #include + > extern "C" value baz() { return Val_int(0); } + > EOF + + $ cat >qux.cpp < #include + > extern "C" value qux() { return Val_int(2000); } + > EOF + + $ cat >quad.ml < external foo : unit -> int = "foo" + > external bar : unit -> int = "bar" + > external baz : unit -> int = "baz" + > external qux : unit -> int = "qux" + > let quad x = foo x + bar x + baz x + qux x + > EOF + + $ cat >quad.mli < val quad : unit -> int + > EOF + + $ cat >main.ml < let () = Printf.printf "%d" (Quad.quad ()) + > EOF + + $ cat >dune < (library + > (name quad) + > (modules quad) + > (foreign_stubs (language c) (names foo)) + > (foreign_stubs_archives bar qux) + > (foreign_stubs (language cxx) (names baz))) + > (rule + > (targets bar%{ext_obj}) + > (deps bar.c) + > (action (run %{ocaml-config:c_compiler} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps}))) + > (rule + > (targets libbar.a) + > (deps bar%{ext_obj}) + > (action (run ar rcs %{targets} %{deps}))) + > (rule + > (targets dllbar%{ext_dll}) + > (deps bar%{ext_obj}) + > (action (run %{ocaml-config:c_compiler} -shared -o %{targets} %{deps}))) + > (rule + > (targets qux%{ext_obj}) + > (deps qux.cpp) + > (action (run %{ocaml-config:c_compiler} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps}))) + > (rule + > (targets libqux.a) + > (deps qux%{ext_obj}) + > (action (run ar rcs %{targets} %{deps}))) + > (rule + > (targets dllqux%{ext_dll}) + > (deps qux%{ext_obj}) + > (action (run %{ocaml-config:c_compiler} -shared -o %{targets} %{deps}))) + > (executable + > (name main) + > (libraries quad) + > (modules main)) + > EOF + + $ dune build --display short + gcc baz$ext_obj + ocamlmklib dllquad_stubs$ext_dll,libquad_stubs$ext_lib + gcc dllbar$ext_dll + gcc qux$ext_obj + ar libqux$ext_lib + ar libbar$ext_lib + ocamldep .quad.objs/quad.mli.d + ocamlc .quad.objs/byte/quad.{cmi,cmti} + ocamldep .main.eobjs/main.ml.d + ocamlc .main.eobjs/byte/dune__exe__Main.{cmi,cmo,cmt} + ocamlopt .main.eobjs/native/dune__exe__Main.{cmx,o} + ocamldep .quad.objs/quad.ml.d + ocamlopt .quad.objs/native/quad.{cmx,o} + ocamlopt quad.{a,cmxa} + ocamlopt quad.cmxs + gcc dllqux$ext_dll + ocamlc .quad.objs/byte/quad.{cmo,cmt} + ocamlc quad.cma + ocamlc main.bc + ocamlopt main.exe + + $ dune exec ./main.exe + 2019 + + $ (cd _build/default && ocamlrun -I . ./main.bc) + 2019 diff --git a/test/blackbox-tests/test-cases/github1855/run.t b/test/blackbox-tests/test-cases/github1855/run.t index d6bae6c71acd..96780444ca93 100644 --- a/test/blackbox-tests/test-cases/github1855/run.t +++ b/test/blackbox-tests/test-cases/github1855/run.t @@ -14,6 +14,7 @@ just quietly shadow the module in the virtual library. > (library > (name vlib1855) > (virtual_modules dom)) + > EOF $ cat > impl/dune < (library > (name impl1855)