Allow to specify which context for merlin

This commit is contained in:
Jérémie Dimino 2017-02-26 20:53:32 +00:00
parent ff0fa5885f
commit 304d7becbc
7 changed files with 117 additions and 95 deletions

View File

@ -60,7 +60,7 @@ let installed_libraries =
let go common = let go common =
set_common common; set_common common;
Future.Scheduler.go ~log:(create_log ()) Future.Scheduler.go ~log:(create_log ())
(Lazy.force Context.default >>= fun ctx -> (Context.default () >>= fun ctx ->
let findlib = Findlib.create ctx in let findlib = Findlib.create ctx in
let pkgs = Findlib.all_packages findlib in let pkgs = Findlib.all_packages findlib in
let max_len = List.longest_map pkgs ~f:(fun p -> p.name) in let max_len = List.longest_map pkgs ~f:(fun p -> p.name) in

View File

@ -8,6 +8,7 @@ end
type t = type t =
{ name : string { name : string
; kind : Kind.t ; kind : Kind.t
; merlin : bool
; for_host : t option ; for_host : t option
; build_dir : Path.t ; build_dir : Path.t
; path : Path.t list ; path : Path.t list
@ -82,7 +83,7 @@ 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 create ~(kind : Kind.t) ~path ~env ~name = 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
| Opam { root; _ } -> | Opam { root; _ } ->
@ -168,6 +169,7 @@ let create ~(kind : Kind.t) ~path ~env ~name =
let t = let t =
{ name { name
; kind ; kind
; merlin
; for_host = None ; for_host = None
; build_dir ; build_dir
; path ; path
@ -232,7 +234,7 @@ let initial_env = lazy (
Lazy.force Ansi_color.setup_env_for_ocaml_colors; Lazy.force Ansi_color.setup_env_for_ocaml_colors;
Unix.environment ()) Unix.environment ())
let default = lazy ( let default ?(merlin=true) () =
let env = Lazy.force initial_env in let env = Lazy.force initial_env in
let rec find_path i = let rec find_path i =
if i = Array.length env then if i = Array.length env then
@ -244,7 +246,7 @@ let default = lazy (
| _ -> find_path (i + 1) | _ -> find_path (i + 1)
in in
let path = find_path 0 in let path = find_path 0 in
create ~kind:Default ~path ~env ~name:"default") create ~kind:Default ~path ~env ~name:"default" ~merlin
let extend_env ~vars ~env = let extend_env ~vars ~env =
let imported = let imported =
@ -261,7 +263,7 @@ let extend_env ~vars ~env =
imported imported
|> Array.of_list |> Array.of_list
let create_for_opam ?root ~switch ~name () = let create_for_opam ?root ~switch ~name ?(merlin=false) () =
match Bin.opam with match Bin.opam with
| None -> die "Program opam not found in PATH" | None -> die "Program opam not found in PATH"
| Some fn -> | Some fn ->
@ -284,7 +286,7 @@ let create_for_opam ?root ~switch ~name () =
in in
let env = Lazy.force initial_env in let env = Lazy.force initial_env in
create ~kind:(Opam { root; switch }) ~path ~env:(extend_env ~vars ~env) create ~kind:(Opam { root; switch }) ~path ~env:(extend_env ~vars ~env)
~name ~name ~merlin
let which t s = Bin.which ~path:t.path s let which t s = Bin.which ~path:t.path s

View File

@ -28,6 +28,9 @@ type t =
{ name : string { name : string
; kind : Kind.t ; kind : Kind.t
; (** [true] if this context is used for the .merlin files *)
merlin : bool
; (** If this context is a cross-compilation context, you need another context for ; (** If this context is a cross-compilation context, you need another context for
building tools used for the compilation that run on the host. *) building tools used for the compilation that run on the host. *)
for_host : t option for_host : t option
@ -101,10 +104,11 @@ val create_for_opam
: ?root:string : ?root:string
-> switch:string -> switch:string
-> name:string -> name:string
-> ?merlin:bool
-> unit -> unit
-> t Future.t -> t Future.t
val default : t Future.t Lazy.t val default : ?merlin:bool -> unit -> t Future.t
(** All contexts in use, by name *) (** All contexts in use, by name *)
val all : unit -> t String_map.t val all : unit -> t String_map.t

View File

@ -626,7 +626,7 @@ module Gen(P : Params) = struct
) )
) )
let requires ~dir ~dep_kind ~item ~libraries ~preprocess ~virtual_deps = let real_requires ~dir ~dep_kind ~item ~libraries ~preprocess ~virtual_deps =
let all_pps = let all_pps =
Preprocess_map.pps preprocess Preprocess_map.pps preprocess
|> Pp_set.elements |> Pp_set.elements
@ -647,65 +647,65 @@ module Gen(P : Params) = struct
Build.store_vfile vrequires); Build.store_vfile vrequires);
Build.vpath vrequires Build.vpath vrequires
let requires ~dir ~dep_kind ~item ~libraries ~preprocess ~virtual_deps =
let real_requires =
real_requires ~dir ~dep_kind ~item ~libraries ~preprocess ~virtual_deps
in
let requires =
if ctx.merlin then
match Path.extract_build_context dir with
| Some (_, remaindir) ->
Build.path (Path.relative remaindir ".merlin") >>> real_requires
| _ -> real_requires
else
real_requires
in
(requires, real_requires)
let dot_merlin ~dir ~requires ~alias_modules = let dot_merlin ~dir ~requires ~alias_modules =
(* Only generate .merlin file in default context *) if ctx.merlin then
match Path.extract_build_context dir with match Path.extract_build_context dir with
| Some ("default", remaindir) -> | Some (_, remaindir) ->
let path = Path.relative remaindir ".merlin" in let path = Path.relative remaindir ".merlin" in
add_rule ( add_rule (
requires requires
>>^ (fun libs_list -> >>^ (fun libs ->
List.concat libs_list let internals, externals =
|> List.partition_map ~f:(function List.partition_map libs ~f:(function
| Lib.Internal (path, _) -> | Lib.Internal (path, _) ->
let path = Path.reach path ~from:remaindir in let path = Path.reach path ~from:remaindir in
Inl ("B " ^ path) Inl ("B " ^ path)
| Lib.External pkg -> | Lib.External pkg ->
Inr ("PKG " ^ pkg.name))) Inr ("PKG " ^ pkg.name))
>>^ (fun (internals, externals) -> in
let dot_merlin = let dot_merlin =
[ "S ." ; "B " ^ (Path.reach dir ~from:remaindir) ] [ "S ." ; "B " ^ (Path.reach dir ~from:remaindir) ]
@ internals @ internals
@ externals @ externals
@ (List.filter_map alias_modules ~f:(fun x -> @ List.map alias_modules ~f:(fun (m : Module.t) ->
Option.map x ~f:(fun (m : Module.t) -> "FLG -open " ^ m.name)
"FLG -open " ^ m.name))) in
in
let dot_merlin =
dot_merlin dot_merlin
|> String_set.of_list |> String_set.of_list
|> String_set.elements |> String_set.elements
|> List.sort ~cmp:String.compare |> List.map ~f:(Printf.sprintf "%s\n")
in |> String.concat ~sep:"")
String.concat ~sep:"\n" dot_merlin ^ "\n")
>>> >>>
Build.echo path Build.echo path
) )
| _ -> | _ ->
() ()
let merge_dot_merlin merlin_deps ~dir = let merge_dot_merlin merlin_deps ~dir =
merlin_deps if ctx.merlin && merlin_deps <> [] then
|> List.fold_left ~init:None ~f:(fun acc (requires, alias_module) -> let requires, alias_modules = List.split merlin_deps in
match acc with let alias_modules = List.filter_map alias_modules ~f:(fun x -> x) in
| None -> let requires = requires >>^ (fun x -> [x]) in let requires =
Some (requires, [alias_module]) Build.all requires
| Some (acc_requires, acc_alias_modules) -> >>^ fun requires ->
let new_acc_requires = Lib.remove_dups_preserve_order (List.concat requires)
let together = acc_requires &&& requires in in
together dot_merlin ~dir ~requires ~alias_modules
>>^ (fun (acc_requires, requires) ->
requires :: acc_requires)
in
let new_acc_alias_modules =
alias_module :: acc_alias_modules
in
Some (new_acc_requires, new_acc_alias_modules))
|> function
| Some (requires, alias_modules) ->
dot_merlin ~dir ~requires ~alias_modules
| None ->
()
let setup_runtime_deps ~dir ~dep_kind ~item ~libraries ~ppx_runtime_libraries = let setup_runtime_deps ~dir ~dep_kind ~item ~libraries ~ppx_runtime_libraries =
let vruntime_deps = Lib_db.vruntime_deps ~dir ~item in let vruntime_deps = Lib_db.vruntime_deps ~dir ~item in
@ -1049,20 +1049,13 @@ module Gen(P : Params) = struct
|> String.concat ~sep:"") |> String.concat ~sep:"")
>>> Build.echo (Path.relative dir m.ml_fname))); >>> Build.echo (Path.relative dir m.ml_fname)));
let real_requires = let requires, real_requires =
requires ~dir ~dep_kind ~item:lib.name requires ~dir ~dep_kind ~item:lib.name
~libraries:lib.buildable.libraries ~libraries:lib.buildable.libraries
~preprocess:lib.buildable.preprocess ~preprocess:lib.buildable.preprocess
~virtual_deps:lib.virtual_deps ~virtual_deps:lib.virtual_deps
in in
let requires =
match Path.extract_build_context dir with
| Some ("default", remaindir) ->
Build.path (Path.relative remaindir ".merlin") >>> real_requires
| _ -> real_requires
in
setup_runtime_deps ~dir ~dep_kind ~item:lib.name setup_runtime_deps ~dir ~dep_kind ~item:lib.name
~libraries:lib.buildable.libraries ~libraries:lib.buildable.libraries
~ppx_runtime_libraries:lib.ppx_runtime_libraries; ~ppx_runtime_libraries:lib.ppx_runtime_libraries;
@ -1217,21 +1210,13 @@ module Gen(P : Params) = struct
let item = List.hd exes.names in let item = List.hd exes.names in
let dep_graph = ocamldep_rules ~dir ~item ~modules ~alias_module:None in let dep_graph = ocamldep_rules ~dir ~item ~modules ~alias_module:None in
let real_requires = let requires, real_requires =
requires ~dir ~dep_kind ~item requires ~dir ~dep_kind ~item
~libraries:exes.buildable.libraries ~libraries:exes.buildable.libraries
~preprocess:exes.buildable.preprocess ~preprocess:exes.buildable.preprocess
~virtual_deps:[] ~virtual_deps:[]
in in
let requires =
match Path.extract_build_context dir with
| Some ("default", remaindir) ->
Build.path (Path.relative remaindir ".merlin") >>> real_requires
| _ ->
real_requires
in
List.iter (Lib_db.select_rules ~dir exes.buildable.libraries) ~f:add_rule; List.iter (Lib_db.select_rules ~dir exes.buildable.libraries) ~f:add_rule;
build_modules ~flags ~dir ~dep_graph ~modules ~requires ~alias_module:None; build_modules ~flags ~dir ~dep_graph ~modules ~requires ~alias_module:None;

