Allow to use installed libraries in jbuild plugins

This commit is contained in:
Jérémie Dimino 2017-02-28 06:31:02 +00:00
parent 0462d4a11f
commit ac372ce63a
4 changed files with 79 additions and 4 deletions

View File

@ -189,11 +189,34 @@ everything Jbuilder needs to know about.
*** OCaml syntax
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
have access to a [[../plugin/jbuild_plugin.mli][Jbuild_plugin]] module containing details about the
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
=jbuild= files are composed of stanzas. For instance a typical

View File

@ -18,6 +18,7 @@ type t =
; for_host : t option
; build_dir : Path.t
; path : Path.t list
; toplevel_path : Path.t option
; ocaml_bin : Path.t
; ocaml : Path.t
; ocamlc : Path.t
@ -86,6 +87,21 @@ let opam_config_var ~env ~cache var =
Hashtbl.add cache ~key:var ~data: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 opam_var_cache = Hashtbl.create 128 in
(match kind with
@ -183,6 +199,7 @@ let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
; for_host = None
; build_dir
; path
; toplevel_path = Option.map (get_env env "OCAML_TOPLEVEL_PATH") ~f:Path.of_string
; ocaml_bin = dir
; ocaml = Path.relative dir "ocaml"

View File

@ -47,6 +47,9 @@ type t =
; (** [PATH] *)
path : Path.t list
; (** [OCAML_TOPLEVEL_PATH] *)
toplevel_path : Path.t option
; (** Ocaml bin directory with all ocaml tools *)
ocaml_bin : Path.t
; ocaml : Path.t

View File

@ -34,6 +34,7 @@ module Jbuilds = struct
let plugin_contents = read_file plugin in
with_file_out (Path.to_string wrapper) ~f:(fun oc ->
Printf.fprintf oc {|
let () = Hashtbl.add Toploop.directive_table "require" (Toploop.Directive_string ignore)
module Jbuild_plugin = struct
module V1 = struct
let context = %S
@ -75,12 +76,43 @@ end
in
let wrapper = Path.extend_basename generated_jbuild ~suffix:".ml" in
ensure_parent_dir_exists generated_jbuild;
let _requires =
let requires =
create_plugin_wrapper context ~exec_dir:dir ~plugin:file ~wrapper ~target:generated_jbuild
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
(Path.to_string context.Context.ocaml)
[ Path.reach ~from:dir wrapper ]
(Path.to_string context.ocaml)
args
>>= fun () ->
let sexps = Sexp_load.many (Path.to_string generated_jbuild) in
return (dir, Stanzas.parse sexps ~dir ~visible_packages))