Merge pull request #462 from rgrinberg/scope-ppx-bug-new-scopes

Fix scoping related ppx bug
This commit is contained in:
Rudi Grinberg 2018-01-31 00:55:41 +08:00 committed by GitHub
commit a80d70aa26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 119 additions and 29 deletions

View File

@ -862,7 +862,7 @@ Add it to your jbuild file to remove this warning.
{ m with source_dirs = { m with source_dirs =
Path.Set.add (Path.relative src_dir ".") m.source_dirs Path.Set.add (Path.relative src_dir ".") m.source_dirs
}) })
|> Option.iter ~f:(Merlin.add_rules sctx ~dir:ctx_dir); |> Option.iter ~f:(Merlin.add_rules sctx ~dir:ctx_dir ~scope);
Option.iter (Utop.exe_stanzas stanzas) ~f:(fun (exe, all_modules) -> Option.iter (Utop.exe_stanzas stanzas) ~f:(fun (exe, all_modules) ->
let dir = Utop.utop_exe_dir ~dir:ctx_dir in let dir = Utop.utop_exe_dir ~dir:ctx_dir in
let merlin = executables_rules exe ~dir ~all_modules ~scope in let merlin = executables_rules exe ~dir ~all_modules ~scope in
@ -1003,7 +1003,8 @@ Add it to your jbuild file to remove this warning.
else else
pps pps
in in
let ppx_exe = SC.PP.get_ppx_driver sctx pps in let scope = Lib_db.find_scope' (SC.libs sctx) ~dir in
let ppx_exe = SC.PP.get_ppx_driver sctx ~scope pps in
[ppx_exe] [ppx_exe]
in in
List.concat List.concat

View File

@ -101,3 +101,7 @@ let remove_dups_preserve_order libs =
in in
loop String_set.empty libs [] loop String_set.empty libs []
;; ;;
let public_name = function
| External pkg -> Some pkg.name
| Internal (_, lib) -> Option.map lib.public ~f:(fun p -> p.name)

View File

@ -33,3 +33,5 @@ val remove_dups_preserve_order : t list -> t list
(*val ppx_runtime_libraries : t list -> String_set.t (*val ppx_runtime_libraries : t list -> String_set.t
*) *)
val public_name : t -> string option

View File

@ -17,6 +17,7 @@ type t =
instalable_internal_libs : Lib.Internal.t String_map.t instalable_internal_libs : Lib.Internal.t String_map.t
; local_public_libs : Path.t String_map.t ; local_public_libs : Path.t String_map.t
; anonymous_root : Path.t ; anonymous_root : Path.t
; by_scope_name : (string, scope) Hashtbl.t
} }
let rec internal_name_scope t ~dir = let rec internal_name_scope t ~dir =
@ -144,6 +145,9 @@ module Scope = struct
Some { dst_fn = result_fn; src_fn }) Some { dst_fn = result_fn; src_fn })
let root t = t.scope.scope.root let root t = t.scope.scope.root
let name t =
Option.value ~default:"" t.scope.scope.name
let resolve t = let resolve t =
(* TODO do something with required_by here *) (* TODO do something with required_by here *)
Jbuild.Scope.resolve t.data.scope.scope Jbuild.Scope.resolve t.data.scope.scope
@ -273,15 +277,22 @@ let create findlib ~scopes ~root internal_libraries =
; instalable_internal_libs = String_map.empty ; instalable_internal_libs = String_map.empty
; local_public_libs ; local_public_libs
; anonymous_root = root ; anonymous_root = root
; by_scope_name = Hashtbl.create 1024
} }
in in
(* Initializes the scopes, including [Path.root] so that when there are no <pkg>.opam (* Initializes the scopes, including [Path.root] so that when there are no <pkg>.opam
files in parent directories, the scope is the whole workspace. *) files in parent directories, the scope is the whole workspace. *)
List.iter scopes ~f:(fun (scope : Jbuild.Scope.t) -> List.iter scopes ~f:(fun (scope : Jbuild.Scope.t) ->
Hashtbl.add t.by_internal_name ~key:scope.root let lib_scope = { libs = String_map.empty; scope } in
~data:{ libs = String_map.empty Option.iter scope.name ~f:(fun name ->
; scope assert (name <> "");
}); assert (not (Hashtbl.mem t.by_scope_name name));
Hashtbl.add t.by_scope_name ~key:name ~data:lib_scope;
);
Hashtbl.add t.by_internal_name ~key:scope.root ~data:lib_scope
);
let anon_scope = internal_name_scope t ~dir:t.anonymous_root in
Hashtbl.add t.by_scope_name ~key:"" ~data:anon_scope;
List.iter internal_libraries ~f:(fun ((dir, lib) as internal) -> List.iter internal_libraries ~f:(fun ((dir, lib) as internal) ->
let scope = internal_name_scope t ~dir in let scope = internal_name_scope t ~dir in
scope.libs <- String_map.add scope.libs ~key:lib.Library.name ~data:internal; scope.libs <- String_map.add scope.libs ~key:lib.Library.name ~data:internal;
@ -326,3 +337,8 @@ let anonymous_scope t =
lib_db = t lib_db = t
; scope = internal_name_scope t ~dir:t.anonymous_root ; scope = internal_name_scope t ~dir:t.anonymous_root
} }
let find_scope_by_name_exn t ~name =
match Hashtbl.find t.by_scope_name name with
| None -> die "Invalid scope '%s'" name
| Some scope -> { Scope.scope ; lib_db = t }

