Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(root_module ..) field for libraries & executables #3825

Merged
merged 4 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ Unreleased

- Avoid pager when running `$ git diff` (#3912, @AltGr)

- Add `(root_module ..)` field to libraries & executables. This makes it
possible to use library dependencies shadowed by local modules (#3825,
@rgrinberg)

2.7.1 (2/09/2020)
-----------------

Expand Down
9 changes: 9 additions & 0 deletions doc/dune-files.rst
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,11 @@ to use the :ref:`include_subdirs` stanza.
configured through options using ``(inline_tests <options>)``. See
:ref:`inline_tests` for a reference of corresponding options.

- ``(root_module <module>)`` this field instructs dune to generate a module that
will contain module aliases for every library specified in dependencies. This
is useful whenever a library is shadowed by a local module. The library may
then still be accessible via this root module

Note that when binding C libraries, dune doesn't provide special support for
tools such as ``pkg-config``, however it integrates easily with
:ref:`configurator` by
Expand Down Expand Up @@ -644,6 +649,10 @@ Executables can also be linked as object or shared object files. See
the current stanza. It is interpreted in the same way as the ``(modules
...)`` field of `library`_

- ``(root_module <module>)`` specifies a ``root_module`` that collects all
dependencies specified in ``libraries``. See the documentation for
``root_module`` in the library stanza.

bobot marked this conversation as resolved.
Show resolved Hide resolved
- ``(modes (<modes>))`` sets the `linking modes`_. The default is
``(exe)``. Before 2.0, it used to be ``(byte exe)``.

Expand Down
4 changes: 1 addition & 3 deletions src/dune_rules/cinaps.ml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ let decode =
field "files" Predicate_lang.Glob.decode ~default:Predicate_lang.any
and+ preprocess, preprocessor_deps = Dune_file.preprocess_fields
and+ libraries =
field "libraries"
(Dune_file.Lib_deps.decode ~allow_re_export:false)
~default:[]
field "libraries" (Dune_file.Lib_deps.decode Executable) ~default:[]
and+ flags = Ocaml_flags.Spec.decode in
{ loc; files; libraries; preprocess; preprocessor_deps; flags })

Expand Down
26 changes: 24 additions & 2 deletions src/dune_rules/compilation_context.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ open! Stdune
open Import
module SC = Super_context

let modules_of_lib = Fdecl.create Dyn.Encoder.opaque

module Includes = struct
type t = Command.Args.dynamic Command.Args.t Cm_kind.Dict.t

Expand Down Expand Up @@ -164,8 +166,8 @@ let for_alias_module t =
let flags =
let project = Scope.project t.scope in
let dune_version = Dune_project.dune_version project in
Ocaml_flags.default ~profile:(Super_context.context t.super_context).profile
~dune_version
let profile = (Super_context.context t.super_context).profile in
Ocaml_flags.default ~dune_version ~profile
in
let sandbox =
let ctx = Super_context.context t.super_context in
Expand All @@ -186,6 +188,20 @@ let for_alias_module t =
; sandbox
}

let for_root_module t =
let flags =
let project = Scope.project t.scope in
let dune_version = Dune_project.dune_version project in
let profile = (Super_context.context t.super_context).profile in
Ocaml_flags.default ~profile ~dune_version
in
{ t with
flags =
Ocaml_flags.append_common flags
[ "-w"; "-49"; "-nopervasives"; "-nostdlib" ]
; stdlib = None
}
bobot marked this conversation as resolved.
Show resolved Hide resolved

let for_module_generated_at_link_time cctx ~requires ~module_ =
let opaque =
(* Cmi's of link time generated modules are compiled with -opaque, hence
Expand Down Expand Up @@ -214,3 +230,9 @@ let for_plugin_executable t ~embed_in_plugin_libraries =
{ t with requires_link }

let without_bin_annot t = { t with bin_annot = false }

let root_module_entries t : Module_name.t list Or_exn.t =
let open Result.O in
let* requires = t.requires_compile in
let local_lib = Fdecl.get modules_of_lib t.super_context in
Result.List.concat_map requires ~f:(Lib.entry_module_names ~local_lib)
8 changes: 8 additions & 0 deletions src/dune_rules/compilation_context.mli
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ type opaque =
| Inherit_from_settings
(** Determined from the version of OCaml and the profile *)

val modules_of_lib :
(* to avoid a cycle with [Dir_contents] *)
(Super_context.t -> dir:Path.Build.t -> name:Lib_name.t -> Modules.t) Fdecl.t

(** Create a compilation context. *)
val create :
super_context:Super_context.t
Expand Down Expand Up @@ -90,6 +94,8 @@ val modes : t -> Mode.Dict.Set.t

val for_wrapped_compat : t -> t

val for_root_module : t -> t

val for_module_generated_at_link_time :
t -> requires:Lib.t list Or_exn.t -> module_:Module.t -> t

Expand All @@ -99,3 +105,5 @@ val for_plugin_executable :
val bin_annot : t -> bool

val without_bin_annot : t -> t

val root_module_entries : t -> Module_name.t list Or_exn.t
8 changes: 8 additions & 0 deletions src/dune_rules/dir_contents.ml
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ end = struct
| See_above _ -> assert false
| Here { t; rules = _; subdirs = _ } -> t )

