Up

module Ocaml_dynloader

: sig

Ocaml plugin loader, from ocaml source file. Wrapper around Dynlink module, including on the fly async ocaml compilation.

This is a low level module, casual user should rather use 'Ocaml_compiler' apart from Make to create a dedicated Plugin loader.

#
module Config : sig
#
type t = {
# pa_files
: string list;
}
#
val t_of_sexp : Sexplib.Sexp.t -> t
#
val sexp_of_t : t -> Sexplib.Sexp.t
end
#
type t

mutable type for loading ocaml modules.

#
type dynloader = t
#
exception Is_not_native

Currently this library works with native code only. Called in bytecode, the function create will return the following exception as an error result.

#
val ocamlopt_opt : string

The convention over the name of the executable inside the archive. All are native executables (.opt)

#
val camlp4o_opt : string
#
val ocamldep_opt : string
#
type 'a create_arguments = ?in_dir:string -> ?include_directories:string list -> ?custom_warnings_spec:string -> ?strict_sequence:bool -> ?cmx_flags:string list -> ?cmxs_flags:string list -> ?trigger_unused_value_warnings_despite_mli:bool -> ?use_cache:Plugin_cache.Config.t -> ?run_plugin_toplevel:[
| `In_async_thread
| `Outside_of_async
] -> 'a
#
val create : (?initialize_compilation_callback:(directory:string -> Config.t option Async.Std.Deferred.Or_error.t) -> ?ocamlopt_opt:string -> ?camlp4o_opt:string -> ?ocamldep_opt:string -> ?pa_files:string list -> unit -> t Async.Std.Deferred.Or_error.t) create_arguments
#
val compilation_config : t -> (string * Config.t option) Core.Std.Or_error.t Async.Std.Lazy_deferred.t

return the lazy_deferred enforcing the initialization of the dynloader.

#
val clean : t -> unit Async.Std.Deferred.Or_error.t

Cleaning the files generated by this Ocaml_dynloader.t from the begining of his life, and try to remove the directory if it is empty once the files have been removed. Doesn't fail if the directory contains other files, keep them and keep the directory in that case. Once cleaned, you cannot use a dynloader anymore, you should just leave it alone and let it be collected by the GC at some point. Using a cleaned dynloader will result in an error.

#
module Univ_constr : sig
#
type 'a t
#
val create : unit -> 'a t
end
#
module type Module_type = sig
#
type t

The type t is the type of a first level module you want to load. This is typically the type of your expected config file, as a top level ocaml module.

The field repr is the concrete OCaml syntax for this module type.

The field univ_constr is used to constr and match_ values of type t, embedded in a value of type Univ.t.

The field univ_constr_repr is the concrete OCaml syntax for the field univ_constr.

Example : CUSTOM(module) M : A.S defined in the library "mylib.cmxa".

     module My_config_loader = Ocaml_plugin.Ocaml_dynloader.Make (
     struct
       type t = (module A.S)
       let repr = "Mylib.A.S"
       let univ_constr = A.univ_constr
       let univ_constr_repr = "Mylib.A.univ_constr"
     end)

repr and univ_constr_repr should be contain complete paths, as it would be used by an ocaml file to link with the shared cmi files, in particular be aware that if you have some 'open' statements in your file, you might have different t and repr, which is a bad practice.

If the module type A.M_intf is defined in a package, you would need to add it in the repr, as it is part of the complete path of the module type ("Mylib" in the example).

#
val t_repr : string
#
val univ_constr : t Univ_constr.t
#
val univ_constr_repr : string
end
#
val find_dependencies : t -> string -> string list Async.Std.Deferred.Or_error.t

find_dependencies t file uses ocamldep to compute the list of .ml and .mli files that file depends on transitively, which you can then pass to load_ocaml_src_files. file must be an .ml file, and all the files it depend on must be in the same folder.

#
module type S = sig
#
type t
#
val load_ocaml_src_files : dynloader -> string list -> t Async.Std.Deferred.Or_error.t

Load a bunch of ocaml files source files (.ml + .mli). The last module's signature should be compatible with the signature X.repr. If the type does not match, there will be an error during OCaml compilation. The files are copied into the compilation directory, and compiled versus a generated mli file including the relevant module signature. This generated file is then dynlinked with the current executable.

The compilation happens using Dynlink.loadfile_private, meaning that the toplevel definition defined in these files are hidden (cannot be referenced) from other modules dynamically loaded afterwards

#
val check_ocaml_src_files : dynloader -> string list -> unit Async.Std.Deferred.Or_error.t

Similar to load_ocaml_src_files, but does not execute the plugin toplevel, just checks that compilation and dynamic linking work.

The following functions are used for step by step use, not for the casual user because they are much more error prone. Prefer load_ocaml_src_files if possible

#
val compile_ocaml_src_files_into_cmxs_file : dynloader -> string list -> output_file:string -> unit Async.Std.Deferred.Or_error.t

This compiles the source files into cmxs file, but does not execute the plugin toplevel. The resulting cmxs file can be loaded by blocking_load_cmxs_file either from the same process or other processes which share the same executable. If compile succeeds, it returns Ok and write the compiled cmxs file into output_file (may override existing file), otherwise it returns Error and won't write to output_file at all.

#
val blocking_load_cmxs_file : string -> t Core.Std.Or_error.t

Dynlink has the following not really wanted property: dynlinking a file with a given filename only works properly the first time. Further dynlinks with the same filename (even a different file) will not load the new module but instead execute the initial module. Some even says that the behavior upon reload depends on the platform. Long story short: don't do that. Dynlink files at most once.

It is worth noting too that this function only works with cmxs files produced by ocaml_plugin's compile_ocaml_src_files_into_cmxs_file. It expects the code to perform some internal library calls, thus it cannot be used with any arbitrary cmxs compiled in some other way. Furthermore this function would return an error even though the cmxs was built with ocaml_plugin when built under a different context (compiler version used, cmi dependencies version, etc.) The intended usage is to have the compilation and loading done using the same executable.

end
#
module Make : functor (X : Module_type) -> S with type t := X.t
#
module Side_effect : S with type t := unit

In some cases, we are only interested by the toplevel side effects of dynlinked modules.

#
module type Side_effect = sig
end
#
val side_effect_univ_constr : (module Side_effect) Univ_constr.t
#
val return_plugin : 'a Univ_constr.t -> (unit -> 'a) -> unit
end