View File

@ -22,13 +22,14 @@ let setup ?filter_out_optional_stanzas_with_missing_deps ?workspace () =
if Sys.file_exists "jbuild-workspace" then if Sys.file_exists "jbuild-workspace" then
Workspace.load "jbuild-workspace" Workspace.load "jbuild-workspace"
else else
[Default] { merlin_context = Some "default"; contexts = [Default] }
in in
Future.all Future.all
(List.map workspace ~f:(function (List.map workspace.contexts ~f:(function
| Workspace.Context.Default -> Lazy.force Context.default | Workspace.Context.Default ->
| Opam { name; switch; root } -> Context.default ~merlin:(workspace.merlin_context = Some "default") ()
Context.create_for_opam ~name ~switch ?root ())) | Opam { name; switch; root; merlin } ->
Context.create_for_opam ~name ~switch ?root ~merlin ()))
>>= fun contexts -> >>= fun contexts ->
Gen_rules.gen conf ~contexts Gen_rules.gen conf ~contexts
?filter_out_optional_stanzas_with_missing_deps ?filter_out_optional_stanzas_with_missing_deps
@ -124,7 +125,8 @@ let bootstrap () =
] ]
anon "Usage: boot.exe [-j JOBS] [--dev]\nOptions are:"; anon "Usage: boot.exe [-j JOBS] [--dev]\nOptions are:";
Future.Scheduler.go ~log:(create_log ()) Future.Scheduler.go ~log:(create_log ())
(setup ~workspace:[Default] () >>= fun { build_system = bs; _ } -> (setup ~workspace:{ merlin_context = Some "default"; contexts = [Default] } ()
>>= fun { build_system = bs; _ } ->
Build_system.do_build_exn bs [Path.(relative root) (pkg ^ ".install")]) Build_system.do_build_exn bs [Path.(relative root) (pkg ^ ".install")])
in in
try try