let () =
let f sctx ~dir ~name =
let t = get sctx ~dir in
let ml_sources = ocaml t in
Ml_sources.modules_of_library ml_sources ~name
in
Fdecl.set Compilation_context.modules_of_lib f

let gen_rules sctx ~dir =
match Memo.exec memo0 (sctx, dir) with
| See_above group_root -> Group_part group_root
Expand Down
50 changes: 32 additions & 18 deletions src/dune_rules/dune_file.ml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ module Js_of_ocaml = struct
{ flags = Ordered_set_lang.Unexpanded.standard; javascript_files = [] }
end

type for_ =
| Executable
| Library of Wrapped.t option

module Lib_deps = struct
type t = Lib_dep.t list

Expand All @@ -53,9 +57,16 @@ module Lib_deps = struct
| Optional
| Forbidden

let decode ~allow_re_export =
let decode for_ =
let+ loc = loc
and+ t = repeat (Lib_dep.decode ~allow_re_export) in
and+ t =
let allow_re_export =
match for_ with
| Library _ -> true
| Executable -> false
in
repeat (Lib_dep.decode ~allow_re_export)
in
let add kind name acc =
match Lib_name.Map.find acc name with
| None -> Lib_name.Map.set acc name kind
Expand Down Expand Up @@ -103,7 +114,7 @@ module Lib_deps = struct
let info t ~kind =
List.concat_map t ~f:(function
| Lib_dep.Re_export (_, s)
| Lib_dep.Direct (_, s) ->
| Direct (_, s) ->
[ (s, kind) ]
| Select { choices; _ } ->
List.concat_map choices ~f:(fun (c : Lib_dep.Select.Choice.t) ->
Expand Down Expand Up @@ -161,18 +172,18 @@ module Buildable = struct
; flags : Ocaml_flags.Spec.t
; js_of_ocaml : Js_of_ocaml.t
; allow_overlapping_dependencies : bool
; root_module : (Loc.t * Module_name.t) option
}

let decode ~in_library ~allow_re_export =
let decode (for_ : for_) =
let use_foreign =
Dune_lang.Syntax.deleted_in Stanza.syntax (2, 0)
~extra_info:"Use the (foreign_stubs ...) field instead."
in
let only_in_library decode =
if in_library then
decode
else
return None
match for_ with
| Executable -> return None
| Library _ -> decode
in
let add_stubs language ~loc ~names ~flags foreign_stubs =
match names with
Expand Down Expand Up @@ -219,8 +230,7 @@ module Buildable = struct
>>> enter (maybe string) )))
and+ modules_without_implementation =
Stanza_common.modules_field "modules_without_implementation"
and+ libraries =
field "libraries" (Lib_deps.decode ~allow_re_export) ~default:[]
and+ libraries = field "libraries" (Lib_deps.decode for_) ~default:[]
and+ flags = Ocaml_flags.Spec.decode
and+ js_of_ocaml =
field "js_of_ocaml" Js_of_ocaml.decode ~default:Js_of_ocaml.default
Expand Down Expand Up @@ -254,6 +264,9 @@ module Buildable = struct
repeat (String_with_vars.decode >>| version_check)
in
(libname, flags))) ))
and+ root_module =
field_o "root_module"
(Dune_lang.Syntax.since Stanza.syntax (2, 8) >>> Module_name.decode_loc)
in
let preprocess =
let init =
Expand Down Expand Up @@ -309,6 +322,7 @@ module Buildable = struct
; flags
; js_of_ocaml
; allow_overlapping_dependencies
; root_module
}

