Skip to content
Open
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
2 changes: 1 addition & 1 deletion bin/describe/describe_external_lib_deps.ml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ let resolve_lib_deps db lib_deps =
let open Memo.O in
Memo.parallel_map lib_deps ~f:(fun (lib : Lib_dep.t) ->
match lib with
| Direct (_, name) | Re_export (_, name) ->
| Direct (_, name) | Re_export (_, name) | Instantiate { lib = name; _ } ->
let+ v = resolve_lib db name Kind.Required in
[ v ]
| Select select ->
Expand Down
2 changes: 1 addition & 1 deletion doc/reference/dune/library.rst
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ order to declare a multi-directory library, you need to use the
List the library parameters used by the library and its dependencies.

This feature is experimental and requires the compiler you are using to
support parameterized libraries.
support parameterised libraries.
See :doc:`/reference/dune/library_parameter`.

.. describe:: (js_of_ocaml ...)
Expand Down
2 changes: 1 addition & 1 deletion doc/reference/dune/library_parameter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ library_parameter
.. warning::

This feature is experimental and requires the compiler you are using to
support parameterized libraries.
support parameterised libraries.

The ``library_parameter`` stanza describes a parameter interface defined in a single ``.mli`` file. To enable this feature,
you need to add ``(using oxcaml 0.1)`` :doc:`extension
Expand Down
66 changes: 66 additions & 0 deletions doc/reference/library-dependencies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,69 @@ be able to see ``foo`` independently of whether :doc:`implicit
transitive dependencies<dune-project/implicit_transitive_deps>` are
allowed or not. When they are allowed, which is the default, all transitive
dependencies are visible, whether they are marked as re-exported or not.

Instantiating Parameterised Dependencies
----------------------------------------

This feature requires OxCaml, see :doc:`/reference/dune/library_parameter`.

A parameterised dependency ``foo`` can be instantiated with the arguments
``bar``, ``qux`` using the syntax:

.. code:: dune

(foo bar qux)

For example:

.. code:: dune

(library
(name test)
(libraries (foo bar qux)))

The library ``foo`` must have declared the set of parameters it expects, and
the arguments given to the instantiation must implement a subset of these
parameters. The ordering of the arguments does not matter, as the instantiation
relies on the implemented parameter to uniquely identify each argument.
For executables, the parameterised dependencies must be fully instantiated.

In the OCaml code, the instantiated library will be available under the module
name ``Foo``. To avoiding overlapping module names when instantiating the same
dependency multiple times, the syntax ``:as`` allows renaming the module. For
example:

.. code:: dune

(library
(name test)
(libraries
(foo a b :as foo_a_b)
(foo bar qux :as foo_bar_qux)))

Then the instantiations will be available under the names ``Foo_a_b`` and
``Foo_bar_qux``.

Dependencies automatically inherit the parameters of their parent library.
For example, assuming the parameterised library ``foo`` requires two
parameters ``p`` and ``q``:

.. code:: dune

(library
(name test)
(parameters p q)
(libraries
(foo :as foo_implicit)
(foo an_implementation_of_q :as foo_q)
(foo bar qux :as foo_bar_qux)
other_foo))

Then ``foo_implicit`` is implicitly ``(foo p q)``,
while ``(foo an_implementation_of_q)`` will only inherit the parameter ``p``.

If ``other_foo``, which is not explicitly instantiated here, is also
parameterised by the parameters ``p`` (and) or ``q``, it will also inherit
its parent arguments. Dune will report an error if a dependency requires
parameters which have neither been given explicitly given via an instantiation
and are not listed in the parent library parameters.
43 changes: 43 additions & 0 deletions src/dune_lang/lib_dep.ml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ type t =
| Direct of (Loc.t * Lib_name.t)
| Re_export of (Loc.t * Lib_name.t)
| Select of Select.t
| Instantiate of
{ loc : Loc.t
; lib : Lib_name.t
; arguments : (Loc.t * Lib_name.t) list
; new_name : Module_name.t option
}

let equal = Poly.equal

Expand All @@ -107,6 +113,13 @@ let to_dyn =
| Direct (_, name) -> Lib_name.to_dyn name
| Re_export (_, name) -> variant "re_export" [ Lib_name.to_dyn name ]
| Select s -> variant "select" [ Select.to_dyn s ]
| Instantiate { lib; arguments; new_name; loc = _ } ->
variant
"instantiate"
[ Lib_name.to_dyn lib
; list (fun (_, arg) -> Lib_name.to_dyn arg) arguments
; option Module_name.to_dyn new_name
]
;;

let direct x = Direct x
Expand All @@ -126,6 +139,16 @@ let decode ~allow_re_export =
, let+ select = Select.decode in
Select select )
]
<|> enter
(let+ () = Syntax.since Oxcaml.syntax (0, 1)
and+ loc, lib = located Lib_name.decode
and+ arguments, new_name =
until_keyword
":as"
~before:(located Lib_name.decode)
~after:Module_name.decode
in
Instantiate { loc; lib; arguments; new_name })
<|> let+ loc, name = located Lib_name.decode in
Direct (loc, name))
in
Expand All @@ -144,11 +167,22 @@ let encode =
Code_error.raise
"Lib_dep.encode: cannot encode select"
[ "select", Select.to_dyn select ]
| Instantiate { lib; arguments; new_name; loc = _ } ->
let as_name =
match new_name with
| None -> []
| Some new_name -> [ string ":as"; Module_name.encode new_name ]
in
list
sexp
((Lib_name.encode lib :: List.map arguments ~f:(fun (_, arg) -> Lib_name.encode arg))
@ as_name)
;;

module L = struct
type kind =
| Required
| Required_multiple
| Optional
| Forbidden

Expand Down Expand Up @@ -186,12 +220,21 @@ module L = struct
[ Pp.textf
"library %S is present both as a forbidden and required dependency"
(Lib_name.to_string name)
]
| Required_multiple, Required_multiple -> acc
| Required_multiple, _ | _, Required_multiple ->
User_error.raise
~loc
[ Pp.textf
"parameterised library %S is present in multiple forms"
(Lib_name.to_string name)
])
in
ignore
(List.fold_left t ~init:Lib_name.Map.empty ~f:(fun acc x ->
match x with
| Re_export (_, s) | Direct (_, s) -> add Required s acc
| Instantiate { lib = s; _ } -> add Required_multiple s acc
| Select { choices; _ } ->
List.fold_left choices ~init:acc ~f:(fun acc (c : Select.Choice.t) ->
let acc = Lib_name.Set.fold c.required ~init:acc ~f:(add Optional) in
Expand Down
6 changes: 6 additions & 0 deletions src/dune_lang/lib_dep.mli
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ type t =
| Direct of (Loc.t * Lib_name.t)
| Re_export of (Loc.t * Lib_name.t)
| Select of Select.t
| Instantiate of
{ loc : Loc.t
; lib : Lib_name.t
; arguments : (Loc.t * Lib_name.t) list
; new_name : Module_name.t option
}

val equal : t -> t -> bool
val to_dyn : t -> Dyn.t
Expand Down
6 changes: 5 additions & 1 deletion src/dune_lang/oxcaml.ml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
open Import

let latest_version = 0, 1

let syntax =
Syntax.create
~name:"oxcaml"
~desc:"experimental support for OxCaml"
~experimental:true
[ (0, 1), `Since (3, 20) ]
[ latest_version, `Since (3, 20) ]
;;

let parameterised_dir = ".parameterised"
2 changes: 2 additions & 0 deletions src/dune_lang/oxcaml.mli
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
open Import

val syntax : Syntax.t
val latest_version : Syntax.Version.t
val parameterised_dir : string
13 changes: 13 additions & 0 deletions src/dune_rules/compilation_context.ml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ type t =
; requires_link : Lib.t list Resolve.t Memo.Lazy.t
; implements : Virtual_rules.t
; parameters : Module_name.t list Resolve.Memo.t
; instances : Parameterised_rules.instances list Resolve.Memo.t option
; includes : Includes.t
; preprocessing : Pp_spec.t
; opaque : bool
Expand Down Expand Up @@ -162,6 +163,7 @@ let create
?modes
?bin_annot
?loc
?instances
()
=
let project = Scope.project scope in
Expand Down Expand Up @@ -236,6 +238,7 @@ let create
; bin_annot
; loc
; ocaml
; instances
}
;;

Expand All @@ -256,6 +259,15 @@ let for_alias_module t alias_module =
let profile = Super_context.context t.super_context |> Context.profile in
Ocaml_flags.default ~dune_version ~profile)
in
let flags =
match t.instances with
| None -> flags
| Some _ ->
(* If the alias file instantiates parameterised libraries,
the [misplace-attribute] warning is currently raised on
[@jane.non_erasable.instances] *)
Ocaml_flags.append_common flags [ "-w"; "-53" ]
in
let sandbox =
(* If the compiler reads the cmi for module alias even with [-w -49
-no-alias-deps], we must sandbox the build of the alias module since the
Expand Down Expand Up @@ -342,3 +354,4 @@ let for_plugin_executable t ~embed_in_plugin_libraries =
let without_bin_annot t = { t with bin_annot = false }
let set_obj_dir t obj_dir = { t with obj_dir }
let set_modes t ~modes = { t with modes }
let instances t = t.instances
2 changes: 2 additions & 0 deletions src/dune_rules/compilation_context.mli
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ val create
-> ?modes:Mode_conf.Set.Details.t Lib_mode.Map.t
-> ?bin_annot:bool
-> ?loc:Loc.t
-> ?instances:Parameterised_rules.instances list Resolve.Memo.t
-> unit
-> t Memo.t

Expand Down Expand Up @@ -90,3 +91,4 @@ val dep_graphs : t -> Dep_graph.t Ml_kind.Dict.t
val loc : t -> Loc.t option
val set_obj_dir : t -> Path.Build.t Obj_dir.t -> t
val set_modes : t -> modes:Lib_mode.Map.Set.t -> t
val instances : t -> Parameterised_rules.instances list Resolve.Memo.t option
2 changes: 1 addition & 1 deletion src/dune_rules/dir_contents.ml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ end = struct
dependencies *)
List.filter_map libraries ~f:(fun dep ->
match (dep : Lib_dep.t) with
| Re_export _ | Direct _ -> None
| Re_export _ | Direct _ | Instantiate _ -> None
| Select s -> Some s.result_fn)
;;

Expand Down
11 changes: 9 additions & 2 deletions src/dune_rules/dune_package.ml
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ module Lib = struct
| Public (_, _) -> Lib_info.Status.Installed
in
let version = None in
let local_main_module_name = main_module_name in
let main_module_name = Lib_info.Inherited.This main_module_name in
let foreign_objects = Lib_info.Source.External foreign_objects in
let public_headers = Lib_info.File_deps.External public_headers in
Expand Down Expand Up @@ -281,6 +282,7 @@ module Lib = struct
~version
~synopsis
~main_module_name
~local_main_module_name
~sub_systems
~requires
~parameters
Expand Down Expand Up @@ -607,11 +609,16 @@ module Or_meta = struct

let parse file lexbuf =
let dir = Path.parent_exn file in
let extensions = [ Dune_lang.Oxcaml.(syntax, latest_version) ] in
let with_extensions decoder =
List.fold_left extensions ~init:decoder ~f:(fun decoder (ext, version) ->
Syntax.set ext (Active version) decoder)
in
match
Vfile.parse_contents lexbuf ~f:(fun lang ->
String_with_vars.set_decoding_env
(Pform.Env.initial ~stanza:lang.version ~extensions:[])
(decode ~lang ~dir))
(Pform.Env.initial ~stanza:lang.version ~extensions)
(with_extensions (decode ~lang ~dir)))
with
| contents -> Ok contents
| exception User_error.E message -> Error message
Expand Down
4 changes: 4 additions & 0 deletions src/dune_rules/exe_rules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ let executables_rules
let* cctx =
let requires_compile = Lib.Compile.direct_requires compile_info in
let requires_link = Lib.Compile.requires_link compile_info in
let instances =
Parameterised_rules.instances ~sctx ~db:(Scope.libs scope) exes.buildable.libraries
in
let js_of_ocaml =
Js_of_ocaml.Mode.Pair.mapi js_of_ocaml ~f:(fun mode x ->
Option.some_if
Expand All @@ -205,6 +208,7 @@ let executables_rules
~opaque:Inherit_from_settings
~melange_package_name:None
~package:exes.package
~instances
in
let lib_config = ocaml.lib_config in
let* requires_compile = Compilation_context.requires_compile cctx in
Expand Down
2 changes: 2 additions & 0 deletions src/dune_rules/findlib.ml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ let to_dune_library (t : Findlib.Package.t) ~dir_contents ~ext_lib ~external_loc
let implements = None in
let parameters = [] in
let orig_src_dir = None in
let local_main_module_name = None in
let main_module_name : Lib_info.Main_module_name.t = This None in
let enabled = Memo.return Lib_info.Enabled_status.Normal in
let requires =
Expand Down Expand Up @@ -252,6 +253,7 @@ let to_dune_library (t : Findlib.Package.t) ~dir_contents ~ext_lib ~external_loc
~version
~synopsis
~main_module_name
~local_main_module_name
~sub_systems
~requires
~parameters
Expand Down
14 changes: 13 additions & 1 deletion src/dune_rules/gen_rules.ml
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,14 @@ let gen_rules_regular_directory (sctx : Super_context.t Memo.t) ~src_dir ~compon
(* XXX sync this list with the pattern matches above. It's quite ugly
we need this, we should rewrite this code to avoid this. *)
Filename.Set.of_list
[ ".js"; "_doc"; "_doc_new"; ".ppx"; ".dune"; ".topmod" ]
[ ".js"
; "_doc"
; "_doc_new"
; ".ppx"
; ".dune"
; ".topmod"
; Dune_lang.Oxcaml.parameterised_dir
]
in
Filename.Set.union automatic toplevel
in
Expand Down Expand Up @@ -605,6 +612,11 @@ let gen_rules ctx sctx ~dir components : Gen_rules.result Memo.t =
~dir
(Subdir_set.of_set (Filename.Set.of_list [ "cc_vendor" ]))
(fun () -> Configurator_rules.gen_rules ctx)
| parameterised_dir :: rest
when String.equal parameterised_dir Dune_lang.Oxcaml.parameterised_dir ->
let* sctx = sctx
and* scope = Scope.DB.find_by_dir dir in
Parameterised_rules.gen_rules ~sctx ~scope ~dir rest
| _ -> gen_rules_regular_directory sctx ~src_dir ~components ~dir
;;

Expand Down
Loading
Loading