View File

@ -34,6 +34,7 @@ module Scope : sig
val lib_is_available : t with_required_by -> string -> bool val lib_is_available : t with_required_by -> string -> bool
val root : t -> Path.t val root : t -> Path.t
val name : t -> string
val resolve : t with_required_by -> string -> (Package.t, string) result val resolve : t with_required_by -> string -> (Package.t, string) result
@ -87,3 +88,7 @@ val anonymous_scope : t -> Scope.t
(** Contains only publicly, and external (findlib) libraries *) (** Contains only publicly, and external (findlib) libraries *)
val external_scope : t -> Scope.t val external_scope : t -> Scope.t
(** Find scope by the their explicit names (opam package names) [""] corresponds
to the anonymous scope *)
val find_scope_by_name_exn : t -> name:string -> Scope.t

View File

@ -12,12 +12,12 @@ type t =
; source_dirs: Path.Set.t ; source_dirs: Path.Set.t
} }
let ppx_flags sctx ~dir:_ ~src_dir:_ { preprocess; libname; _ } = let ppx_flags sctx ~dir:_ ~scope ~src_dir:_ { preprocess; libname; _ } =
match preprocess with match preprocess with
| Pps { pps; flags } -> | Pps { pps; flags } ->
let exe = SC.PP.get_ppx_driver sctx pps in let exe = SC.PP.get_ppx_driver sctx ~scope pps in
let command = let command =
List.map (Path.to_absolute_filename exe List.map (Path.to_absolute_filename exe
:: "--as-ppx" :: "--as-ppx"
:: SC.PP.cookie_library_name libname :: SC.PP.cookie_library_name libname
@ flags) @ flags)
@ -27,7 +27,7 @@ let ppx_flags sctx ~dir:_ ~src_dir:_ { preprocess; libname; _ } =
[sprintf "FLG -ppx %s" (Filename.quote command)] [sprintf "FLG -ppx %s" (Filename.quote command)]
| _ -> [] | _ -> []
let dot_merlin sctx ~dir ({ requires; flags; _ } as t) = let dot_merlin sctx ~dir ~scope ({ requires; flags; _ } as t) =
match Path.drop_build_context dir with match Path.drop_build_context dir with
| Some remaindir -> | Some remaindir ->
let merlin_file = Path.relative dir ".merlin" in let merlin_file = Path.relative dir ".merlin" in
@ -46,7 +46,7 @@ let dot_merlin sctx ~dir ({ requires; flags; _ } as t) =
SC.add_rule sctx ~mode:Promote_but_delete_on_clean ( SC.add_rule sctx ~mode:Promote_but_delete_on_clean (
requires &&& flags requires &&& flags
>>^ (fun (libs, flags) -> >>^ (fun (libs, flags) ->
let ppx_flags = ppx_flags sctx ~dir ~src_dir:remaindir t in let ppx_flags = ppx_flags sctx ~dir ~scope ~src_dir:remaindir t in
let internals, externals = let internals, externals =
List.fold_left libs ~init:([], []) ~f:(fun (internals, externals) -> List.fold_left libs ~init:([], []) ~f:(fun (internals, externals) ->
function function
@ -117,6 +117,6 @@ let merge_all = function
| [] -> None | [] -> None
| init::ts -> Some (List.fold_left ~init ~f:merge_two ts) | init::ts -> Some (List.fold_left ~init ~f:merge_two ts)
let add_rules sctx ~dir merlin = let add_rules sctx ~dir ~scope merlin =
if (SC.context sctx).merlin then if (SC.context sctx).merlin then
dot_merlin sctx ~dir merlin dot_merlin sctx ~dir ~scope merlin

View File

@ -11,5 +11,10 @@ type t =
val merge_all : t list -> t option val merge_all : t list -> t option
(** Add rules for generating the .merlin in a directory *) (** Add rules for generating the .merlin in a directory *)
val add_rules : Super_context.t -> dir:Path.t -> t -> unit val add_rules
: Super_context.t
-> dir:Path.t
-> scope:Lib_db.Scope.t Lib_db.with_required_by
-> t
-> unit

View File

@ -853,6 +853,12 @@ module PP = struct
| [key] -> | [key] ->
let ppx_dir = Path.relative sctx.ppx_dir key in let ppx_dir = Path.relative sctx.ppx_dir key in
let exe = Path.relative ppx_dir "ppx.exe" in let exe = Path.relative ppx_dir "ppx.exe" in
let (key, scope) =
match String.rsplit2 key ~on:'@' with
| None ->
(key, Libs.external_scope sctx)
| Some (key, scope) ->
(key, Lib_db.find_scope_by_name_exn sctx.libs ~name:scope) in
let names = let names =
match key with match key with
| "+none+" -> [] | "+none+" -> []
@ -866,27 +872,57 @@ module PP = struct
in in
let scope = let scope =
{ Lib_db. { Lib_db.
data = Lib_db.anonymous_scope sctx.libs data = scope
; required_by = [sprintf "required by (pps (%s))" ; required_by = [sprintf "required by (pps (%s))"
(String.concat names ~sep:", ")] (String.concat names ~sep:", ")]
} in } in
build_ppx_driver sctx names ~scope ~dep_kind:Required ~target:exe ~driver build_ppx_driver sctx names ~scope ~dep_kind:Required ~target:exe ~driver
| _ -> () | _ -> ()
let get_ppx_driver sctx pps = let get_ppx_driver sctx ~scope pps =
let names = let (driver, names) =
match List.rev_map pps ~f:Pp.to_string with match List.rev_map pps ~f:Pp.to_string with
| [] -> [] | [] -> (None, [])
| driver :: rest -> | driver :: rest -> (Some driver, rest)
List.sort rest ~cmp:String.compare @ [driver]
in in
let sctx = host_sctx sctx in
let public_name name =
match Lib_db.Scope.find scope name with
| None -> Some name (* XXX unknown but assume it's public *)
| Some lib -> Lib.public_name lib in
let (driver_private, driver) =
match driver with
| None -> (false, None)
| Some driver ->
begin match public_name driver with
| None -> (true, Some driver)
| Some driver -> (false, Some driver)
end in
let (libs, has_private_libs) =
List.fold_left ~f:(fun (libs, has_private_libs) lib ->
match public_name lib with
| None -> (lib :: libs, true)
| Some pub_name -> (pub_name :: libs, has_private_libs)
) ~init:([], driver_private) names in
let libs = List.sort ~cmp:String.compare libs in
let names =
match driver with
| None -> libs
| Some driver -> libs @ [driver] in
let key = let key =
match names with match names with
| [] -> "+none+" | [] -> "+none+"
| _ -> String.concat names ~sep:"+" | _ -> String.concat names ~sep:"+"
in in
let sctx = host_sctx sctx in let sctx = host_sctx sctx in
let ppx_dir = Path.relative sctx.ppx_dir key in let ppx_dir =
Path.relative sctx.ppx_dir (
if has_private_libs then (
sprintf "%s@%s" key (Lib_db.Scope.name scope.data)
) else (
key
)
) in
Path.relative ppx_dir "ppx.exe" Path.relative ppx_dir "ppx.exe"
let target_var = String_with_vars.virt_var __POS__ "@" let target_var = String_with_vars.virt_var __POS__ "@"
@ -971,7 +1007,7 @@ module PP = struct
~scope) ~scope)
) )
| Pps { pps; flags } -> | Pps { pps; flags } ->
let ppx_exe = get_ppx_driver sctx pps in let ppx_exe = get_ppx_driver sctx ~scope pps in
Module.iter ast ~f:(fun kind src -> Module.iter ast ~f:(fun kind src ->
let src_path = Path.relative dir src.name in let src_path = Path.relative dir src.name in
let args = let args =
@ -1035,7 +1071,7 @@ module PP = struct
lint_module ~ast ~source:m; lint_module ~ast ~source:m;
ast ast
| Pps { pps; flags } -> | Pps { pps; flags } ->
let ppx_exe = get_ppx_driver sctx pps in let ppx_exe = get_ppx_driver sctx ~scope pps in
let ast = setup_reason_rules sctx ~dir m in let ast = setup_reason_rules sctx ~dir m in
lint_module ~ast ~source:m; lint_module ~ast ~source:m;
let uses_ppx_driver = uses_ppx_driver ~pps in let uses_ppx_driver = uses_ppx_driver ~pps in

View File

@ -216,7 +216,11 @@ module PP : sig
-> Module.t String_map.t -> Module.t String_map.t
(** Get a path to a cached ppx driver *) (** Get a path to a cached ppx driver *)
val get_ppx_driver : t -> Pp.t list -> Path.t val get_ppx_driver
: t
-> scope:Lib_db.Scope.t Lib_db.with_required_by
-> Pp.t list
-> Path.t
(** [cookie_library_name lib_name] is ["--cookie"; lib_name] if [lib_name] is not (** [cookie_library_name lib_name] is ["--cookie"; lib_name] if [lib_name] is not
[None] *) [None] *)

View File

@ -3,7 +3,7 @@
ocamlc ppx/fooppx.{cmi,cmo,cmt} ocamlc ppx/fooppx.{cmi,cmo,cmt}
ocamlopt ppx/fooppx.{cmx,o} ocamlopt ppx/fooppx.{cmx,o}
ocamlopt ppx/fooppx.{a,cmxa} ocamlopt ppx/fooppx.{a,cmxa}
ocamlopt .ppx/fooppx/ppx.exe ocamlopt .ppx/fooppx@/ppx.exe
ppx w_omp_driver.pp.ml ppx w_omp_driver.pp.ml
ocamldep w_omp_driver.depends.ocamldep-output ocamldep w_omp_driver.depends.ocamldep-output
ocamlc w_omp_driver.{cmi,cmo,cmt} ocamlc w_omp_driver.{cmi,cmo,cmt}

View File

@ -1,5 +1,22 @@
$ $JBUILDER build -j1 --root . @install $ $JBUILDER build -j1 --root . @install
Error: External library "a_kernel" not found. ocamldep a/ppx/a.depends.ocamldep-output
-> required by "required by (pps (a_kernel))" ocamlc a/ppx/a.{cmi,cmo,cmt}
Hint: try: jbuilder external-lib-deps --missing --root . @install ocamldep a/kernel/a_kernel.depends.ocamldep-output
[1] ocamlc a/kernel/a_kernel.{cmi,cmo,cmt}
ocamlopt a/ppx/a.{cmx,o}
ocamlc a/ppx/a.cma
ocamlopt a/kernel/a_kernel.{cmx,o}
ocamlc a/kernel/a_kernel.cma
ocamlopt a/ppx/a.{a,cmxa}
ocamlopt a/kernel/a_kernel.{a,cmxa}
ocamlopt a/ppx/a.cmxs
ocamlopt a/kernel/a_kernel.cmxs
ocamlopt .ppx/a.kernel/ppx.exe
ocamlopt .ppx/a/ppx.exe
ppx b/b.pp.ml
ocamldep b/b.depends.ocamldep-output
ocamlc b/b.{cmi,cmo,cmt}
ocamlopt b/b.{cmx,o}
ocamlc b/b.cma
ocamlopt b/b.{a,cmxa}
ocamlopt b/b.cmxs