diff --git a/bin/main.ml b/bin/main.ml index 0caa0337..07b43e4d 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -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 diff --git a/jbuild-workspace.dev b/jbuild-workspace.dev index 1e045a47..1e390d9a 100644 --- a/jbuild-workspace.dev +++ b/jbuild-workspace.dev @@ -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))) diff --git a/src/context.ml b/src/context.ml index f7f87bf4..baea3baf 100644 --- a/src/context.ml +++ b/src/context.ml @@ -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,195 +190,258 @@ 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 ocamlc = - match which "ocamlc" with - | None -> prog_not_found_in_path "ocamlc" - | Some x -> x + 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 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 = - 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) - | None -> - (* If there no ocamlfind in the PATH, check if we have opam and assume a stan opam - setup *) - opam_config_var ~env ~cache:opam_var_cache "lib" - >>| function - | Some s -> [Path.absolute s] - | None -> - (* If neither opam neither ocamlfind are present, assume that libraries are - [dir ^ "/../lib"] *) - [Path.relative (Path.parent dir) "lib"] - else - return [] - in - both - 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 - | 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 - let get_opt var = String_map.find var ocamlc_config in - let get ?default var = - match get_opt var with - | Some s -> s - | None -> - match default with + + 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 get_tool_using_findlib_config "ocamlc" with | Some x -> x | None -> - die "variable %S not found in the output of `%s`" var ocamlc_config_cmd - in - let get_bool ?default var = - match get ?default:(Option.map default ~f:string_of_bool) var with - | "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 - let natdynlink_supported = Path.exists (Path.relative stdlib_dir "dynlink.cmxa") in - let version = get "version" in - let env,env_extra = - (* See comment in ansi_color.ml for setup_env_for_colors. For OCaml < 4.05, - OCAML_COLOR is not supported so we use OCAMLPARAM. OCaml 4.02 doesn't support - 'color' in OCAMLPARAM, so we just don't force colors with 4.02. *) - let ocaml_version = Scanf.sscanf version "%u.%u" (fun a b -> a, b) in - if !Clflags.capture_outputs - && Lazy.force Ansi_color.stderr_supports_colors - && ocaml_version > (4, 02) - && ocaml_version < (4, 05) then - let value = - match get_env env "OCAMLPARAM" with - | None -> "color=always,_" - | Some s -> "color=always," ^ s - in - extend_env ~env ~vars:((Env_var_map.singleton "OCAMLPARAM" value)), - (Env_var_map.add ~key:"OCAMLPARAM" ~data:value env_extra) - else - env,env_extra - in - let c_compiler, ocamlc_cflags, ocamlopt_cflags = - match get_opt "c_compiler" with - | Some c_compiler -> (* >= 4.06 *) - (c_compiler, get "ocamlc_cflags", get "ocamlopt_cflags") - | None -> - let split_prog s = - let len = String.length s in - let rec loop i = - if i = len then - (s, "") - else - match s.[i] with - | ' ' | '\t' -> - (String.sub s ~pos:0 ~len:i, - String.sub s ~pos:i ~len:(len - i)) - | _ -> loop (i + 1) + match which "ocamlc" with + | Some x -> x + | None -> prog_not_found_in_path "ocamlc" + in + let dir = Path.parent ocamlc in + 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 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 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 -> + 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 standard opam setup *) + opam_config_var ~env ~cache:opam_var_cache "lib" + >>| function + | Some s -> [Path.absolute s] + | None -> + (* If neither opam neither ocamlfind are present, assume that libraries are + [dir ^ "/../lib"] *) + [Path.relative (Path.parent dir) "lib"] + else + return [] + in + both + 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 + | 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 + let get_opt var = String_map.find var ocamlc_config in + let get ?default var = + match get_opt var with + | Some s -> s + | None -> + match default with + | Some x -> x + | None -> + die "variable %S not found in the output of `%s`" var ocamlc_config_cmd + in + let get_bool ?default var = + match get ?default:(Option.map default ~f:string_of_bool) var with + | "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 + let natdynlink_supported = Path.exists (Path.relative stdlib_dir "dynlink.cmxa") in + let version = get "version" in + let env, env_extra = + (* See comment in ansi_color.ml for setup_env_for_colors. For OCaml < 4.05, + OCAML_COLOR is not supported so we use OCAMLPARAM. OCaml 4.02 doesn't support + 'color' in OCAMLPARAM, so we just don't force colors with 4.02. *) + let ocaml_version = Scanf.sscanf version "%u.%u" (fun a b -> a, b) in + if !Clflags.capture_outputs + && Lazy.force Ansi_color.stderr_supports_colors + && ocaml_version > (4, 02) + && ocaml_version < (4, 05) then + let value = + match get_env env "OCAMLPARAM" with + | None -> "color=always,_" + | Some s -> "color=always," ^ s in - loop 0 - in - let c_compiler, ocamlc_cflags = split_prog (get "bytecomp_c_compiler") in - let _, ocamlopt_cflags = split_prog (get "native_c_compiler") in - (c_compiler, ocamlc_cflags, ocamlopt_cflags) + extend_env ~env ~vars:((Env_var_map.singleton "OCAMLPARAM" value)), + (Env_var_map.add ~key:"OCAMLPARAM" ~data:value env_extra) + else + env,env_extra + in + let c_compiler, ocamlc_cflags, ocamlopt_cflags = + match get_opt "c_compiler" with + | Some c_compiler -> (* >= 4.06 *) + (c_compiler, get "ocamlc_cflags", get "ocamlopt_cflags") + | None -> + let split_prog s = + let len = String.length s in + let rec loop i = + if i = len then + (s, "") + else + match s.[i] with + | ' ' | '\t' -> + (String.sub s ~pos:0 ~len:i, + String.sub s ~pos:i ~len:(len - i)) + | _ -> loop (i + 1) + in + loop 0 + in + let c_compiler, ocamlc_cflags = split_prog (get "bytecomp_c_compiler") in + let _, ocamlopt_cflags = split_prog (get "native_c_compiler") in + (c_compiler, ocamlc_cflags, ocamlopt_cflags) + in + let arch_sixtyfour = + match get_opt "word_size" with + | Some ws -> ws = "64" + | None -> get_arch_sixtyfour stdlib_dir + in + return + { name + ; implicit + ; kind + ; merlin + ; for_host = host + ; build_dir + ; path + ; toplevel_path = Option.map (get_env env "OCAML_TOPLEVEL_PATH") ~f:Path.absolute + + ; ocaml_bin = dir + ; ocaml = (match which "ocaml" with Some p -> p | None -> prog_not_found_in_path "ocaml") + ; ocamlc + ; 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 + + ; natdynlink_supported + + ; stdlib_dir + ; ocamlc_config = String_map.bindings ocamlc_config + ; version + ; ccomp_type = get "ccomp_type" + ; c_compiler + ; ocamlc_cflags + ; ocamlopt_cflags + ; bytecomp_c_libraries = get "bytecomp_c_libraries" + ; 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" + ; flambda = get_bool "flambda" ~default:false + ; 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" + + ; which_cache + } in - let arch_sixtyfour = - match get_opt "word_size" with - | Some ws -> ws = "64" - | None -> get_arch_sixtyfour stdlib_dir - in - return - { name - ; kind - ; merlin - ; for_host = None - ; 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) - ; ocamlc - ; ocamlopt = best_prog "ocamlopt" - ; ocamldep = get_prog "ocamldep" - ; ocamlmklib = get_prog "ocamlmklib" - - ; env - ; env_extra - ; findlib = Findlib.create ~stdlib_dir ~path:findlib_path - ; arch_sixtyfour - - ; opam_var_cache - - ; natdynlink_supported - - ; stdlib_dir - ; ocamlc_config = String_map.bindings ocamlc_config - ; version - ; ccomp_type = get "ccomp_type" - ; c_compiler - ; ocamlc_cflags - ; ocamlopt_cflags - ; bytecomp_c_libraries = get "bytecomp_c_libraries" - ; 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" - ; flambda = get_bool "flambda" ~default:false - ; 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" - - ; which_cache - } + 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 diff --git a/src/context.mli b/src/context.mli index cfc906f6..88db55fa 100644 --- a/src/context.mli +++ b/src/context.mli @@ -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 diff --git a/src/main.ml b/src/main.ml index 29fd3855..55f68c28 100644 --- a/src/main.ml +++ b/src/main.ml @@ -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 () diff --git a/src/main.mli b/src/main.mli index 16821729..13476a18 100644 --- a/src/main.mli +++ b/src/main.mli @@ -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 diff --git a/src/workspace.ml b/src/workspace.ml index a5027681..9e951978 100644 --- a/src/workspace.ml +++ b/src/workspace.ml @@ -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 + { name : string + ; switch : string + ; root : string option + ; merlin : bool + ; targets : Target.t list } let t = - record - (field "switch" string >>= fun switch -> - field "name" string ~default:switch >>= fun name -> - field_o "root" string >>= fun root -> - field_b "merlin" >>= fun merlin -> - return { switch - ; name - ; root - ; merlin - }) + 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" - | Opam o -> o.name + | 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) diff --git a/src/workspace.mli b/src/workspace.mli index 750a8952..27463e9c 100644 --- a/src/workspace.mli +++ b/src/workspace.mli @@ -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