Add target support

* Create targets from findlib toolchains by reading findlib configs
* Define targets inside workspace files
* Set cross compilation targets with -x argument
This commit is contained in:
Jeremie Dimino 2017-12-21 19:54:00 +08:00 committed by Rudi Grinberg
parent afb602d7ef
commit 6e64156913
8 changed files with 401 additions and 246 deletions

View File

@ -21,6 +21,7 @@ type common =
; target_prefix : string
; only_packages : String_set.t option
; capture_outputs : bool
; x : string option
; (* Original arguments for the external-lib-deps hint *)
orig_args : string list
}
@ -74,7 +75,9 @@ module Main = struct
?unlink_aliases
?workspace_file:common.workspace_file
?only_packages:common.only_packages
?filter_out_optional_stanzas_with_missing_deps ()
?filter_out_optional_stanzas_with_missing_deps
?x:common.x
()
end
type target =
@ -154,6 +157,7 @@ let common =
no_buffer
workspace_file
(root, only_packages, orig)
x
=
let root, to_cwd =
match root with
@ -181,6 +185,7 @@ let common =
; only_packages =
Option.map only_packages
~f:(fun s -> String_set.of_list (String.split s ~on:','))
; x
}
in
let docs = copts_sect in
@ -304,6 +309,12 @@ let common =
$ only_packages
$ frop))
in
let x =
Arg.(value
& opt (some string) None
& info ["x"] ~docs
~doc:{|Cross-compile using this toolchain.|})
in
Term.(const make
$ concurrency
$ ddep_path
@ -314,6 +325,7 @@ let common =
$ no_buffer
$ workspace_file
$ root_and_only_packages
$ x
)
let installed_libraries =
@ -321,7 +333,8 @@ let installed_libraries =
let go common na =
set_common common ~targets:[];
Future.Scheduler.go ~log:(Log.create ())
(Context.default () >>= fun ctx ->
(Context.create (Default [Native]) >>= fun ctxs ->
let ctx = List.hd ctxs in
let findlib = ctx.findlib in
if na then begin
let pkgs = Findlib.all_unavailable_packages findlib in

View File

@ -1,7 +1,7 @@
;; This file is used by `make all-supported-ocaml-versions`
(context ((switch 4.02.3)))
(context ((switch 4.03.0)))
(context ((switch 4.04.2)))
(context ((switch 4.05.0)))
(context ((switch 4.06.0)))
(context ((switch 4.07.0+trunk)))
(context (opam (switch 4.02.3)))
(context (opam (switch 4.03.0)))
(context (opam (switch 4.04.2)))
(context (opam (switch 4.05.0)))
(context (opam (switch 4.06.0)))
(context (opam (switch 4.07.0+trunk)))

View File

@ -34,6 +34,7 @@ type t =
; kind : Kind.t
; merlin : bool
; for_host : t option
; implicit : bool
; build_dir : Path.t
; path : Path.t list
; toplevel_path : Path.t option
@ -46,6 +47,7 @@ type t =
; env : string array
; env_extra : string Env_var_map.t
; findlib : Findlib.t
; findlib_toolchain : string option
; arch_sixtyfour : bool
; opam_var_cache : (string, string) Hashtbl.t
; natdynlink_supported : bool
@ -175,7 +177,8 @@ let extend_env ~vars ~env =
imported
|> Array.of_list
let create ~(kind : Kind.t) ~path ~base_env ~env_extra ~name ~merlin ~use_findlib =
let create ~(kind : Kind.t) ~path ~base_env ~env_extra ~name ~merlin
~use_findlib ~targets () =
let env = extend_env ~env:base_env ~vars:env_extra in
let opam_var_cache = Hashtbl.create 128 in
(match kind with
@ -187,37 +190,83 @@ let create ~(kind : Kind.t) ~path ~base_env ~env_extra ~name ~merlin ~use_findli
in
let which_cache = Hashtbl.create 128 in
let which x = which ~cache:which_cache ~path x in
let findlib_config_path = lazy (
match which "ocamlfind" with
| None -> prog_not_found_in_path "ocamlfind"
| Some fn ->
(* When OCAMLFIND_CONF is set, "ocamlfind printconf" does print the contents of the
variable, but "ocamlfind printconf conf" still prints the configuration file set
at the configuration time of ocamlfind, sigh... *)
match Sys.getenv "OCAMLFIND_CONF" with
| s -> Future.return (Path.absolute s)
| exception Not_found ->
Future.run_capture_line ~env Strict
(Path.to_string fn) ["printconf"; "conf"]
>>| Path.absolute)
in
let create_one ~name ~implicit ?findlib_toolchain ?host ~merlin () =
(match findlib_toolchain with
| None -> Future.return None
| Some toolchain ->
Lazy.force findlib_config_path >>| fun path ->
Some (Findlib.Config.load path ~toolchain ~context:name))
>>= fun findlib_config ->
let get_tool_using_findlib_config prog =
match findlib_config with
| None -> None
| Some conf ->
match Findlib.Config.get conf prog with
| "" -> None
| s ->
match Filename.analyze_program_name s with
| In_path | Relative_to_current_dir -> which s
| Absolute -> Some (Path.absolute s)
in
let ocamlc =
match which "ocamlc" with
| None -> prog_not_found_in_path "ocamlc"
match get_tool_using_findlib_config "ocamlc" with
| Some x -> x
| None ->
match which "ocamlc" with
| Some x -> x
| None -> prog_not_found_in_path "ocamlc"
in
let dir = Path.parent ocamlc in
let prog_not_found prog =
let ocaml_tool_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
let get_ocaml_tool prog =
match get_tool_using_findlib_config prog with
| None -> Bin.best_prog dir prog
| Some _ as x -> x
in
let get_ocaml_tool_exn prog =
match get_ocaml_tool prog with
| None -> ocaml_tool_not_found prog
| Some fn -> fn
in
let build_dir =
Path.of_string (sprintf "_build/%s" name)
in
let build_dir = Path.of_string (sprintf "_build/%s" name) in
let ocamlc_config_cmd = sprintf "%s -config" (Path.to_string ocamlc) in
let findlib_path =
if use_findlib then
(* If ocamlfind is present, it has precedence over everything else. *)
match which "ocamlfind" with
| Some fn ->
(Future.run_capture_lines ~env Strict
(Path.to_string fn) ["printconf"; "path"]
>>| List.map ~f:Path.absolute)
let args =
let args = ["printconf"; "path"] in
match findlib_toolchain with
| None -> args
| Some s -> "-toolchain" :: s :: args
in
Future.run_capture_lines ~env Strict (Path.to_string fn) args
>>| List.map ~f:Path.absolute
| None ->
(* If there no ocamlfind in the PATH, check if we have opam and assume a stan opam
setup *)
(* If there no ocamlfind in the PATH, check if we have opam
and assume a standard opam setup *)
opam_config_var ~env ~cache:opam_var_cache "lib"
>>| function
| Some s -> [Path.absolute s]
@ -232,6 +281,7 @@ let create ~(kind : Kind.t) ~path ~base_env ~env_extra ~name ~merlin ~use_findli
findlib_path
(Future.run_capture_lines ~env Strict (Path.to_string ocamlc) ["-config"])
>>= fun (findlib_path, ocamlc_config) ->
let ocamlc_config =
List.map ocamlc_config ~f:(fun line ->
match String.index line ':' with
@ -317,23 +367,25 @@ let create ~(kind : Kind.t) ~path ~base_env ~env_extra ~name ~merlin ~use_findli
in
return
{ name
; implicit
; kind
; merlin
; for_host = None
; for_host = host
; build_dir
; path
; toplevel_path = Option.map (get_env env "OCAML_TOPLEVEL_PATH") ~f:Path.absolute
; ocaml_bin = dir
; ocaml = Path.relative dir ("ocaml" ^ Bin.exe)
; ocaml = (match which "ocaml" with Some p -> p | None -> prog_not_found_in_path "ocaml")
; ocamlc
; ocamlopt = best_prog "ocamlopt"
; ocamldep = get_prog "ocamldep"
; ocamlmklib = get_prog "ocamlmklib"
; ocamlopt = get_ocaml_tool "ocamlopt"
; ocamldep = get_ocaml_tool_exn "ocamldep"
; ocamlmklib = get_ocaml_tool_exn "ocamlmklib"
; env
; env_extra
; findlib = Findlib.create ~stdlib_dir ~path:findlib_path
; findlib_toolchain
; arch_sixtyfour
; opam_var_cache
@ -376,6 +428,20 @@ let create ~(kind : Kind.t) ~path ~base_env ~env_extra ~name ~merlin ~use_findli
; which_cache
}
in
let implicit = not (List.mem ~set:targets Workspace.Context.Target.Native) in
create_one () ~implicit ~name ~merlin >>= fun native ->
Future.all (
List.filter_map targets ~f:(function
| Native -> None
| Named findlib_toolchain ->
let name = sprintf "%s.%s" name findlib_toolchain in
Some (create_one () ~implicit:false ~name ~findlib_toolchain ~host:native
~merlin:false)
)
) >>| fun others ->
native :: others
let opam_config_var t var = opam_config_var ~env:t.env ~cache:t.opam_var_cache var
@ -383,7 +449,7 @@ let initial_env = lazy (
Lazy.force Ansi_color.setup_env_for_colors;
Unix.environment ())
let default ?(merlin=true) ?(use_findlib=true) () =
let default ?(merlin=true) ?(use_findlib=true) ~targets () =
let env = Lazy.force initial_env in
let path =
match get_env env "PATH" with
@ -391,9 +457,9 @@ let default ?(merlin=true) ?(use_findlib=true) () =
| None -> []
in
create ~kind:Default ~path ~base_env:env ~env_extra:Env_var_map.empty
~name:"default" ~merlin ~use_findlib
~name:"default" ~merlin ~use_findlib ~targets ()
let create_for_opam ?root ~switch ~name ?(merlin=false) () =
let create_for_opam ?root ~targets ~switch ~name ?(merlin=false) () =
match Bin.opam with
| None -> Utils.program_not_found "opam"
| Some fn ->
@ -430,8 +496,14 @@ let create_for_opam ?root ~switch ~name ?(merlin=false) () =
| Some s -> Bin.parse_path s
in
let env = Lazy.force initial_env in
create ~kind:(Opam { root; switch }) ~path ~base_env:env ~env_extra:vars
~name ~merlin ~use_findlib:true
create ~kind:(Opam { root; switch }) ~targets
~path ~base_env:env ~env_extra:vars ~name ~merlin ~use_findlib:true ()
let create ?use_findlib ?merlin def =
match (def : Workspace.Context.t) with
| Default targets -> default ~targets ?merlin ?use_findlib ()
| Opam { name; switch; root; targets; _ } ->
create_for_opam ?root ~switch ~name ?merlin ~targets ()
let which t s = which ~cache:t.which_cache ~path:t.path s

View File

@ -48,6 +48,10 @@ type t =
building tools used for the compilation that run on the host. *)
for_host : t option
; (** [false] if a user explicitly listed this context in the workspace.
Controls whether we add artifacts from this context @install *)
implicit : bool
; (** Directory where artifact are stored, for instance "_build/default" *)
build_dir : Path.t
@ -72,6 +76,7 @@ type t =
env_extra : string Env_var_map.t
; findlib : Findlib.t
; findlib_toolchain : string option
; (** Misc *)
arch_sixtyfour : bool
@ -124,18 +129,14 @@ val sexp_of_t : t -> Sexp.t
(** Compare the context names *)
val compare : t -> t -> int
val create_for_opam
: ?root:string
-> switch:string
-> name:string
-> ?merlin:bool
-> unit
-> t Future.t
(** If [use_findlib] is [false], don't try to guess the library search path with opam or
ocamlfind. This is only for building jbuilder itself, so that its build is completely
independent of the user setup. *)
val default : ?merlin:bool -> ?use_findlib:bool -> unit -> t Future.t
val create
: ?use_findlib:bool
-> ?merlin:bool
-> Workspace.Context.t
-> t list Future.t
val which : t -> string -> Path.t option

View File

@ -12,7 +12,8 @@ type setup =
let package_install_file { packages; _ } pkg =
match String_map.find pkg packages with
| None -> Error ()
| Some p -> Ok (Path.relative p.path (p.name ^ ".install"))
| Some p ->
Ok (Path.relative p.path (Utils.install_file ~package:p.name ~findlib_toolchain:None))
let setup ?(log=Log.no_log) ?unlink_aliases
?filter_out_optional_stanzas_with_missing_deps
@ -20,6 +21,7 @@ let setup ?(log=Log.no_log) ?unlink_aliases
?(use_findlib=true)
?only_packages
?extra_ignored_subtrees
?x
() =
let conf = Jbuild_load.load ?extra_ignored_subtrees () in
Option.iter only_packages ~f:(fun set ->
@ -33,26 +35,34 @@ let setup ?(log=Log.no_log) ?unlink_aliases
| Some w -> w
| None ->
if Sys.file_exists workspace_file then
Workspace.load workspace_file
Workspace.load ?x workspace_file
else
{ merlin_context = Some "default"; contexts = [Default] }
{ merlin_context = Some "default"
; contexts = [Default [
match x with
| None -> Native
| Some x -> Named x
]]
}
in
Future.all
(List.map workspace.contexts ~f:(function
| Workspace.Context.Default ->
Context.default ~merlin:(workspace.merlin_context = Some "default")
~use_findlib ()
| Opam { name; switch; root; merlin } ->
Context.create_for_opam ~name ~switch ?root ~merlin ()))
Future.all (
List.map workspace.contexts ~f:(fun ctx_def ->
let name = Workspace.Context.name ctx_def in
Context.create ctx_def ~merlin:(workspace.merlin_context = Some name) ~use_findlib)
)
>>= fun contexts ->
List.iter contexts ~f:(fun ctx ->
let contexts = List.concat contexts in
List.iter contexts ~f:(fun (ctx : Context.t) ->
Log.infof log "@[<1>Jbuilder context:@,%a@]@." Sexp.pp (Context.sexp_of_t ctx));
Gen_rules.gen conf ~contexts
Gen_rules.gen conf
~contexts
?unlink_aliases
?only_packages
?filter_out_optional_stanzas_with_missing_deps
>>= fun (rules, stanzas) ->
let build_system = Build_system.create ~contexts ~file_tree:conf.file_tree ~rules in
let build_system = Build_system.create ~contexts
~file_tree:conf.file_tree ~rules in
return { build_system
; stanzas
; contexts
@ -211,7 +221,7 @@ let bootstrap () =
Clflags.debug_dep_path := true;
let log = Log.create () in
Future.Scheduler.go ~log
(setup ~log ~workspace:{ merlin_context = Some "default"; contexts = [Default] }
(setup ~log ~workspace:{ merlin_context = Some "default"; contexts = [Default [Native]] }
~use_findlib:false
~extra_ignored_subtrees:ignored_during_bootstrap
()

View File

@ -22,6 +22,7 @@ val setup
-> ?workspace:Workspace.t
-> ?workspace_file:string
-> ?only_packages:String_set.t
-> ?x:string
-> unit
-> setup Future.t
val external_lib_deps

View File

@ -2,36 +2,69 @@ open Import
open Sexp.Of_sexp
module Context = struct
module Target = struct
type t =
| Native
| Named of string
let t sexp =
match string sexp with
| "native" -> Native
| s -> Named s
end
module Opam = struct
type t =
{ name : string
; switch : string
; root : string option
; merlin : bool
; targets : Target.t list
}
let t =
record
(field "switch" string >>= fun switch ->
field "switch" string >>= fun switch ->
field "name" string ~default:switch >>= fun name ->
field "targets" (list Target.t) ~default:[Target.Native] >>= fun targets ->
field_o "root" string >>= fun root ->
field_b "merlin" >>= fun merlin ->
return { switch
; name
; root
; merlin
})
; targets
}
end
type t = Default | Opam of Opam.t
type t = Default of Target.t list | Opam of Opam.t
let t = function
| Atom (_, "default") -> Default
| sexp -> Opam (Opam.t sexp)
| Atom (_, "default") -> Default [Native]
| List (_, List _ :: _) as sexp -> Opam (record Opam.t sexp)
| sexp ->
sum
[ cstr_record "default"
(field "targets" (list Target.t) ~default:[Target.Native]
>>= fun targets ->
return (Default targets))
; cstr_record "opam"
(Opam.t >>= fun x -> return (Opam x))
]
sexp
let name = function
| Default -> "default"
| Default _ -> "default"
| Opam o -> o.name
let targets = function
| Default l -> l
| Opam o -> o.targets
let all_names t =
let n = name t in
n :: List.filter_map (targets t) ~f:(function
| Native -> None
| Named s -> Some (n ^ "." ^ s))
end
type t =
@ -39,7 +72,8 @@ type t =
; contexts : Context.t list
}
let t sexps =
let t ?x sexps =
let defined_names = ref String_set.empty in
let merlin_ctx, contexts =
List.fold_left sexps ~init:(None, []) ~f:(fun (merlin_ctx, ctxs) sexp ->
let ctx =
@ -47,6 +81,21 @@ let t sexps =
[ cstr "context" (Context.t @> nil) (fun x -> x) ]
sexp
in
let ctx =
match x with
| None -> ctx
| Some s ->
let target = Context.Target.Named s in
let add_target target targets =
if List.mem target ~set:targets then
targets
else
targets @ [target]
in
match ctx with
| Default targets -> Default (add_target target targets)
| Opam o -> Opam { o with targets = add_target target o.targets }
in
let name = Context.name ctx in
if name = "" ||
String.is_prefix name ~prefix:"." ||
@ -55,8 +104,9 @@ let t sexps =
String.contains name '/' ||
String.contains name '\\' then
of_sexp_errorf sexp "%S is not allowed as a build context name" name;
if List.exists ctxs ~f:(fun c -> Context.name c = name) then
if String_set.mem name !defined_names then
of_sexp_errorf sexp "second definition of build context %S" name;
defined_names := String_set.union !defined_names (String_set.of_list (Context.all_names ctx));
match ctx, merlin_ctx with
| Opam { merlin = true; _ }, Some _ ->
of_sexp_errorf sexp "you can only have one context for merlin"
@ -67,14 +117,14 @@ let t sexps =
in
let contexts =
match contexts with
| [] -> [Context.Default]
| [] -> [Context.Default [Native]]
| _ -> contexts
in
let merlin_ctx =
match merlin_ctx with
| Some _ -> merlin_ctx
| None ->
if List.mem Context.Default ~set:contexts then
if List.exists contexts ~f:(function Context.Default _ -> true | _ -> false) then
Some "default"
else
None
@ -83,4 +133,4 @@ let t sexps =
; contexts = List.rev contexts
}
let load fname = t (Sexp.load ~fname ~mode:Many)
let load ?x fname = t ?x (Sexp.load ~fname ~mode:Many)

View File

@ -3,16 +3,24 @@
open! Import
module Context : sig
module Target : sig
type t =
| Native
| Named of string
end
module Opam : sig
type t =
{ name : string
; switch : string
; root : string option
; merlin : bool
; targets : Target.t list
}
end
type t = Default | Opam of Opam.t
type t = Default of Target.t list | Opam of Opam.t
val name : t -> string
end
type t =
@ -20,4 +28,4 @@ type t =
; contexts : Context.t list
}
val load : string -> t
val load : ?x:string -> string -> t