mte/unikernel/duniverse/mirage/lib/functoria/DSL.mli
2025-11-11 02:07:51 +01:00

210 lines
7.8 KiB
OCaml

(*
* Copyright (c) 2013-2020 Thomas Gazagnaire <thomas@gazagnaire.org>
* Copyright (c) 2013-2020 Anil Madhavapeddy <anil@recoil.org>
* Copyright (c) 2015-2020 Gabriel Radanne <drupyog@zoho.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)
(** The Functoria DSL allows users to describe how to create portable and
flexible applications. It allows to pass application parameters easily using
command-line arguments either at configure-time or at runtime.
Users of the Functoria DSL composes their application by defining a list of
{{!main} module} implementations, specify the command-line {!type-key} that
are required and {{!section-combinators} combine} all of them together using
{{:http://dx.doi.org/10.1017/S0956796807006326} applicative} operators.
The DSL expression is then compiled into an
{{!section-app} application builder}, which will, once evaluated, produced
the final portable and flexible application. *)
(** {1:combinators Combinators} *)
type 'a typ = 'a Type.t
(** The type for values representing module types. *)
val typ : 'a -> 'a typ
(** [type t] is a value representing the module type [t]. *)
val ( @-> ) : 'a typ -> 'b typ -> ('a -> 'b) typ
(** Construct a functor type from a type and an existing functor type. This
corresponds to prepending a parameter to the list of functor parameters. For
example:
{[
kv_ro @-> ip @-> kv_ro
]}
This describes a functor type that accepts two arguments -- a [kv_ro] and an
[ip] device -- and returns a [kv_ro]. *)
type 'a impl = 'a Impl.t
(** The type for values representing module implementations. *)
val ( $ ) : ('a -> 'b) impl -> 'a impl -> 'b impl
(** [m $ a] applies the functor [m] to the module [a]. *)
type abstract_impl = Impl.abstract
(** Same as {!type-impl} but with hidden type. *)
val dep : 'a impl -> abstract_impl
(** [dep t] is the (build-time) dependency towards [t]. *)
(** {1:keys Keys} *)
type 'a key = 'a Key.key
(** The type for configure-time command-line arguments. *)
type 'a runtime_arg = 'a Runtime_arg.arg
(** The type for runtime command-line arguments. *)
val runtime_arg :
pos:string * int * int * int ->
?packages:Package.t list ->
string ->
Runtime_arg.t
(** [runtime_arg ~pos ?packages v] is the runtime argument pointing to the value
[v]. [pos] is expected to be [__POS__]. [packages] specifies in which opam
package the value [v] is defined. *)
type abstract_key = Key.t
(** The type for abstract keys. *)
type context = Context.t
(** The type for keys' parsing context. See {!module-Key.type-context}. *)
type 'a value = 'a Key.value
(** The type for values parsed from the command-line. See {!Key.type-value}. *)
val key : 'a key -> Key.t
(** [key k] is an untyped representation of [k]. *)
val if_impl : bool value -> 'a impl -> 'a impl -> 'a impl
(** [if_impl v impl1 impl2] is [impl1] if [v] is resolved to true and [impl2]
otherwise. *)
val match_impl : 'b value -> default:'a impl -> ('b * 'a impl) list -> 'a impl
(** [match_impl v cases ~default] chooses the implementation amongst [cases] by
matching the [v]'s value. [default] is chosen if no value matches. *)
(** {1:pkg Package dependencies}
For specifying opam package dependencies, the type {!type-package} is used.
It consists of the opam package name, the ocamlfind names, and optional
lower and upper bounds. The version constraints are merged with other
modules. *)
type package = Package.t
(** The type for opam packages. *)
type scope = Package.scope
(** Installation scope of a package. *)
val package :
?scope:scope ->
?build:bool ->
?sublibs:string list ->
?libs:string list ->
?min:string ->
?max:string ->
?pin:string ->
?pin_version:string ->
string ->
package
(** [package ~scope ~build ~sublibs ~libs ~min ~max ~pin opam] is a [package].
[Build] indicates a build-time dependency only, defaults to [false]. The
library name is by default the same as [opam], you can specify [~sublibs] to
add additional sublibraries (e.g. [~sublibs:["mirage"] "foo"] will result in
the library names [["foo"; "foo.mirage"]]. In case the library name is
disjoint (or empty), use [~libs]. Specifying both [~libs] and [~sublibs]
leads to an invalid argument. Version constraints are given as [min]
(inclusive) and [max] (exclusive). If [pin] is provided, a
{{:https://opam.ocaml.org/doc/Manual.html#opamfield-pin-depends}
pin-depends} is generated, [pin_version] is ["dev"] by default. [~scope]
specifies the installation location of the package. *)
(** {1:app Application Builder}
Values of type {!type-impl} are tied to concrete module implementation with
the {!device} and {!main} construct. Module implementations of type
{!type-job} can then be {{!Functoria.Lib.Make.register} registered} into an
application builder. The builder is in charge if parsing the command-line
arguments and of generating code for the final application. See
{!Functoria.Lib} for details. *)
type info = Info.t
(** The type for build information. *)
val main :
?pos:string * int * int * int ->
?packages:package list ->
?packages_v:package list value ->
?local_libs:string list ->
?runtime_args:Runtime_arg.t list ->
?deps:abstract_impl list ->
string ->
'a typ ->
'a impl
(** [main name typ] is the functor [name], having the module type [typ]. The
connect code will call [<name>.start].
- If [packages] or [packages_v] is set, then the given packages are
installed before compiling the current application. *)
(** {1 Devices} *)
type 'a code = 'a Device.code
val code :
pos:string * int * int * int ->
('a, Format.formatter, unit, 'b code) format4 ->
'a
type 'a device = ('a, abstract_impl) Device.t
val of_device : 'a device -> 'a impl
(** [of_device t] is the implementation device [t]. *)
val impl :
?packages:package list ->
?packages_v:package list Key.value ->
?local_libs:string list ->
?install:(Info.t -> Install.t) ->
?install_v:(Info.t -> Install.t Key.value) ->
?keys:Key.t list ->
?runtime_args:Runtime_arg.t list ->
?extra_deps:abstract_impl list ->
?connect:(info -> string -> string list -> 'a code) ->
?dune:(info -> Dune.stanza list) ->
?configure:(info -> unit Action.t) ->
?files:(info -> Fpath.t list) ->
string ->
'a typ ->
'a impl
(** [impl ~packages ~packages_v ~install ~install_v ~keys ~runtime_args
~extra_deps ~connect ~dune ~configure ~files module_name module_type] is an
implementation of the device constructed by the arguments. [packages] and
[packages_v] are the dependencies (where [packages_v] is inside
{!Key.value}). [install] and [install_v] are the install instructions (used
in the generated opam file), [keys] are the configuration-time keys,
[runtime_args] the arguments at runtime, [extra_deps] are a list of extra
dependencies (other implementations), [connect] is the code emitted for
initializing the device, [dune] are dune stanzas added to the build rule,
[configure] are commands executed at the configuration phase, [files] are
files to be added to the list of generated files, [module_name] is the name
of the device module, and [module_type] is the type of the module. *)
(** {1 Jobs} *)
type job = Job.t