Merge pull request #380 from rgrinberg/yy

Cross Compilation
This commit is contained in:
Rudi Grinberg 2018-01-01 22:47:44 +08:00 committed by GitHub
commit 5cb909bb80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 866 additions and 367 deletions

View File

@ -19,7 +19,7 @@ next
absolute path but with the context's environment set appropriately. Lastly, absolute path but with the context's environment set appropriately. Lastly,
`jbuilder exec` will change the root as to which paths are relative using the `jbuilder exec` will change the root as to which paths are relative using the
`-root` option. (#286) `-root` option. (#286)
- Fix `jbuilder rules` printing rules when some binaries are missing (#292) - Fix `jbuilder rules` printing rules when some binaries are missing (#292)
- Build documentation for non public libraries (#306) - Build documentation for non public libraries (#306)
@ -28,6 +28,11 @@ next
- Fix copy# for C/C++ with Microsoft C compiler (#353) - Fix copy# for C/C++ with Microsoft C compiler (#353)
- Add support for cross-compilation. Currently we are supporting the
opam-cross-x repositories such as
[opam-cross-windows](https://github.com/whitequark/opam-cross-windows)
(#355)
1.0+beta16 (05/11/2017) 1.0+beta16 (05/11/2017)
----------------------- -----------------------

View File

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

View File

@ -102,6 +102,99 @@ set of predicates:
it is linked as part of a driver or meant to add a ``-ppx`` argument it is linked as part of a driver or meant to add a ``-ppx`` argument
to the compiler, choose the former behavior to the compiler, choose the former behavior
Cross Compilation
=================
Jbuilder allows for cross compilation by defining build contexts with
multiple targets. Targets are specified by adding a ``targets`` field
to the definition of a build context.
``targets`` takes a list of target name. It can be either:
- ``native`` which means using the native tools that can build
binaries that run on the machine doing the build
- the name of an alternative toolchain
Note that at the moment, there is no official support for
cross-compilation in OCaml. Jbuilder supports the two following
opam-cross-x repositories:
- `opam-cross-windows <https://github.com/whitequark/opam-cross-windows>`_
- `opam-cross-android <https://github.com/whitequark/opam-cross-android>`_
To build Windows binaries using opam-cross-windows, write ``windows``
in the list of targets. To build Android binaries using
opam-cross-android, write ``android`` in the list of targets.
For example, the following workspace file defines three different
targets for the ``default`` build context:
.. code:: scheme
(context (default (targets (native windows android))))
This configuration defines three build contexts:
- ``default``
- ``default.windows``
- ``default.android``
Note that the ``native`` target is always implicitly added when not
present. However, when implicitly added ``jbuilder build @install``
will skip this context, i.e. ``default`` will only be used for
building executables needed by the other contexts.
With such a setup, calling ``jbuilder build @install`` will build all
the packages three times.
Note that instead of writing a ``jbuild-workspace`` file, you can also
use the ``-x`` command line option. Passing ``-x foo`` to ``jbuilder``
without having a ``jbuild-workspace`` file is the same as writing the
following ``jbuild-workspace`` file:
.. code:: scheme
(context (default (targets (foo))))
If you have a ``jbuild-workspace`` and pass a ``-x foo`` option,
``foo`` will be added as target of all context stanzas.
How does it work?
-----------------
In such a setup, binaries that need to be built and executed in the
``default.windows`` or ``default.android`` contexts as part of the
build, will no longer be executed. Instead, all the binaries that will
be executed will come from the ``default`` context. One consequence of
this is that all preprocessing (ppx or otherwise) will be done using
binaries built in the ``default`` context.
To clarify this with an example, let's assume that you have the
following ``src/jbuild`` file:
.. code:: scheme
(executable ((name foo)))
(rule (with-stdout-to blah (run ./foo.exe)))
When building ``_build/default/src/blah``, jbuilder will resolve ``./foo.exe`` to
``_build/default/src/foo.exe`` as expected. However, for
``_build/default.windows/src/blah`` jbuilder will resolve ``./foo.exe`` to
``_build/default/src/foo.exe``
Assuming that the right packages are installed or that your workspace
has no external dependencies, jbuilder will be able to cross-compile a
given package without doing anything special.
Some packages might still have to be updated to support cross-compilation. For
instance if the ``foo.exe`` program in the previous example was using
``Sys.os_type``, it should instead take it as a command line argument:
.. code:: scheme
(rule (with-stdout-to blah (run ./foo.exe -os-type ${os_type})))
Classical ppx Classical ppx
============= =============

View File

@ -334,9 +334,9 @@ a typical ``jbuild-workspace`` file looks like:
.. code:: scheme .. code:: scheme
(context ((switch 4.02.3))) (context (opam (switch 4.02.3)))
(context ((switch 4.03.0))) (context (opam (switch 4.03.0)))
(context ((switch 4.04.0))) (context (opam (switch 4.04.0)))
The rest of this section describe the stanzas available. The rest of this section describe the stanzas available.
@ -354,13 +354,13 @@ context
~~~~~~~ ~~~~~~~
The ``(context ...)`` stanza declares a build context. The argument The ``(context ...)`` stanza declares a build context. The argument
can be either ``default`` for the default build context or can be the can be either ``default`` or ``(default)`` for the default build
description of an opam switch, as follows: context or can be the description of an opam switch, as follows:
.. code:: scheme .. code:: scheme
(context ((switch <opam-switch-name>) (context (opam (switch <opam-switch-name>)
<optional-fields>)) <optional-fields>))
``<optional-fields>`` are: ``<optional-fields>`` are:
@ -374,6 +374,10 @@ description of an opam switch, as follows:
- ``(merlin)`` instructs Jbuilder to use this build context for - ``(merlin)`` instructs Jbuilder to use this build context for
merlin merlin
Both ``(default ...)`` and ``(opam ...)`` accept a ``targets`` field
in order to setup cross compilation. See `Cross Compilation`_ for more
information.
Merlin reads compilation artifacts and it can only read the Merlin reads compilation artifacts and it can only read the
compilation artifacts of a single context. Usually, you should use compilation artifacts of a single context. Usually, you should use
the artifacts from the ``default`` context, and if you have the the artifacts from the ``default`` context, and if you have the
@ -384,6 +388,15 @@ For rare cases where this is not what you want, you can force Jbuilder
to use a different build contexts for merlin by adding the field to use a different build contexts for merlin by adding the field
``(merlin)`` to this context. ``(merlin)`` to this context.
Note that the following syntax is still accepted but is deprecated:
.. code:: scheme
(context ((switch <opam-switch-name>)
<optional-fields>))
it is interpreted the same as ``(context (opam (switch ...) ...))``.
Building JavaScript with js_of_ocaml Building JavaScript with js_of_ocaml
==================================== ====================================

View File

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

View File

@ -327,23 +327,28 @@ module Unexpanded = struct
~map:(fun x -> (x, [])) ~map:(fun x -> (x, []))
end end
let rec expand dir t ~f : Unresolved.t = let rec expand t ~dir ~map_exe ~f : Unresolved.t =
match t with match t with
| Run (prog, args) -> | Run (prog, args) ->
let args = List.concat_map args ~f:(E.strings ~dir ~f) in let args = List.concat_map args ~f:(E.strings ~dir ~f) in
let prog, more_args = E.prog_and_args ~dir ~f prog in let prog, more_args = E.prog_and_args ~dir ~f prog in
let prog =
match prog with
| Search _ -> prog
| This path -> This (map_exe path)
in
Run (prog, more_args @ args) Run (prog, more_args @ args)
| Chdir (fn, t) -> | Chdir (fn, t) ->
let fn = E.path ~dir ~f fn in let fn = E.path ~dir ~f fn in
Chdir (fn, expand fn t ~f) Chdir (fn, expand t ~dir:fn ~map_exe ~f)
| Setenv (var, value, t) -> | Setenv (var, value, t) ->
Setenv (E.string ~dir ~f var, E.string ~dir ~f value, Setenv (E.string ~dir ~f var, E.string ~dir ~f value,
expand dir t ~f) expand t ~dir ~map_exe ~f)
| Redirect (outputs, fn, t) -> | Redirect (outputs, fn, t) ->
Redirect (outputs, E.path ~dir ~f fn, expand dir t ~f) Redirect (outputs, E.path ~dir ~f fn, expand t ~dir ~map_exe ~f)
| Ignore (outputs, t) -> | Ignore (outputs, t) ->
Ignore (outputs, expand dir t ~f) Ignore (outputs, expand t ~dir ~map_exe ~f)
| Progn l -> Progn (List.map l ~f:(fun t -> expand dir t ~f)) | Progn l -> Progn (List.map l ~f:(fun t -> expand t ~dir ~map_exe ~f))
| Echo x -> Echo (E.string ~dir ~f x) | Echo x -> Echo (E.string ~dir ~f x)
| Cat x -> Cat (E.path ~dir ~f x) | Cat x -> Cat (E.path ~dir ~f x)
| Copy (x, y) -> | Copy (x, y) ->
@ -406,7 +411,7 @@ module Unexpanded = struct
~special:VE.to_prog_and_args ~special:VE.to_prog_and_args
end end
let rec partial_expand dir t ~f : Partial.t = let rec partial_expand t ~dir ~map_exe ~f : Partial.t =
match t with match t with
| Run (prog, args) -> | Run (prog, args) ->
let args = let args =
@ -419,6 +424,11 @@ module Unexpanded = struct
match E.prog_and_args ~dir ~f prog with match E.prog_and_args ~dir ~f prog with
| Inl (prog, more_args) -> | Inl (prog, more_args) ->
let more_args = List.map more_args ~f:(fun x -> Inl x) in let more_args = List.map more_args ~f:(fun x -> Inl x) in
let prog =
match prog with
| Search _ -> prog
| This path -> This (map_exe path)
in
Run (Inl prog, more_args @ args) Run (Inl prog, more_args @ args)
| Inr _ as prog -> | Inr _ as prog ->
Run (prog, args) Run (prog, args)
@ -427,7 +437,7 @@ module Unexpanded = struct
let res = E.path ~dir ~f fn in let res = E.path ~dir ~f fn in
match res with match res with
| Inl dir -> | Inl dir ->
Chdir (res, partial_expand dir t ~f) Chdir (res, partial_expand t ~dir ~map_exe ~f)
| Inr fn -> | Inr fn ->
let loc = SW.loc fn in let loc = SW.loc fn in
Loc.fail loc Loc.fail loc
@ -436,12 +446,12 @@ module Unexpanded = struct
end end
| Setenv (var, value, t) -> | Setenv (var, value, t) ->
Setenv (E.string ~dir ~f var, E.string ~dir ~f value, Setenv (E.string ~dir ~f var, E.string ~dir ~f value,
partial_expand dir t ~f) partial_expand t ~dir ~map_exe ~f)
| Redirect (outputs, fn, t) -> | Redirect (outputs, fn, t) ->
Redirect (outputs, E.path ~dir ~f fn, partial_expand dir t ~f) Redirect (outputs, E.path ~dir ~f fn, partial_expand t ~dir ~map_exe ~f)
| Ignore (outputs, t) -> | Ignore (outputs, t) ->
Ignore (outputs, partial_expand dir t ~f) Ignore (outputs, partial_expand t ~dir ~map_exe ~f)
| Progn l -> Progn (List.map l ~f:(fun t -> partial_expand dir t ~f)) | Progn l -> Progn (List.map l ~f:(fun t -> partial_expand t ~dir ~map_exe ~f))
| Echo x -> Echo (E.string ~dir ~f x) | Echo x -> Echo (E.string ~dir ~f x)
| Cat x -> Cat (E.path ~dir ~f x) | Cat x -> Cat (E.path ~dir ~f x)
| Copy (x, y) -> | Copy (x, y) ->
@ -525,6 +535,20 @@ type exec_context =
} }
let run ~ectx ~dir ~env_extra ~stdout_to ~stderr_to prog args = let run ~ectx ~dir ~env_extra ~stdout_to ~stderr_to prog args =
begin match ectx.context with
| None
| Some { Context.for_host = None; _ } -> ()
| Some ({ Context.for_host = Some host; _ } as target) ->
let invalid_prefix prefix =
match Path.descendant prog ~of_:(Path.of_string prefix) with
| None -> ()
| Some _ ->
die "Context %s has a host %s.@.It's not possible to execute binary %a \
in it.@.@.This is a bug and should be reported upstream."
target.name host.name Path.pp prog in
invalid_prefix ("_build/" ^ target.name);
invalid_prefix ("_build/install/" ^ target.name);
end;
let stdout_to = get_std_output stdout_to in let stdout_to = get_std_output stdout_to in
let stderr_to = get_std_output stderr_to in let stderr_to = get_std_output stderr_to in
let env = Context.extend_env ~vars:env_extra ~env:ectx.env in let env = Context.extend_env ~vars:env_extra ~env:ectx.env in

View File

@ -78,15 +78,17 @@ module Unexpanded : sig
with type string = (string , String_with_vars.t) either with type string = (string , String_with_vars.t) either
val expand val expand
: Path.t : t
-> t -> dir:Path.t
-> map_exe:(Path.t -> Path.t)
-> f:(Loc.t -> String.t -> Var_expansion.t option) -> f:(Loc.t -> String.t -> Var_expansion.t option)
-> Unresolved.t -> Unresolved.t
end end
val partial_expand val partial_expand
: Path.t : t
-> t -> dir:Path.t
-> map_exe:(Path.t -> Path.t)
-> f:(Loc.t -> string -> Var_expansion.t option) -> f:(Loc.t -> string -> Var_expansion.t option)
-> Partial.t -> Partial.t
end end

View File

@ -34,6 +34,7 @@ type t =
; kind : Kind.t ; kind : Kind.t
; merlin : bool ; merlin : bool
; for_host : t option ; for_host : t option
; implicit : bool
; build_dir : Path.t ; build_dir : Path.t
; path : Path.t list ; path : Path.t list
; toplevel_path : Path.t option ; toplevel_path : Path.t option
@ -46,6 +47,7 @@ type t =
; env : string array ; env : string array
; env_extra : string Env_var_map.t ; env_extra : string Env_var_map.t
; findlib : Findlib.t ; findlib : Findlib.t
; findlib_toolchain : string option
; arch_sixtyfour : bool ; arch_sixtyfour : bool
; opam_var_cache : (string, string) Hashtbl.t ; opam_var_cache : (string, string) Hashtbl.t
; natdynlink_supported : bool ; natdynlink_supported : bool
@ -175,7 +177,8 @@ let extend_env ~vars ~env =
imported imported
|> Array.of_list |> 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 env = extend_env ~env:base_env ~vars:env_extra in
let opam_var_cache = Hashtbl.create 128 in let opam_var_cache = Hashtbl.create 128 in
(match kind with (match kind with
@ -187,195 +190,258 @@ let create ~(kind : Kind.t) ~path ~base_env ~env_extra ~name ~merlin ~use_findli
in in
let which_cache = Hashtbl.create 128 in let which_cache = Hashtbl.create 128 in
let which x = which ~cache:which_cache ~path x in let which x = which ~cache:which_cache ~path x in
let ocamlc = let findlib_config_path = lazy (
match which "ocamlc" with match which "ocamlfind" with
| None -> prog_not_found_in_path "ocamlc" | None -> prog_not_found_in_path "ocamlfind"
| Some x -> x | 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 in
let dir = Path.parent ocamlc in
let prog_not_found prog = let create_one ~name ~implicit ?findlib_toolchain ?host ~merlin () =
die "ocamlc found in %s, but %s/%s doesn't exist (context: %s)" (match findlib_toolchain with
(Path.to_string dir) (Path.to_string dir) prog name | None -> Future.return None
in | Some toolchain ->
let best_prog prog = Bin.best_prog dir prog in Lazy.force findlib_config_path >>| fun path ->
let get_prog prog = Some (Findlib.Config.load path ~toolchain ~context:name))
match best_prog prog with >>= fun findlib_config ->
| None -> prog_not_found prog
| Some fn -> fn let get_tool_using_findlib_config prog =
in match findlib_config with
let build_dir = | None -> None
Path.of_string (sprintf "_build/%s" name) | Some conf ->
in match Findlib.Config.get conf prog with
let ocamlc_config_cmd = sprintf "%s -config" (Path.to_string ocamlc) in | "" -> None
let findlib_path = | s ->
if use_findlib then match Filename.analyze_program_name s with
(* If ocamlfind is present, it has precedence over everything else. *) | In_path | Relative_to_current_dir -> which s
match which "ocamlfind" with | Absolute -> Some (Path.absolute s)
| Some fn -> in
(Future.run_capture_lines ~env Strict
(Path.to_string fn) ["printconf"; "path"] let ocamlc =
>>| List.map ~f:Path.absolute) match get_tool_using_findlib_config "ocamlc" with
| 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
| Some x -> x | Some x -> x
| None -> | None ->
die "variable %S not found in the output of `%s`" var ocamlc_config_cmd match which "ocamlc" with
in | Some x -> x
let get_bool ?default var = | None -> prog_not_found_in_path "ocamlc"
match get ?default:(Option.map default ~f:string_of_bool) var with in
| "true" -> true let dir = Path.parent ocamlc in
| "false" -> false let ocaml_tool_not_found prog =
| _ -> die "variable %S is neither 'true' neither 'false' in the output of `%s`" die "ocamlc found in %s, but %s/%s doesn't exist (context: %s)"
var ocamlc_config_cmd (Path.to_string dir) (Path.to_string dir) prog name
in in
let get_path var = Path.absolute (get var) in let get_ocaml_tool prog =
let stdlib_dir = get_path "standard_library" in match get_tool_using_findlib_config prog with
let natdynlink_supported = Path.exists (Path.relative stdlib_dir "dynlink.cmxa") in | None -> Bin.best_prog dir prog
let version = get "version" in | Some _ as x -> x
let env,env_extra = in
(* See comment in ansi_color.ml for setup_env_for_colors. For OCaml < 4.05, let get_ocaml_tool_exn prog =
OCAML_COLOR is not supported so we use OCAMLPARAM. OCaml 4.02 doesn't support match get_ocaml_tool prog with
'color' in OCAMLPARAM, so we just don't force colors with 4.02. *) | None -> ocaml_tool_not_found prog
let ocaml_version = Scanf.sscanf version "%u.%u" (fun a b -> a, b) in | Some fn -> fn
if !Clflags.capture_outputs in
&& Lazy.force Ansi_color.stderr_supports_colors
&& ocaml_version > (4, 02) let build_dir = Path.of_string (sprintf "_build/%s" name) in
&& ocaml_version < (4, 05) then let ocamlc_config_cmd = sprintf "%s -config" (Path.to_string ocamlc) in
let value = let findlib_path =
match get_env env "OCAMLPARAM" with if use_findlib then
| None -> "color=always,_" (* If ocamlfind is present, it has precedence over everything else. *)
| Some s -> "color=always," ^ s match which "ocamlfind" with
in | Some fn ->
extend_env ~env ~vars:((Env_var_map.singleton "OCAMLPARAM" value)), let args =
(Env_var_map.add ~key:"OCAMLPARAM" ~data:value env_extra) let args = ["printconf"; "path"] in
else match findlib_toolchain with
env,env_extra | None -> args
in | Some s -> "-toolchain" :: s :: args
let c_compiler, ocamlc_cflags, ocamlopt_cflags = in
match get_opt "c_compiler" with Future.run_capture_lines ~env Strict (Path.to_string fn) args
| Some c_compiler -> (* >= 4.06 *) >>| List.map ~f:Path.absolute
(c_compiler, get "ocamlc_cflags", get "ocamlopt_cflags") | None ->
| None -> (* If there no ocamlfind in the PATH, check if we have opam
let split_prog s = and assume a standard opam setup *)
let len = String.length s in opam_config_var ~env ~cache:opam_var_cache "lib"
let rec loop i = >>| function
if i = len then | Some s -> [Path.absolute s]
(s, "") | None ->
else (* If neither opam neither ocamlfind are present, assume that libraries are
match s.[i] with [dir ^ "/../lib"] *)
| ' ' | '\t' -> [Path.relative (Path.parent dir) "lib"]
(String.sub s ~pos:0 ~len:i, else
String.sub s ~pos:i ~len:(len - i)) return []
| _ -> loop (i + 1) 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 in
loop 0 extend_env ~env ~vars:((Env_var_map.singleton "OCAMLPARAM" value)),
in (Env_var_map.add ~key:"OCAMLPARAM" ~data:value env_extra)
let c_compiler, ocamlc_cflags = split_prog (get "bytecomp_c_compiler") in else
let _, ocamlopt_cflags = split_prog (get "native_c_compiler") in env,env_extra
(c_compiler, ocamlc_cflags, ocamlopt_cflags) 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 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 let implicit = not (List.mem ~set:targets Workspace.Context.Target.Native) in
; ocaml = Path.relative dir ("ocaml" ^ Bin.exe) create_one () ~implicit ~name ~merlin >>= fun native ->
; ocamlc Future.all (
; ocamlopt = best_prog "ocamlopt" List.filter_map targets ~f:(function
; ocamldep = get_prog "ocamldep" | Native -> None
; ocamlmklib = get_prog "ocamlmklib" | Named findlib_toolchain ->
let name = sprintf "%s.%s" name findlib_toolchain in
; env Some (create_one () ~implicit:false ~name ~findlib_toolchain ~host:native
; env_extra ~merlin:false)
; findlib = Findlib.create ~stdlib_dir ~path:findlib_path )
; arch_sixtyfour ) >>| fun others ->
native :: others
; 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 opam_config_var t var = opam_config_var ~env:t.env ~cache:t.opam_var_cache var 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; Lazy.force Ansi_color.setup_env_for_colors;
Unix.environment ()) 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 env = Lazy.force initial_env in
let path = let path =
match get_env env "PATH" with match get_env env "PATH" with
@ -391,9 +457,9 @@ let default ?(merlin=true) ?(use_findlib=true) () =
| None -> [] | None -> []
in in
create ~kind:Default ~path ~base_env:env ~env_extra:Env_var_map.empty 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 match Bin.opam with
| None -> Utils.program_not_found "opam" | None -> Utils.program_not_found "opam"
| Some fn -> | Some fn ->
@ -430,8 +496,14 @@ let create_for_opam ?root ~switch ~name ?(merlin=false) () =
| Some s -> Bin.parse_path s | Some s -> Bin.parse_path s
in in
let env = Lazy.force initial_env in let env = Lazy.force initial_env in
create ~kind:(Opam { root; switch }) ~path ~base_env:env ~env_extra:vars create ~kind:(Opam { root; switch }) ~targets
~name ~merlin ~use_findlib:true ~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 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. *) building tools used for the compilation that run on the host. *)
for_host : t option 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" *) ; (** Directory where artifact are stored, for instance "_build/default" *)
build_dir : Path.t build_dir : Path.t
@ -72,6 +76,7 @@ type t =
env_extra : string Env_var_map.t env_extra : string Env_var_map.t
; findlib : Findlib.t ; findlib : Findlib.t
; findlib_toolchain : string option
; (** Misc *) ; (** Misc *)
arch_sixtyfour : bool arch_sixtyfour : bool
@ -124,18 +129,14 @@ val sexp_of_t : t -> Sexp.t
(** Compare the context names *) (** Compare the context names *)
val compare : t -> t -> int 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 (** 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 ocamlfind. This is only for building jbuilder itself, so that its build is completely
independent of the user setup. *) 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 val which : t -> string -> Path.t option

View File

@ -40,9 +40,7 @@ let load ?(extra_ignored_subtrees=Path.Set.empty) path =
Path.readdir path Path.readdir path
|> List.filter_map ~f:(fun fn -> |> List.filter_map ~f:(fun fn ->
let path = Path.relative path fn in let path = Path.relative path fn in
let is_directory = let is_directory = Path.is_directory path in
try Path.is_directory path with _ -> false
in
if ignore_file fn ~is_directory then if ignore_file fn ~is_directory then
None None
else if is_directory then else if is_directory then

View File

@ -118,6 +118,29 @@ module Vars = struct
let get_words t var preds = String.extract_comma_space_separated_words (get t var preds) let get_words t var preds = String.extract_comma_space_separated_words (get t var preds)
end end
module Config = struct
type t =
{ vars : Vars.t
; preds : string list
}
let load path ~toolchain ~context =
let path = Path.extend_basename path ~suffix:".d" in
let conf_file = Path.relative path (toolchain ^ ".conf") in
if not (Path.exists conf_file) then
die "@{<error>Error@}: ocamlfind toolchain %s isn't defined in %a \
(context: %s)" toolchain Path.pp path context;
let vars =
(Meta.simplify { name = ""
; entries = Meta.load (Path.to_string conf_file)
}).vars
in
{ vars = String_map.map vars ~f:Rules.of_meta_rules; preds = [toolchain] }
let get { vars; preds } var =
Vars.get vars var preds
end
type package = type package =
{ name : string { name : string
; dir : Path.t ; dir : Path.t

View File

@ -82,3 +82,9 @@ val all_packages : t -> package list
val all_unavailable_packages : t -> Package_not_available.t list val all_unavailable_packages : t -> Package_not_available.t list
val stdlib_with_archives : t -> package val stdlib_with_archives : t -> package
module Config : sig
type t
val load : Path.t -> toolchain:string -> context:string -> t
val get : t -> string -> string
end

View File

@ -1057,12 +1057,21 @@ Add it to your jbuild file to remove this warning.
entries entries
in in
let fn = let fn =
Path.relative (Path.append ctx.build_dir package_path) (package ^ ".install") Path.relative (Path.append ctx.build_dir package_path)
(Utils.install_file ~package ~findlib_toolchain:ctx.findlib_toolchain)
in in
let entries = local_install_rules entries ~package in let entries = local_install_rules entries ~package in
SC.add_rule sctx SC.add_rule sctx
(Build.path_set (Install.files entries) (Build.path_set (Install.files entries)
>>^ (fun () -> >>^ (fun () ->
let entries =
match ctx.findlib_toolchain with
| None -> entries
| Some toolchain ->
let prefix = Path.of_string (toolchain ^ "-sysroot") in
List.map entries
~f:(Install.Entry.add_install_prefix ~prefix ~package)
in
Install.gen_install_file entries) Install.gen_install_file entries)
>>> >>>
Build.write_file_dyn fn) Build.write_file_dyn fn)
@ -1086,22 +1095,28 @@ Add it to your jbuild file to remove this warning.
install_file pkg.path pkg.name stanzas) install_file pkg.path pkg.name stanzas)
let () = let () =
let is_default = Path.basename ctx.build_dir = "default" in let copy_to_src =
String_map.iter (SC.packages sctx) not ctx.implicit &&
~f:(fun ~key:pkg ~data:{ Package.path = src_path; _ } -> match ctx.kind with
let install_fn = pkg ^ ".install" in | Default -> true
| Opam _ -> false
in
if not ctx.implicit then
String_map.iter (SC.packages sctx)
~f:(fun ~key:pkg ~data:{ Package.path = src_path; _ } ->
let install_fn = Utils.install_file ~package:pkg ~findlib_toolchain:ctx.findlib_toolchain in
let ctx_path = Path.append ctx.build_dir src_path in let ctx_path = Path.append ctx.build_dir src_path in
let ctx_install_alias = Alias.install ~dir:ctx_path in let ctx_install_alias = Alias.install ~dir:ctx_path in
let ctx_install_file = Path.relative ctx_path install_fn in let ctx_install_file = Path.relative ctx_path install_fn in
Alias.add_deps (SC.aliases sctx) ctx_install_alias [ctx_install_file]; Alias.add_deps (SC.aliases sctx) ctx_install_alias [ctx_install_file];
if is_default then begin if copy_to_src then begin
let src_install_alias = Alias.install ~dir:src_path in let src_install_alias = Alias.install ~dir:src_path in
let src_install_file = Path.relative src_path install_fn in let src_install_file = Path.relative src_path install_fn in
SC.add_rule sctx (Build.copy ~src:ctx_install_file ~dst:src_install_file); SC.add_rule sctx (Build.copy ~src:ctx_install_file ~dst:src_install_file);
Alias.add_deps (SC.aliases sctx) src_install_alias [src_install_file] Alias.add_deps (SC.aliases sctx) src_install_alias [src_install_file]
end) end)
end end
let gen ~contexts ?(filter_out_optional_stanzas_with_missing_deps=true) let gen ~contexts ?(filter_out_optional_stanzas_with_missing_deps=true)
@ -1116,38 +1131,55 @@ let gen ~contexts ?(filter_out_optional_stanzas_with_missing_deps=true)
String_map.filter packages ~f:(fun _ { Package.name; _ } -> String_map.filter packages ~f:(fun _ { Package.name; _ } ->
String_set.mem name pkgs) String_set.mem name pkgs)
in in
List.map contexts ~f:(fun context -> let sctxs : (string, (Super_context.t * _)) Hashtbl.t = Hashtbl.create 4 in
Jbuild_load.Jbuilds.eval ~context jbuilds >>| fun stanzas -> let rec make_sctx (context : Context.t) : (_ * _) Future.t =
let stanzas = match Hashtbl.find sctxs context.name with
match only_packages with | Some r -> Future.return r
| None -> stanzas | None ->
| Some pkgs -> let host =
List.map stanzas ~f:(fun (dir, pkgs_ctx, stanzas) -> match context.for_host with
(dir, | None -> Future.return None
pkgs_ctx, | Some h -> make_sctx h >>| (fun (sctx, _) -> Some sctx)
List.filter stanzas ~f:(fun stanza -> in
match (stanza : Stanza.t) with let stanzas =
| Library { public = Some { package; _ }; _ } Jbuild_load.Jbuilds.eval ~context jbuilds >>| fun stanzas ->
| Alias { package = Some package ; _ } match only_packages with
| Install { package; _ } -> | None -> stanzas
String_set.mem package.name pkgs | Some pkgs ->
| _ -> true))) List.map stanzas ~f:(fun (dir, pkgs_ctx, stanzas) ->
in (dir,
let sctx = pkgs_ctx,
Super_context.create List.filter stanzas ~f:(fun stanza ->
~context match (stanza : Stanza.t) with
~aliases | Library { public = Some { package; _ }; _ }
~scopes | Alias { package = Some package ; _ }
~file_tree | Install { package; _ } ->
~packages String_set.mem package.name pkgs
~filter_out_optional_stanzas_with_missing_deps | _ -> true)))
~stanzas in
in Future.both host stanzas >>| fun (host, stanzas) ->
let module M = Gen(struct let sctx = sctx end) in let sctx =
(Super_context.rules sctx, (context.name, stanzas))) Super_context.create
?host
~context
~aliases
~scopes
~file_tree
~packages
~filter_out_optional_stanzas_with_missing_deps
~stanzas
in
let module M = Gen(struct let sctx = sctx end) in
Hashtbl.add sctxs ~key:context.name ~data:(sctx, stanzas);
(sctx, stanzas) in
List.map ~f:make_sctx contexts
|> Future.all |> Future.all
>>| fun l -> >>| fun l ->
let rules, context_names_and_stanzas = List.split l in let rules, context_names_and_stanzas =
List.map l ~f:(fun (sctx, stanzas) ->
(Super_context.rules sctx, ((Super_context.context sctx).name, stanzas)))
|> List.split
in
Alias.Store.unlink aliases unlink_aliases; Alias.Store.unlink aliases unlink_aliases;
(Alias.rules aliases @ List.concat rules, (Alias.rules aliases @ List.concat rules,
String_map.of_alist_exn context_names_and_stanzas) String_map.of_alist_exn context_names_and_stanzas)

View File

@ -47,6 +47,35 @@ module Section = struct
; "man" , Man ; "man" , Man
; "misc" , Misc ; "misc" , Misc
] ]
module Paths = struct
let lib = Path.(relative root) "lib"
let libexec = Path.(relative root) "lib"
let bin = Path.(relative root) "bin"
let sbin = Path.(relative root) "sbin"
let toplevel = Path.(relative root) "lib/toplevel"
let share = Path.(relative root) "share"
let share_root = Path.(relative root) "share_root"
let etc = Path.(relative root) "etc"
let doc = Path.(relative root) "doc"
let stublibs = Path.(relative root) "lib/stublibs"
let man = Path.(relative root) "man"
end
let install_dir t ~package =
match t with
| Bin -> Paths.bin
| Sbin -> Paths.sbin
| Toplevel -> Paths.toplevel
| Share_root -> Paths.share_root
| Stublibs -> Paths.stublibs
| Man -> Paths.man
| Lib -> Path.relative Paths.lib package
| Libexec -> Path.relative Paths.libexec package
| Share -> Path.relative Paths.share package
| Etc -> Path.relative Paths.etc package
| Doc -> Path.relative Paths.doc package
| Misc -> invalid_arg "Install.Section.install_dir"
end end
module Entry = struct module Entry = struct
@ -82,36 +111,8 @@ module Entry = struct
let set_src t src = { t with src } let set_src t src = { t with src }
module Paths = struct
let lib = Path.(relative root) "lib"
let libexec = Path.(relative root) "lib"
let bin = Path.(relative root) "bin"
let sbin = Path.(relative root) "sbin"
let toplevel = Path.(relative root) "lib/toplevel"
let share = Path.(relative root) "share"
let share_root = Path.(relative root) "share_root"
let etc = Path.(relative root) "etc"
let doc = Path.(relative root) "doc"
let stublibs = Path.(relative root) "lib/stublibs"
let man = Path.(relative root) "man"
end
let relative_installed_path t ~package = let relative_installed_path t ~package =
let main_dir = let main_dir = Section.install_dir t.section ~package in
match t.section with
| Bin -> Paths.bin
| Sbin -> Paths.sbin
| Toplevel -> Paths.toplevel
| Share_root -> Paths.share_root
| Stublibs -> Paths.stublibs
| Man -> Paths.man
| Lib -> Path.relative Paths.lib package
| Libexec -> Path.relative Paths.libexec package
| Share -> Path.relative Paths.share package
| Etc -> Path.relative Paths.etc package
| Doc -> Path.relative Paths.doc package
| Misc -> invalid_arg "Install.Entry.relative_installed_path"
in
let dst = let dst =
match t.dst with match t.dst with
| Some x -> x | Some x -> x
@ -126,6 +127,18 @@ module Entry = struct
| _ -> dst | _ -> dst
in in
Path.relative main_dir dst Path.relative main_dir dst
let add_install_prefix t ~package ~prefix =
let opam_will_install_in_this_dir =
Section.install_dir t.section ~package
in
let i_want_to_install_the_file_as =
Path.append prefix (relative_installed_path t ~package)
in
let dst =
Path.reach i_want_to_install_the_file_as ~from:opam_will_install_in_this_dir
in
{ t with dst = Some dst }
end end
module SMap = Map.Make(Section) module SMap = Map.Make(Section)

View File

@ -29,6 +29,7 @@ module Entry : sig
val set_src : t -> Path.t -> t val set_src : t -> Path.t -> t
val relative_installed_path : t -> package:string -> Path.t val relative_installed_path : t -> package:string -> Path.t
val add_install_prefix : t -> package:string -> prefix:Path.t -> t
end end
val files : Entry.t list -> Path.Set.t val files : Entry.t list -> Path.Set.t

View File

@ -104,6 +104,7 @@ end
create_plugin_wrapper context ~exec_dir:dir ~plugin:file ~wrapper create_plugin_wrapper context ~exec_dir:dir ~plugin:file ~wrapper
~target:generated_jbuild ~target:generated_jbuild
in in
let context = Option.value context.for_host ~default:context in
let pkgs = let pkgs =
List.map requires ~f:(Findlib.find_exn context.findlib List.map requires ~f:(Findlib.find_exn context.findlib
~required_by:[Utils.jbuild_name_in ~dir:dir]) ~required_by:[Utils.jbuild_name_in ~dir:dir])

View File

@ -12,7 +12,8 @@ type setup =
let package_install_file { packages; _ } pkg = let package_install_file { packages; _ } pkg =
match String_map.find pkg packages with match String_map.find pkg packages with
| None -> Error () | 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 let setup ?(log=Log.no_log) ?unlink_aliases
?filter_out_optional_stanzas_with_missing_deps ?filter_out_optional_stanzas_with_missing_deps
@ -20,6 +21,7 @@ let setup ?(log=Log.no_log) ?unlink_aliases
?(use_findlib=true) ?(use_findlib=true)
?only_packages ?only_packages
?extra_ignored_subtrees ?extra_ignored_subtrees
?x
() = () =
let conf = Jbuild_load.load ?extra_ignored_subtrees () in let conf = Jbuild_load.load ?extra_ignored_subtrees () in
Option.iter only_packages ~f:(fun set -> Option.iter only_packages ~f:(fun set ->
@ -33,26 +35,34 @@ let setup ?(log=Log.no_log) ?unlink_aliases
| Some w -> w | Some w -> w
| None -> | None ->
if Sys.file_exists workspace_file then if Sys.file_exists workspace_file then
Workspace.load workspace_file Workspace.load ?x workspace_file
else else
{ merlin_context = Some "default"; contexts = [Default] } { merlin_context = Some "default"
; contexts = [Default [
match x with
| None -> Native
| Some x -> Named x
]]
}
in in
Future.all
(List.map workspace.contexts ~f:(function Future.all (
| Workspace.Context.Default -> List.map workspace.contexts ~f:(fun ctx_def ->
Context.default ~merlin:(workspace.merlin_context = Some "default") let name = Workspace.Context.name ctx_def in
~use_findlib () Context.create ctx_def ~merlin:(workspace.merlin_context = Some name) ~use_findlib)
| Opam { name; switch; root; merlin } -> )
Context.create_for_opam ~name ~switch ?root ~merlin ()))
>>= fun contexts -> >>= 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)); 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 ?unlink_aliases
?only_packages ?only_packages
?filter_out_optional_stanzas_with_missing_deps ?filter_out_optional_stanzas_with_missing_deps
>>= fun (rules, stanzas) -> >>= 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 return { build_system
; stanzas ; stanzas
; contexts ; contexts
@ -211,7 +221,7 @@ let bootstrap () =
Clflags.debug_dep_path := true; Clflags.debug_dep_path := true;
let log = Log.create () in let log = Log.create () in
Future.Scheduler.go ~log 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 ~use_findlib:false
~extra_ignored_subtrees:ignored_during_bootstrap ~extra_ignored_subtrees:ignored_during_bootstrap
() ()

View File

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

View File

@ -381,7 +381,9 @@ let drop_build_context t =
let exists t = Sys.file_exists (to_string t) let exists t = Sys.file_exists (to_string t)
let readdir t = Sys.readdir (to_string t) |> Array.to_list let readdir t = Sys.readdir (to_string t) |> Array.to_list
let is_directory t = Sys.is_directory (to_string t) let is_directory t =
try Sys.is_directory (to_string t)
with Sys_error _ -> false
let rmdir t = Unix.rmdir (to_string t) let rmdir t = Unix.rmdir (to_string t)
let unlink t = Unix.unlink (to_string t) let unlink t = Unix.unlink (to_string t)
let unlink_no_err t = try Unix.unlink (to_string t) with _ -> () let unlink_no_err t = try Unix.unlink (to_string t) with _ -> ()
@ -429,3 +431,13 @@ let change_extension ~ext t =
let extension = Filename.extension let extension = Filename.extension
let pp = Format.pp_print_string let pp = Format.pp_print_string
let drop_prefix t ~prefix =
let t = to_string t in
let prefix =
to_string (
if String.is_suffix prefix ~suffix:"/" then
prefix
else
prefix ^ "/") in
String.drop_prefix t ~prefix

View File

@ -114,4 +114,12 @@ val change_extension : ext:string -> t -> t
val extension : t -> string val extension : t -> string
(** maintains the invariant:
{[
let suffix = Option.value_exn (Path.drop_prefix t ~prefix) in
Path.relative prefix suffix = t
]}
*)
val drop_prefix : t -> prefix:t -> string option
val pp : t Fmt.t val pp : t Fmt.t

View File

@ -343,20 +343,35 @@ module Of_sexp = struct
let ( @> ) a b = Constructor_args_spec.Cons (a, b) let ( @> ) a b = Constructor_args_spec.Cons (a, b)
module Constructor_spec = struct module Constructor_spec = struct
type ('a, 'b, 'c) unpacked = type ('a, 'b, 'c) tuple =
{ name : string { name : string
; args : ('a, 'b) Constructor_args_spec.t ; args : ('a, 'b) Constructor_args_spec.t
; rest : ('b, 'c) rest ; rest : ('b, 'c) rest
; make : Loc.t -> 'a ; make : Loc.t -> 'a
} }
type 'a t = T : (_, _, 'a) unpacked -> 'a t type 'a record =
{ name : string
; parse : 'a record_parser
}
type 'a t =
| Tuple : (_, _, 'a) tuple -> 'a t
| Record : 'a record -> 'a t
let name = function
| Tuple x -> x.name
| Record x -> x.name
end end
module C = Constructor_spec
let cstr_loc name args make = let cstr_loc name args make =
Constructor_spec.T { name; args; make; rest = No_rest } C.Tuple { name; args; make; rest = No_rest }
let cstr_rest_loc name args rest make = let cstr_rest_loc name args rest make =
Constructor_spec.T { name; args; make; rest = Many rest } C.Tuple { name; args; make; rest = Many rest }
let cstr_record name parse =
C.Record { name; parse }
let cstr name args make = let cstr name args make =
cstr_loc name args (fun _ -> make) cstr_loc name args (fun _ -> make)
@ -368,8 +383,8 @@ module Of_sexp = struct
let find_cstr cstrs sexp name = let find_cstr cstrs sexp name =
match match
List.find cstrs ~f:(fun (Constructor_spec.T cstr) -> List.find cstrs ~f:(fun cstr ->
equal_cstr_name cstr.name name) equal_cstr_name (C.name cstr) name)
with with
| Some cstr -> cstr | Some cstr -> cstr
| None -> | None ->
@ -377,22 +392,24 @@ module Of_sexp = struct
"Unknown constructor %s%s" name "Unknown constructor %s%s" name
(hint (hint
(String.uncapitalize_ascii name) (String.uncapitalize_ascii name)
(List.map cstrs ~f:(fun (Constructor_spec.T c) -> (List.map cstrs ~f:(fun c ->
String.uncapitalize_ascii c.name))) String.uncapitalize_ascii (C.name c))))
let sum cstrs sexp = let sum cstrs sexp =
match sexp with match sexp with
| Atom (loc, s) -> begin | Atom (loc, s) -> begin
let (Constructor_spec.T c) = find_cstr cstrs sexp s in match find_cstr cstrs sexp s with
Constructor_args_spec.convert c.args c.rest sexp [] (c.make loc) | C.Tuple t -> Constructor_args_spec.convert t.args t.rest sexp [] (t.make loc)
| C.Record _ -> of_sexp_error sexp "'%s' expect arguments"
end end
| List (_, []) -> of_sexp_error sexp "non-empty list expected" | List (_, []) -> of_sexp_error sexp "non-empty list expected"
| List (loc, name_sexp :: args) -> | List (loc, name_sexp :: args) ->
match name_sexp with match name_sexp with
| List _ -> of_sexp_error name_sexp "Atom expected" | List _ -> of_sexp_error name_sexp "Atom expected"
| Atom (_, s) -> | Atom (_, s) ->
let (Constructor_spec.T c) = find_cstr cstrs sexp s in match find_cstr cstrs sexp s with
Constructor_args_spec.convert c.args c.rest sexp args (c.make loc) | C.Tuple t -> Constructor_args_spec.convert t.args t.rest sexp args (t.make loc)
| C.Record r -> record r.parse (List (loc, args))
let enum cstrs sexp = let enum cstrs sexp =
match sexp with match sexp with

View File

@ -85,6 +85,8 @@ module Of_sexp : sig
-> 'a -> 'a
-> 'c Constructor_spec.t -> 'c Constructor_spec.t
val cstr_record : string -> 'a record_parser -> 'a Constructor_spec.t
val cstr_loc val cstr_loc
: string : string
-> ('a, 'b) Constructor_args_spec.t -> ('a, 'b) Constructor_args_spec.t

View File

@ -57,6 +57,7 @@ type t =
; ppx_drivers : (string, Path.t) Hashtbl.t ; ppx_drivers : (string, Path.t) Hashtbl.t
; external_dirs : (Path.t, External_dir.t) Hashtbl.t ; external_dirs : (Path.t, External_dir.t) Hashtbl.t
; chdir : (Action.t, Action.t) Build.t ; chdir : (Action.t, Action.t) Build.t
; host : t option
} }
let context t = t.context let context t = t.context
@ -69,6 +70,8 @@ let rules t = t.rules
let stanzas_to_consider_for_install t = t.stanzas_to_consider_for_install let stanzas_to_consider_for_install t = t.stanzas_to_consider_for_install
let cxx_flags t = t.cxx_flags let cxx_flags t = t.cxx_flags
let host_sctx t = Option.value t.host ~default:t
let expand_var_no_root t var = String_map.find var t.vars let expand_var_no_root t var = String_map.find var t.vars
let get_external_dir t ~dir = let get_external_dir t ~dir =
@ -87,6 +90,7 @@ let resolve_program t ?hint bin =
let create let create
~(context:Context.t) ~(context:Context.t)
?host
~aliases ~aliases
~scopes ~scopes
~file_tree ~file_tree
@ -186,6 +190,7 @@ let create
| Error _ -> assert false | Error _ -> assert false
in in
{ context { context
; host
; libs ; libs
; stanzas ; stanzas
; packages ; packages
@ -505,7 +510,17 @@ module Action = struct
acc.sdeps <- Pset.add path acc.sdeps; acc.sdeps <- Pset.add path acc.sdeps;
Some (path_exp path) Some (path_exp path)
let expand_step1 sctx ~dir ~dep_kind ~scope ~targets_written_by_user t = let map_exe sctx =
match sctx.host with
| None -> (fun exe -> exe)
| Some host ->
fun exe ->
match Path.extract_build_context_dir exe with
| Some (dir, exe) when dir = sctx.context.build_dir ->
Path.append host.context.build_dir exe
| _ -> exe
let expand_step1 sctx ~dir ~dep_kind ~scope ~targets_written_by_user ~map_exe t =
let acc = let acc =
{ failures = [] { failures = []
; lib_deps = String_map.empty ; lib_deps = String_map.empty
@ -514,14 +529,17 @@ module Action = struct
} }
in in
let t = let t =
U.partial_expand dir t ~f:(fun loc key -> U.partial_expand t ~dir ~map_exe ~f:(fun loc key ->
let open Action.Var_expansion in let open Action.Var_expansion in
let cos, var = parse_bang key in let cos, var = parse_bang key in
match String.lsplit2 var ~on:':' with match String.lsplit2 var ~on:':' with
| Some ("path-no-dep", s) -> Some (path_exp (Path.relative dir s)) | Some ("path-no-dep", s) -> Some (path_exp (Path.relative dir s))
| Some ("exe" , s) -> static_dep_exp acc (Path.relative dir s) | Some ("exe" , s) ->
let exe = map_exe (Path.relative dir s) in
static_dep_exp acc exe
| Some ("path" , s) -> static_dep_exp acc (Path.relative dir s) | Some ("path" , s) -> static_dep_exp acc (Path.relative dir s)
| Some ("bin" , s) -> begin | Some ("bin" , s) -> begin
let sctx = host_sctx sctx in
match Artifacts.binary (artifacts sctx) s with match Artifacts.binary (artifacts sctx) s with
| Ok path -> | Ok path ->
static_dep_exp acc path static_dep_exp acc path
@ -539,6 +557,7 @@ module Action = struct
| Error fail -> add_fail acc fail | Error fail -> add_fail acc fail
end end
| Some ("libexec" , s) -> begin | Some ("libexec" , s) -> begin
let sctx = host_sctx sctx in
let lib_dep, res = let lib_dep, res =
Artifacts.file_of_lib (artifacts sctx) ~loc ~from:dir s in Artifacts.file_of_lib (artifacts sctx) ~loc ~from:dir s in
add_lib_dep acc lib_dep dep_kind; add_lib_dep acc lib_dep dep_kind;
@ -612,9 +631,9 @@ module Action = struct
in in
(t, acc) (t, acc)
let expand_step2 ~dir ~dynamic_expansions ~deps_written_by_user t = let expand_step2 ~dir ~dynamic_expansions ~deps_written_by_user ~map_exe t =
let open Action.Var_expansion in let open Action.Var_expansion in
U.Partial.expand dir t ~f:(fun _loc key -> U.Partial.expand t ~dir ~map_exe ~f:(fun _loc key ->
match String_map.find key dynamic_expansions with match String_map.find key dynamic_expansions with
| Some _ as opt -> opt | Some _ as opt -> opt
| None -> | None ->
@ -633,9 +652,10 @@ module Action = struct
let run sctx t ~dir ~dep_kind ~targets:targets_written_by_user ~scope let run sctx t ~dir ~dep_kind ~targets:targets_written_by_user ~scope
: (Path.t list, Action.t) Build.t = : (Path.t list, Action.t) Build.t =
let map_exe = map_exe sctx in
let t, forms = let t, forms =
expand_step1 sctx t ~dir ~dep_kind ~scope expand_step1 sctx t ~dir ~dep_kind ~scope
~targets_written_by_user ~targets_written_by_user ~map_exe
in in
let { Action.Infer.Outcome. deps; targets } = let { Action.Infer.Outcome. deps; targets } =
match targets_written_by_user with match targets_written_by_user with
@ -689,9 +709,10 @@ module Action = struct
String_map.add acc ~key:var ~data:value) String_map.add acc ~key:var ~data:value)
in in
let unresolved = let unresolved =
expand_step2 t ~dir ~dynamic_expansions ~deps_written_by_user expand_step2 t ~dir ~dynamic_expansions ~deps_written_by_user ~map_exe
in in
Action.Unresolved.resolve unresolved ~f:(fun prog -> Action.Unresolved.resolve unresolved ~f:(fun prog ->
let sctx = host_sctx sctx in
match Artifacts.binary sctx.artifacts prog with match Artifacts.binary sctx.artifacts prog with
| Ok path -> path | Ok path -> path
| Error fail -> Action.Prog.Not_found.raise fail)) | Error fail -> Action.Prog.Not_found.raise fail))
@ -818,6 +839,7 @@ module PP = struct
| [] -> "+none+" | [] -> "+none+"
| _ -> String.concat names ~sep:"+" | _ -> String.concat names ~sep:"+"
in in
let sctx = host_sctx sctx in
match Hashtbl.find sctx.ppx_drivers key with match Hashtbl.find sctx.ppx_drivers key with
| Some x -> x | Some x -> x
| None -> | None ->

View File

@ -22,6 +22,7 @@ type t
val create val create
: context:Context.t : context:Context.t
-> ?host:t
-> aliases:Alias.Store.t -> aliases:Alias.Store.t
-> scopes:Scope.t list -> scopes:Scope.t list
-> file_tree:File_tree.t -> file_tree:File_tree.t

View File

@ -133,6 +133,11 @@ let obj_name_of_basename fn =
| None -> fn | None -> fn
| Some i -> String.sub fn ~pos:0 ~len:i | Some i -> String.sub fn ~pos:0 ~len:i
let install_file ~package ~findlib_toolchain =
match findlib_toolchain with
| None -> package ^ ".install"
| Some x -> sprintf "%s-%s.install" package x
module Cached_digest = struct module Cached_digest = struct
type file = type file =
{ mutable digest : Digest.t { mutable digest : Digest.t

View File

@ -43,6 +43,8 @@ val find_deps : dir:Path.t -> 'a String_map.t -> string -> 'a
*) *)
val obj_name_of_basename : string -> string val obj_name_of_basename : string -> string
val install_file : package:string -> findlib_toolchain:string option -> string
(** Digest files with caching *) (** Digest files with caching *)
module Cached_digest : sig module Cached_digest : sig
(** Digest the contents of the following file *) (** Digest the contents of the following file *)

View File

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

View File

@ -162,3 +162,10 @@
(action (action
(chdir test-cases/c-stubs (chdir test-cases/c-stubs
(setenv JBUILDER ${bin:jbuilder} (run ${exe:cram.exe} run.t)))))) (setenv JBUILDER ${bin:jbuilder} (run ${exe:cram.exe} run.t))))))
(alias
((name runtest)
(deps ((files_recursively_in test-cases/cross-compilation)))
(action
(chdir test-cases/cross-compilation
(setenv JBUILDER ${bin:jbuilder} (run ${exe:cram.exe} run.t))))))

View File

@ -0,0 +1 @@
let () = Printf.printf "%d\n" P.x

View File

@ -0,0 +1,6 @@
(jbuild_version 1)
(executable
((name blah)
(public_name blah)
(libraries (p))))

View File

@ -0,0 +1,3 @@
(jbuild_version 1)
(rule (with-stdout-to file (run ./bin/blah.exe)))

View File

@ -0,0 +1,5 @@
(jbuild_version 1)
(library
((name p)
(public_name p)))

View File

@ -0,0 +1 @@
let x = 42

View File

@ -0,0 +1,41 @@
$ env OCAMLFIND_CONF=$PWD/etc/findlib.conf $JBUILDER build --root . -j1 -x foo file @install
ocamldep bin/blah.depends.ocamldep-output [default.foo]
ocamldep lib/p.depends.ocamldep-output [default.foo]
ocamldep bin/blah.depends.ocamldep-output
ocamlc lib/p.{cmi,cmo,cmt} [default.foo]
ocamldep lib/p.depends.ocamldep-output
ocamlopt lib/p.{cmx,o} [default.foo]
ocamlc bin/blah.{cmi,cmo,cmt} [default.foo]
ocamlc lib/p.cma [default.foo]
ocamlc lib/p.{cmi,cmo,cmt}
ocamlopt lib/p.{a,cmxa} [default.foo]
ocamlopt bin/blah.{cmx,o} [default.foo]
ocamlc bin/blah.{cmi,cmo,cmt}
ocamlopt lib/p.{cmx,o}
ocamlopt lib/p.cmxs [default.foo]
ocamlopt bin/blah.exe [default.foo]
ocamlopt bin/blah.{cmx,o}
ocamlopt lib/p.{a,cmxa}
ocamlopt bin/blah.exe
blah file [default.foo]
blah file
$ cat _build/default.foo/file
42
$ ls *.install
p-foo.install
$ cat p-foo.install
lib: [
"_build/install/default.foo/lib/p/META" {"../../foo-sysroot/lib/p/META"}
"_build/install/default.foo/lib/p/opam" {"../../foo-sysroot/lib/p/opam"}
"_build/install/default.foo/lib/p/p.cmi" {"../../foo-sysroot/lib/p/p.cmi"}
"_build/install/default.foo/lib/p/p.cmx" {"../../foo-sysroot/lib/p/p.cmx"}
"_build/install/default.foo/lib/p/p.cmt" {"../../foo-sysroot/lib/p/p.cmt"}
"_build/install/default.foo/lib/p/p.ml" {"../../foo-sysroot/lib/p/p.ml"}
"_build/install/default.foo/lib/p/p.cma" {"../../foo-sysroot/lib/p/p.cma"}
"_build/install/default.foo/lib/p/p.cmxa" {"../../foo-sysroot/lib/p/p.cmxa"}
"_build/install/default.foo/lib/p/p.a" {"../../foo-sysroot/lib/p/p.a"}
"_build/install/default.foo/lib/p/p.cmxs" {"../../foo-sysroot/lib/p/p.cmxs"}
]
bin: [
"_build/install/default.foo/bin/blah" {"../foo-sysroot/bin/blah"}
]