View File

@ -7,6 +7,7 @@ module Context = struct
{ name : string { name : string
; switch : string ; switch : string
; root : string option ; root : string option
; merlin : bool
} }
let t = let t =
@ -14,9 +15,11 @@ module Context = struct
(field "switch" string >>= fun switch -> (field "switch" string >>= fun switch ->
field "name" string ~default:switch >>= fun name -> field "name" string ~default:switch >>= fun name ->
field_o "root" string >>= fun root -> field_o "root" string >>= fun root ->
field_b "merlin" >>= fun merlin ->
return { switch return { switch
; name ; name
; root ; root
; merlin
}) })
end end
@ -31,25 +34,47 @@ module Context = struct
| Opam o -> o.name | Opam o -> o.name
end end
type t = Context.t list type t =
{ merlin_context : string option
; contexts : Context.t list
}
let t sexps = let t sexps =
List.fold_left sexps ~init:[] ~f:(fun acc sexp -> let merlin_ctx, contexts =
let ctx = List.fold_left sexps ~init:(None, []) ~f:(fun (merlin_ctx, ctxs) sexp ->
sum let ctx =
[ cstr "context" [Context.t] (fun x -> x) ] sum
sexp [ cstr "context" [Context.t] (fun x -> x) ]
in sexp
let name = Context.name ctx in in
if name = "" || let name = Context.name ctx in
String.is_prefix name ~prefix:"." || if name = "" ||
name = "log" || String.is_prefix name ~prefix:"." ||
String.contains name '/' || name = "log" ||
String.contains name '\\' then String.contains name '/' ||
of_sexp_errorf sexp "%S is not allowed as a build context name" name; String.contains name '\\' then
if List.exists acc ~f:(fun c -> Context.name c = name) then of_sexp_errorf sexp "%S is not allowed as a build context name" name;
of_sexp_errorf sexp "second definition of build context %S" name; if List.exists ctxs ~f:(fun c -> Context.name c = name) then
ctx :: acc) of_sexp_errorf sexp "second definition of build context %S" name;
|> List.rev match ctx, merlin_ctx with
| Opam { merlin = true; _ }, Some _ ->
of_sexp_errorf sexp "you can only have one context for merlin"
| Opam { merlin = true; _ }, None ->
(Some name, ctx :: ctxs)
| _ ->
(merlin_ctx, ctx :: ctxs))
in
let merlin_ctx =
match merlin_ctx with
| Some _ -> merlin_ctx
| None ->
if List.mem Context.Default ~set:contexts then
Some "default"
else
None
in
{ merlin_context = merlin_ctx
; contexts = List.rev contexts
}
let load fn = t (Sexp_load.many fn) let load fn = t (Sexp_load.many fn)

View File

@ -8,12 +8,16 @@ module Context : sig
{ name : string { name : string
; switch : string ; switch : string
; root : string option ; root : string option
; merlin : bool
} }
end end
type t = Default | Opam of Opam.t type t = Default | Opam of Opam.t
end end
type t = Context.t list type t =
{ merlin_context : string option
; contexts : Context.t list
}
val load : string -> t val load : string -> t