2016-12-02 13:54:32 +00:00
|
|
|
open Import
|
|
|
|
open Future
|
|
|
|
|
|
|
|
module Kind = struct
|
2017-02-26 21:28:30 +00:00
|
|
|
module Opam = struct
|
|
|
|
type t =
|
|
|
|
{ root : string
|
|
|
|
; switch : string
|
|
|
|
}
|
|
|
|
end
|
|
|
|
type t = Default | Opam of Opam.t
|
2016-12-02 13:54:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
type t =
|
2017-02-25 00:28:10 +00:00
|
|
|
{ name : string
|
|
|
|
; kind : Kind.t
|
2017-02-26 20:53:32 +00:00
|
|
|
; merlin : bool
|
2016-12-02 13:54:32 +00:00
|
|
|
; for_host : t option
|
|
|
|
; build_dir : Path.t
|
|
|
|
; path : Path.t list
|
2017-02-28 06:31:02 +00:00
|
|
|
; toplevel_path : Path.t option
|
2016-12-02 13:54:32 +00:00
|
|
|
; ocaml_bin : Path.t
|
|
|
|
; ocaml : Path.t
|
|
|
|
; ocamlc : Path.t
|
|
|
|
; ocamlopt : Path.t option
|
|
|
|
; ocamldep : Path.t
|
|
|
|
; ocamllex : Path.t
|
|
|
|
; ocamlyacc : Path.t
|
|
|
|
; ocamlmklib : Path.t
|
|
|
|
; env : string array
|
2017-02-28 06:01:27 +00:00
|
|
|
; findlib : Findlib.t
|
2016-12-02 13:54:32 +00:00
|
|
|
; arch_sixtyfour : bool
|
2017-02-28 06:01:27 +00:00
|
|
|
; opam_var_cache : (string, string) Hashtbl.t
|
2017-03-07 11:36:59 +00:00
|
|
|
; natdynlink_supported : bool
|
2017-02-26 19:49:54 +00:00
|
|
|
; ocamlc_config : (string * string) list
|
2016-12-02 13:54:32 +00:00
|
|
|
; version : string
|
|
|
|
; stdlib_dir : Path.t
|
|
|
|
; ccomp_type : string
|
|
|
|
; bytecomp_c_compiler : string
|
|
|
|
; bytecomp_c_libraries : string
|
|
|
|
; native_c_compiler : string
|
|
|
|
; native_c_libraries : string
|
|
|
|
; native_pack_linker : string
|
|
|
|
; ranlib : string
|
|
|
|
; cc_profile : string
|
|
|
|
; architecture : string
|
|
|
|
; system : string
|
|
|
|
; ext_obj : string
|
|
|
|
; ext_asm : string
|
|
|
|
; ext_lib : string
|
|
|
|
; ext_dll : string
|
|
|
|
; os_type : string
|
|
|
|
; default_executable_name : string
|
|
|
|
; host : string
|
|
|
|
; target : string
|
|
|
|
; flambda : bool
|
|
|
|
; exec_magic_number : string
|
|
|
|
; cmi_magic_number : string
|
|
|
|
; cmo_magic_number : string
|
|
|
|
; cma_magic_number : string
|
|
|
|
; cmx_magic_number : string
|
|
|
|
; cmxa_magic_number : string
|
|
|
|
; ast_impl_magic_number : string
|
|
|
|
; ast_intf_magic_number : string
|
|
|
|
; cmxs_magic_number : string
|
|
|
|
; cmt_magic_number : string
|
2017-03-01 11:04:32 +00:00
|
|
|
; which_cache : (string, Path.t option) Hashtbl.t
|
2016-12-02 13:54:32 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 14:15:52 +00:00
|
|
|
let compare a b = compare a.name b.name
|
|
|
|
|
2016-12-02 13:54:32 +00:00
|
|
|
let get_arch_sixtyfour stdlib_dir =
|
|
|
|
let config_h = Path.relative stdlib_dir "caml/config.h" in
|
|
|
|
List.exists (lines_of_file (Path.to_string config_h)) ~f:(fun line ->
|
|
|
|
match String.split_words line with
|
|
|
|
| ["#define"; "ARCH_SIXTYFOUR"] -> true
|
|
|
|
| _ -> false)
|
|
|
|
|
2017-02-24 15:41:52 +00:00
|
|
|
let opam_config_var ~env ~cache var =
|
|
|
|
match Hashtbl.find cache var with
|
|
|
|
| Some _ as x -> return x
|
|
|
|
| None ->
|
|
|
|
match Bin.opam with
|
|
|
|
| None -> return None
|
|
|
|
| Some fn ->
|
2017-02-27 11:37:28 +00:00
|
|
|
Future.run_capture Strict (Path.to_string fn) ~env ["config"; "var"; var]
|
2017-02-24 15:41:52 +00:00
|
|
|
>>| fun s ->
|
|
|
|
let s = String.trim s in
|
|
|
|
Hashtbl.add cache ~key:var ~data:s;
|
|
|
|
Some s
|
|
|
|
|
2017-02-28 06:31:02 +00:00
|
|
|
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
|
|
|
|
|
2017-03-01 11:04:32 +00:00
|
|
|
let which ~cache ~path x =
|
|
|
|
Hashtbl.find_or_add cache x ~f:(Bin.which ~path)
|
|
|
|
|
2017-02-26 20:53:32 +00:00
|
|
|
let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
2017-02-24 15:41:52 +00:00
|
|
|
let opam_var_cache = Hashtbl.create 128 in
|
|
|
|
(match kind with
|
|
|
|
| Opam { root; _ } ->
|
|
|
|
Hashtbl.add opam_var_cache ~key:"root" ~data:root
|
|
|
|
| Default -> ());
|
2016-12-02 13:54:32 +00:00
|
|
|
let prog_not_found_in_path prog =
|
|
|
|
die "Program %s not found in PATH (context: %s)" prog name
|
|
|
|
in
|
2017-03-01 11:04:32 +00:00
|
|
|
let which_cache = Hashtbl.create 128 in
|
|
|
|
let which x = which ~cache:which_cache ~path x in
|
2016-12-02 13:54:32 +00:00
|
|
|
let ocamlc =
|
|
|
|
match which "ocamlc" with
|
|
|
|
| None -> prog_not_found_in_path "ocamlc"
|
|
|
|
| Some x -> x
|
|
|
|
in
|
|
|
|
let dir = Path.parent ocamlc in
|
|
|
|
let prog_not_found prog =
|
|
|
|
die "ocamlc found in %s, but %s/%s doesn't exist (context: %s)"
|
|
|
|
(Path.to_string dir) (Path.to_string dir) prog name
|
|
|
|
in
|
|
|
|
let best_prog prog = Bin.best_prog dir prog in
|
|
|
|
let get_prog prog =
|
|
|
|
match best_prog prog with
|
|
|
|
| None -> prog_not_found prog
|
|
|
|
| Some fn -> fn
|
|
|
|
in
|
|
|
|
let build_dir =
|
2017-03-01 16:09:02 +00:00
|
|
|
Path.of_string (sprintf "_build/%s" name)
|
2016-12-02 13:54:32 +00:00
|
|
|
in
|
|
|
|
let ocamlc_config_cmd = sprintf "%s -config" (Path.to_string ocamlc) in
|
|
|
|
both
|
2016-12-15 11:20:46 +00:00
|
|
|
(both
|
2017-02-24 15:41:52 +00:00
|
|
|
(opam_config_var ~env ~cache:opam_var_cache "lib"
|
|
|
|
>>| function
|
|
|
|
| None -> []
|
|
|
|
| Some s -> [Path.absolute s])
|
2016-12-15 11:20:46 +00:00
|
|
|
(match which "ocamlfind" with
|
|
|
|
| None ->
|
|
|
|
return []
|
|
|
|
| Some fn ->
|
2017-02-27 11:37:28 +00:00
|
|
|
Future.run_capture_lines ~env (Accept [127])
|
|
|
|
(Path.to_string fn) ["printconf"; "path"]
|
|
|
|
>>| function
|
|
|
|
| Ok lines -> List.map lines ~f:Path.absolute
|
|
|
|
| Error _ -> [])
|
2016-12-15 11:20:46 +00:00
|
|
|
>>| fun (a, b) ->
|
|
|
|
match a @ b with
|
|
|
|
| [] -> [Path.relative (Path.parent dir) "lib"]
|
|
|
|
| l ->
|
|
|
|
List.fold_left l ~init:l ~f:(fun acc x ->
|
|
|
|
if List.mem x ~set:acc then
|
|
|
|
acc
|
|
|
|
else
|
|
|
|
x :: acc)
|
|
|
|
|> List.rev)
|
2017-02-27 11:37:28 +00:00
|
|
|
(Future.run_capture_lines ~env Strict (Path.to_string ocamlc) ["-config"])
|
2016-12-02 13:54:32 +00:00
|
|
|
>>= fun (findlib_path, ocamlc_config) ->
|
|
|
|
let ocamlc_config =
|
|
|
|
List.map ocamlc_config ~f:(fun line ->
|
|
|
|
match String.index line ':' with
|
|
|
|
| Some i ->
|
|
|
|
(String.sub line ~pos:0 ~len:i,
|
|
|
|
String.sub line ~pos:(i + 2) ~len:(String.length line - i - 2))
|
|
|
|
| None ->
|
|
|
|
die "unrecognized line in the output of `%s`: %s" ocamlc_config_cmd
|
|
|
|
line)
|
|
|
|
|> String_map.of_alist
|
|
|
|
|> function
|
|
|
|
| Ok x -> x
|
|
|
|
| Error (key, _, _) ->
|
|
|
|
die "variable %S present twice in the output of `%s`" key ocamlc_config_cmd
|
|
|
|
in
|
2017-02-26 21:28:30 +00:00
|
|
|
let get ?default var =
|
2016-12-02 13:54:32 +00:00
|
|
|
match String_map.find var ocamlc_config with
|
|
|
|
| Some s -> s
|
2017-02-26 21:28:30 +00:00
|
|
|
| None ->
|
|
|
|
match default with
|
|
|
|
| Some x -> x
|
|
|
|
| None ->
|
|
|
|
die "variable %S not found in the output of `%s`" var ocamlc_config_cmd
|
2016-12-02 13:54:32 +00:00
|
|
|
in
|
2017-02-26 21:28:30 +00:00
|
|
|
let get_bool ?default var =
|
|
|
|
match get ?default:(Option.map default ~f:string_of_bool) var with
|
2016-12-02 13:54:32 +00:00
|
|
|
| "true" -> true
|
|
|
|
| "false" -> false
|
|
|
|
| _ -> die "variable %S is neither 'true' neither 'false' in the output of `%s`"
|
|
|
|
var ocamlc_config_cmd
|
|
|
|
in
|
|
|
|
let get_path var = Path.absolute (get var) in
|
|
|
|
let stdlib_dir = get_path "standard_library" in
|
2017-03-07 11:36:59 +00:00
|
|
|
let natdynlink_supported = Path.exists (Path.relative stdlib_dir "dynlink.cmxa") in
|
2017-02-26 21:49:41 +00:00
|
|
|
return
|
2017-02-25 00:28:10 +00:00
|
|
|
{ name
|
|
|
|
; kind
|
2017-02-26 20:53:32 +00:00
|
|
|
; merlin
|
2016-12-02 13:54:32 +00:00
|
|
|
; for_host = None
|
|
|
|
; build_dir
|
|
|
|
; path
|
2017-02-28 06:31:02 +00:00
|
|
|
; toplevel_path = Option.map (get_env env "OCAML_TOPLEVEL_PATH") ~f:Path.of_string
|
2016-12-02 13:54:32 +00:00
|
|
|
|
|
|
|
; ocaml_bin = dir
|
|
|
|
; ocaml = Path.relative dir "ocaml"
|
|
|
|
; ocamlc
|
|
|
|
; ocamlopt = best_prog "ocamlopt"
|
|
|
|
; ocamllex = get_prog "ocamllex"
|
|
|
|
; ocamlyacc = get_prog "ocamlyacc"
|
|
|
|
; ocamldep = get_prog "ocamldep"
|
|
|
|
; ocamlmklib = get_prog "ocamlmklib"
|
|
|
|
|
|
|
|
; env
|
2017-02-28 06:01:27 +00:00
|
|
|
; findlib = Findlib.create ~stdlib_dir ~path:findlib_path
|
2016-12-02 13:54:32 +00:00
|
|
|
; arch_sixtyfour = get_arch_sixtyfour stdlib_dir
|
|
|
|
|
2017-02-24 15:41:52 +00:00
|
|
|
; opam_var_cache
|
|
|
|
|
2017-03-07 11:36:59 +00:00
|
|
|
; natdynlink_supported
|
|
|
|
|
2016-12-02 13:54:32 +00:00
|
|
|
; stdlib_dir
|
2017-02-26 19:49:54 +00:00
|
|
|
; ocamlc_config = String_map.bindings ocamlc_config
|
2016-12-02 13:54:32 +00:00
|
|
|
; version = get "version"
|
|
|
|
; ccomp_type = get "ccomp_type"
|
|
|
|
; bytecomp_c_compiler = get "bytecomp_c_compiler"
|
|
|
|
; bytecomp_c_libraries = get "bytecomp_c_libraries"
|
|
|
|
; native_c_compiler = get "native_c_compiler"
|
|
|
|
; native_c_libraries = get "native_c_libraries"
|
|
|
|
; native_pack_linker = get "native_pack_linker"
|
|
|
|
; ranlib = get "ranlib"
|
|
|
|
; cc_profile = get "cc_profile"
|
|
|
|
; architecture = get "architecture"
|
|
|
|
; system = get "system"
|
|
|
|
; ext_obj = get "ext_obj"
|
|
|
|
; ext_asm = get "ext_asm"
|
|
|
|
; ext_lib = get "ext_lib"
|
|
|
|
; ext_dll = get "ext_dll"
|
|
|
|
; os_type = get "os_type"
|
|
|
|
; default_executable_name = get "default_executable_name"
|
|
|
|
; host = get "host"
|
|
|
|
; target = get "target"
|
2017-02-26 21:28:30 +00:00
|
|
|
; flambda = get_bool "flambda" ~default:false
|
2016-12-02 13:54:32 +00:00
|
|
|
; exec_magic_number = get "exec_magic_number"
|
|
|
|
; cmi_magic_number = get "cmi_magic_number"
|
|
|
|
; cmo_magic_number = get "cmo_magic_number"
|
|
|
|
; cma_magic_number = get "cma_magic_number"
|
|
|
|
; cmx_magic_number = get "cmx_magic_number"
|
|
|
|
; cmxa_magic_number = get "cmxa_magic_number"
|
|
|
|
; ast_impl_magic_number = get "ast_impl_magic_number"
|
|
|
|
; ast_intf_magic_number = get "ast_intf_magic_number"
|
|
|
|
; cmxs_magic_number = get "cmxs_magic_number"
|
|
|
|
; cmt_magic_number = get "cmt_magic_number"
|
2017-03-01 11:04:32 +00:00
|
|
|
|
|
|
|
; which_cache
|
2016-12-02 13:54:32 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 15:41:52 +00:00
|
|
|
let opam_config_var t var = opam_config_var ~env:t.env ~cache:t.opam_var_cache var
|
|
|
|
|
2017-02-23 12:12:02 +00:00
|
|
|
let initial_env = lazy (
|
|
|
|
Lazy.force Ansi_color.setup_env_for_ocaml_colors;
|
|
|
|
Unix.environment ())
|
2016-12-02 13:54:32 +00:00
|
|
|
|
2017-02-26 20:53:32 +00:00
|
|
|
let default ?(merlin=true) () =
|
2016-12-02 13:54:32 +00:00
|
|
|
let env = Lazy.force initial_env in
|
|
|
|
let rec find_path i =
|
|
|
|
if i = Array.length env then
|
|
|
|
[]
|
|
|
|
else
|
|
|
|
match String.lsplit2 env.(i) ~on:'=' with
|
|
|
|
| Some ("PATH", s) ->
|
|
|
|
Bin.parse_path s
|
|
|
|
| _ -> find_path (i + 1)
|
|
|
|
in
|
|
|
|
let path = find_path 0 in
|
2017-02-26 20:53:32 +00:00
|
|
|
create ~kind:Default ~path ~env ~name:"default" ~merlin
|
2016-12-02 13:54:32 +00:00
|
|
|
|
|
|
|
let extend_env ~vars ~env =
|
|
|
|
let imported =
|
|
|
|
Array.to_list env
|
|
|
|
|> List.filter ~f:(fun s ->
|
|
|
|
match String.index s '=' with
|
|
|
|
| None -> true
|
|
|
|
| Some i ->
|
|
|
|
let key = String.sub s ~pos:0 ~len:i in
|
|
|
|
not (String_map.mem key vars))
|
|
|
|
in
|
|
|
|
List.rev_append
|
|
|
|
(List.map (String_map.bindings vars) ~f:(fun (k, v) -> sprintf "%s=%s" k v))
|
|
|
|
imported
|
|
|
|
|> Array.of_list
|
|
|
|
|
2017-02-26 20:53:32 +00:00
|
|
|
let create_for_opam ?root ~switch ~name ?(merlin=false) () =
|
2016-12-02 13:54:32 +00:00
|
|
|
match Bin.opam with
|
|
|
|
| None -> die "Program opam not found in PATH"
|
|
|
|
| Some fn ->
|
|
|
|
(match root with
|
|
|
|
| Some root -> return root
|
|
|
|
| None ->
|
2017-02-27 11:37:28 +00:00
|
|
|
Future.run_capture_line Strict (Path.to_string fn) ["config"; "var"; "root"])
|
2016-12-02 13:54:32 +00:00
|
|
|
>>= fun root ->
|
2017-02-27 11:37:28 +00:00
|
|
|
Future.run_capture Strict (Path.to_string fn)
|
2016-12-02 13:54:32 +00:00
|
|
|
["config"; "env"; "--root"; root; "--switch"; switch; "--sexp"]
|
|
|
|
>>= fun s ->
|
|
|
|
let vars =
|
|
|
|
Sexp_lexer.single (Lexing.from_string s)
|
|
|
|
|> Sexp.Of_sexp.(string_map string)
|
|
|
|
in
|
|
|
|
let path =
|
|
|
|
match String_map.find "PATH" vars with
|
|
|
|
| None -> Bin.path
|
|
|
|
| Some s -> Bin.parse_path s
|
|
|
|
in
|
|
|
|
let env = Lazy.force initial_env in
|
|
|
|
create ~kind:(Opam { root; switch }) ~path ~env:(extend_env ~vars ~env)
|
2017-02-26 20:53:32 +00:00
|
|
|
~name ~merlin
|
2016-12-02 13:54:32 +00:00
|
|
|
|
2017-03-01 11:04:32 +00:00
|
|
|
let which t s = which ~cache:t.which_cache ~path:t.path s
|
2017-02-24 15:41:52 +00:00
|
|
|
|
|
|
|
let install_prefix t =
|
|
|
|
opam_config_var t "prefix" >>| function
|
|
|
|
| Some x -> Path.absolute x
|
|
|
|
| None -> Path.parent t.ocaml_bin
|
2017-03-01 12:09:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
(* CR-someday jdimino: maybe we should just do this for [t.env] directly? *)
|
|
|
|
let env_for_exec t =
|
|
|
|
let sep = if Sys.win32 then ';' else ':' in
|
|
|
|
let cwd = Sys.getcwd () in
|
|
|
|
let extend_var var v =
|
|
|
|
let v = Filename.concat cwd (Path.to_string v) in
|
|
|
|
match get_env t.env var with
|
|
|
|
| None -> (var, v)
|
|
|
|
| Some prev -> (var, sprintf "%s%c%s" v sep prev)
|
|
|
|
in
|
|
|
|
let vars =
|
2017-03-02 17:40:16 +00:00
|
|
|
[ extend_var "CAML_LD_LIBRARY_PATH" (Path.relative
|
|
|
|
(Config.local_install_dir ~context:t.name)
|
|
|
|
"lib/stublibs")
|
2017-03-01 12:09:57 +00:00
|
|
|
; extend_var "OCAMLPATH" (Path.relative
|
|
|
|
(Config.local_install_dir ~context:t.name)
|
|
|
|
"lib")
|
|
|
|
; extend_var "PATH" (Config.local_install_bin_dir ~context:t.name)
|
2017-03-01 12:56:31 +00:00
|
|
|
; extend_var "MANPATH" (Config.local_install_man_dir ~context:t.name)
|
2017-03-01 12:09:57 +00:00
|
|
|
]
|
|
|
|
in
|
|
|
|
extend_env ~env:t.env ~vars:(String_map.of_alist_exn vars)
|