let has_foreign t =
Expand Down Expand Up @@ -564,8 +578,9 @@ module Library = struct
let decode =
fields
(let* stanza_loc = loc in
let* wrapped = Wrapped.field in
let* dune_version = Dune_lang.Syntax.get_exn Stanza.syntax in
let+ buildable = Buildable.decode ~in_library:true ~allow_re_export:true
let+ buildable = Buildable.decode (Library (Option.map ~f:snd wrapped))
and+ name = field_o "name" Lib_name.Local.decode_loc
and+ public =
field_o "public_name" (Public_lib.decode ~allow_deprecated_names:false)
Expand All @@ -585,7 +600,6 @@ module Library = struct
field "modes" Mode_conf.Set.decode
~default:(Mode_conf.Set.default stanza_loc)
and+ kind = field "kind" Lib_kind.decode ~default:Lib_kind.Normal
and+ wrapped = Wrapped.field
and+ optional = field_b "optional"
and+ no_dynlink = field_b "no_dynlink"
and+ () =
Expand Down Expand Up @@ -902,13 +916,14 @@ module Library = struct
let wrapped = Some conf.wrapped in
let special_builtin_support = conf.special_builtin_support in
let instrumentation_backend = conf.instrumentation_backend in
let entry_modules = Lib_info.Source.Local in
Lib_info.create ~loc ~name ~kind ~status ~src_dir ~orig_src_dir ~obj_dir
~version ~synopsis ~main_module_name ~sub_systems ~requires
~foreign_objects ~plugins ~archives ~ppx_runtime_deps ~foreign_archives
~native_archives ~foreign_dll_files ~jsoo_runtime ~jsoo_archive
~preprocess ~enabled ~virtual_deps ~dune_version ~virtual_ ~implements
~default_implementation ~modes ~wrapped ~special_builtin_support
~exit_module ~instrumentation_backend
~preprocess ~enabled ~virtual_deps ~dune_version ~virtual_ ~entry_modules
~implements ~default_implementation ~modes ~wrapped
~special_builtin_support ~exit_module ~instrumentation_backend
end

module Plugin = struct
Expand Down Expand Up @@ -1347,7 +1362,7 @@ module Executables = struct

let common =
let* dune_version = Dune_lang.Syntax.get_exn Stanza.syntax in
let+ buildable = Buildable.decode ~in_library:false ~allow_re_export:false
let+ buildable = Buildable.decode Executable
and+ (_ : bool) =
field "link_executables" ~default:true
(Dune_lang.Syntax.deleted_in Stanza.syntax (1, 0) >>> bool)
Expand Down Expand Up @@ -1772,8 +1787,7 @@ module Tests = struct

let gen_parse names =
fields
(let+ buildable =
Buildable.decode ~in_library:false ~allow_re_export:false
(let+ buildable = Buildable.decode Executable
and+ link_flags = Ordered_set_lang.Unexpanded.field "link_flags"
and+ names = names
and+ package = field_o "package" Stanza_common.Pkg.decode
Expand Down
7 changes: 6 additions & 1 deletion src/dune_rules/dune_file.mli
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ module Js_of_ocaml : sig
val default : t
end

type for_ =
| Executable
| Library of Wrapped.t option

module Lib_deps : sig
type nonrec t = Lib_dep.t list

val of_pps : Lib_name.t list -> t

val info : t -> kind:Lib_deps_info.Kind.t -> Lib_deps_info.t

val decode : allow_re_export:bool -> t Dune_lang.Decoder.t
val decode : for_ -> t Dune_lang.Decoder.t
end

(** [preprocess] and [preprocessor_deps] fields *)
Expand All @@ -49,6 +53,7 @@ module Buildable : sig
; flags : Ocaml_flags.Spec.t
; js_of_ocaml : Js_of_ocaml.t
; allow_overlapping_dependencies : bool
; root_module : (Loc.t * Module_name.t) option
}

(** Check if the buildable has any foreign stubs or archives. *)
Expand Down
18 changes: 10 additions & 8 deletions src/dune_rules/dune_package.ml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ module Lib = struct
and+ orig_src_dir = field_o "orig_src_dir" path
and+ modules =
let src_dir = Obj_dir.dir obj_dir in
field_o "modules"
field "modules"
(Modules.decode
~implements:(Option.is_some implements)
~src_dir ~version:lang.version)
Expand All @@ -153,6 +153,9 @@ module Lib = struct
field_o "instrumentation.backend" (located Lib_name.decode)
in
let modes = Mode.Dict.Set.of_list modes in
let entry_modules =
Modules.entry_modules modules |> List.map ~f:Module.name
in
let info : Path.t Lib_info.t =
let src_dir = Obj_dir.dir obj_dir in
let enabled = Lib_info.Enabled_status.Normal in
Expand All @@ -170,25 +173,24 @@ module Lib = struct
let dune_version = None in
let virtual_ =
if virtual_ then
let modules = Option.value_exn modules in
Some (Lib_info.Source.External modules)
else
None
in
let wrapped =
Option.map modules ~f:Modules.wrapped
|> Option.map ~f:(fun w -> Lib_info.Inherited.This w)
Some (Lib_info.Inherited.This (Modules.wrapped modules))
in
let entry_modules = Lib_info.Source.External (Ok entry_modules) in
Lib_info.create ~loc ~name ~kind ~status ~src_dir ~orig_src_dir
~obj_dir ~version ~synopsis ~main_module_name ~sub_systems ~requires
~foreign_objects ~plugins ~archives ~ppx_runtime_deps
~foreign_archives ~native_archives ~foreign_dll_files:[]
~jsoo_runtime ~jsoo_archive ~preprocess ~enabled ~virtual_deps
~dune_version ~virtual_ ~implements ~default_implementation ~modes
~wrapped ~special_builtin_support ~exit_module:None
~instrumentation_backend
~dune_version ~virtual_ ~entry_modules ~implements
~default_implementation ~modes ~wrapped ~special_builtin_support
~exit_module:None ~instrumentation_backend
in
{ info; main_module_name; modules })
{ info; main_module_name; modules = Some modules })

let modules t = t.modules

Expand Down
Loading