Allow to use installed libraries in jbuild plugins
This commit is contained in:
parent
0462d4a11f
commit
ac372ce63a
|
@ -189,11 +189,34 @@ everything Jbuilder needs to know about.
|
||||||
*** OCaml syntax
|
*** OCaml syntax
|
||||||
|
|
||||||
If a =jbuild= file starts with =(* -*- tuareg -*- *)=, then it is
|
If a =jbuild= file starts with =(* -*- tuareg -*- *)=, then it is
|
||||||
interpreted as an OCaml script that generated the =jbuild= file as
|
interpreted as an OCaml script that generates the =jbuild= file as
|
||||||
described in the rest of this section. The code in the script will
|
described in the rest of this section. The code in the script will
|
||||||
have access to a [[../plugin/jbuild_plugin.mli][Jbuild_plugin]] module containing details about the
|
have access to a [[../plugin/jbuild_plugin.mli][Jbuild_plugin]] module containing details about the
|
||||||
build context it is executed in.
|
build context it is executed in.
|
||||||
|
|
||||||
|
The script can use the directive =#require= to access libraries:
|
||||||
|
|
||||||
|
#+begin_src ocaml
|
||||||
|
#require "base,re";;
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Note that any library required by a =jbuild= file must be part of the
|
||||||
|
installed world. This mean that your project won't be compilable in
|
||||||
|
the same workspace as the required library, unless the library has
|
||||||
|
been previously installed.
|
||||||
|
|
||||||
|
If you don't like the S-expression syntax, then this method gives you
|
||||||
|
a way to use whatever else you want. For instance you could have an
|
||||||
|
API to describe your project in OCaml directly:
|
||||||
|
|
||||||
|
#+begin_src ocaml
|
||||||
|
(* -*- tuareg -*- *)
|
||||||
|
#require "my_jbuild_api";;
|
||||||
|
include My_jbuild_api.Make(Jbuild_plugin);;
|
||||||
|
|
||||||
|
library "foo" ~modules:["plop"; "bidule"];;
|
||||||
|
#+end_src
|
||||||
|
|
||||||
*** Specification
|
*** Specification
|
||||||
|
|
||||||
=jbuild= files are composed of stanzas. For instance a typical
|
=jbuild= files are composed of stanzas. For instance a typical
|
||||||
|
|
|
@ -18,6 +18,7 @@ type t =
|
||||||
; for_host : t option
|
; for_host : t option
|
||||||
; build_dir : Path.t
|
; build_dir : Path.t
|
||||||
; path : Path.t list
|
; path : Path.t list
|
||||||
|
; toplevel_path : Path.t option
|
||||||
; ocaml_bin : Path.t
|
; ocaml_bin : Path.t
|
||||||
; ocaml : Path.t
|
; ocaml : Path.t
|
||||||
; ocamlc : Path.t
|
; ocamlc : Path.t
|
||||||
|
@ -86,6 +87,21 @@ let opam_config_var ~env ~cache var =
|
||||||
Hashtbl.add cache ~key:var ~data:s;
|
Hashtbl.add cache ~key:var ~data:s;
|
||||||
Some s
|
Some s
|
||||||
|
|
||||||
|
let get_env env var =
|
||||||
|
let prefix = var ^ "=" in
|
||||||
|
let rec loop i =
|
||||||
|
if i = Array.length env then
|
||||||
|
None
|
||||||
|
else
|
||||||
|
let entry = env.(i) in
|
||||||
|
if String.is_prefix entry ~prefix then
|
||||||
|
let len_p = String.length prefix in
|
||||||
|
Some (String.sub entry ~pos:len_p ~len:(String.length entry - len_p))
|
||||||
|
else
|
||||||
|
loop (i + 1)
|
||||||
|
in
|
||||||
|
loop 0
|
||||||
|
|
||||||
let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
||||||
let opam_var_cache = Hashtbl.create 128 in
|
let opam_var_cache = Hashtbl.create 128 in
|
||||||
(match kind with
|
(match kind with
|
||||||
|
@ -183,6 +199,7 @@ let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
||||||
; for_host = None
|
; for_host = None
|
||||||
; build_dir
|
; build_dir
|
||||||
; path
|
; path
|
||||||
|
; toplevel_path = Option.map (get_env env "OCAML_TOPLEVEL_PATH") ~f:Path.of_string
|
||||||
|
|
||||||
; ocaml_bin = dir
|
; ocaml_bin = dir
|
||||||
; ocaml = Path.relative dir "ocaml"
|
; ocaml = Path.relative dir "ocaml"
|
||||||
|
|
|
@ -47,6 +47,9 @@ type t =
|
||||||
; (** [PATH] *)
|
; (** [PATH] *)
|
||||||
path : Path.t list
|
path : Path.t list
|
||||||
|
|
||||||
|
; (** [OCAML_TOPLEVEL_PATH] *)
|
||||||
|
toplevel_path : Path.t option
|
||||||
|
|
||||||
; (** Ocaml bin directory with all ocaml tools *)
|
; (** Ocaml bin directory with all ocaml tools *)
|
||||||
ocaml_bin : Path.t
|
ocaml_bin : Path.t
|
||||||
; ocaml : Path.t
|
; ocaml : Path.t
|
||||||
|
|
|
@ -34,6 +34,7 @@ module Jbuilds = struct
|
||||||
let plugin_contents = read_file plugin in
|
let plugin_contents = read_file plugin in
|
||||||
with_file_out (Path.to_string wrapper) ~f:(fun oc ->
|
with_file_out (Path.to_string wrapper) ~f:(fun oc ->
|
||||||
Printf.fprintf oc {|
|
Printf.fprintf oc {|
|
||||||
|
let () = Hashtbl.add Toploop.directive_table "require" (Toploop.Directive_string ignore)
|
||||||
module Jbuild_plugin = struct
|
module Jbuild_plugin = struct
|
||||||
module V1 = struct
|
module V1 = struct
|
||||||
let context = %S
|
let context = %S
|
||||||
|
@ -75,12 +76,43 @@ end
|
||||||
in
|
in
|
||||||
let wrapper = Path.extend_basename generated_jbuild ~suffix:".ml" in
|
let wrapper = Path.extend_basename generated_jbuild ~suffix:".ml" in
|
||||||
ensure_parent_dir_exists generated_jbuild;
|
ensure_parent_dir_exists generated_jbuild;
|
||||||
let _requires =
|
let requires =
|
||||||
create_plugin_wrapper context ~exec_dir:dir ~plugin:file ~wrapper ~target:generated_jbuild
|
create_plugin_wrapper context ~exec_dir:dir ~plugin:file ~wrapper ~target:generated_jbuild
|
||||||
in
|
in
|
||||||
|
let pkgs =
|
||||||
|
List.map requires ~f:(Findlib.find_exn context.findlib)
|
||||||
|
|> Findlib.closure
|
||||||
|
in
|
||||||
|
let includes =
|
||||||
|
List.fold_left pkgs ~init:Path.Set.empty ~f:(fun acc pkg ->
|
||||||
|
Path.Set.add pkg.Findlib.dir acc)
|
||||||
|
|> Path.Set.elements
|
||||||
|
|> List.concat_map ~f:(fun path ->
|
||||||
|
[ "-I"; Path.to_string path ])
|
||||||
|
in
|
||||||
|
let cmas =
|
||||||
|
List.concat_map pkgs ~f:(fun pkg -> pkg.archives.byte)
|
||||||
|
in
|
||||||
|
let args =
|
||||||
|
List.concat
|
||||||
|
[ [ "-I"; "+compiler-libs" ]
|
||||||
|
; includes
|
||||||
|
; cmas
|
||||||
|
; [ Path.reach ~from:dir wrapper ]
|
||||||
|
]
|
||||||
|
in
|
||||||
|
(* CR-someday jdimino: if we want to allow plugins to use findlib:
|
||||||
|
{[
|
||||||
|
let args =
|
||||||
|
match context.toplevel_path with
|
||||||
|
| None -> args
|
||||||
|
| Some path -> "-I" :: Path.reach ~from:dir path :: args
|
||||||
|
in
|
||||||
|
]}
|
||||||
|
*)
|
||||||
Future.run Strict ~dir:(Path.to_string dir) ~env:context.env
|
Future.run Strict ~dir:(Path.to_string dir) ~env:context.env
|
||||||
(Path.to_string context.Context.ocaml)
|
(Path.to_string context.ocaml)
|
||||||
[ Path.reach ~from:dir wrapper ]
|
args
|
||||||
>>= fun () ->
|
>>= fun () ->
|
||||||
let sexps = Sexp_load.many (Path.to_string generated_jbuild) in
|
let sexps = Sexp_load.many (Path.to_string generated_jbuild) in
|
||||||
return (dir, Stanzas.parse sexps ~dir ~visible_packages))
|
return (dir, Stanzas.parse sexps ~dir ~visible_packages))
|
||||||
|
|
Loading…
Reference in New Issue