Remove (provides ...) and uses (install ...) instead
This commit is contained in:
parent
60be8f7734
commit
6e25a7dfe5
|
@ -513,44 +513,6 @@ The typical use of the =alias= stanza is to define tests:
|
||||||
|
|
||||||
See the [[runtest][section about running tests]] for details.
|
See the [[runtest][section about running tests]] for details.
|
||||||
|
|
||||||
**** provides
|
|
||||||
|
|
||||||
The =provides= stanza allows you to globally name a file, either a
|
|
||||||
source file or a target. This is especially important for build tools;
|
|
||||||
by using the =provides= mechanism, you don't need to know whether the
|
|
||||||
binary is in the workspace or installed.
|
|
||||||
|
|
||||||
The syntax is as follow:
|
|
||||||
|
|
||||||
#+begin_src scheme
|
|
||||||
(provides (<name> (file <filename>)))
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
=<name>= is the name under which the artifact can be referred and
|
|
||||||
=<filename>= is the file it resolves to. When =<filename>= can be
|
|
||||||
guessed from the =<name>=, you can use the following shorter syntax:
|
|
||||||
|
|
||||||
#+begin_src scheme
|
|
||||||
(provides <name>)
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
In this case, the file name is guessed as follow:
|
|
||||||
|
|
||||||
- if =<name>= contains a =:=, the file name is anything that comes
|
|
||||||
after the first =:=
|
|
||||||
- otherwise it is the same as =<name>=
|
|
||||||
|
|
||||||
Once you have written a =provides= stanza, you can refer to the file
|
|
||||||
in points to using the special forms =${bin:<name>}= or
|
|
||||||
=${findlib:<library>:<file>}= inside =(action ...)= fields. See the
|
|
||||||
[[Variables expansion][section about variables expansion]] for details.
|
|
||||||
|
|
||||||
Note that any file referred by a =provides= stanza should probably be
|
|
||||||
installed as well, using an [[install]] stanza. If the file is meant to be
|
|
||||||
installed in a library directory, then its name should be of the form
|
|
||||||
=<public-library-name>:<file>=. If it is meant to be installed in the
|
|
||||||
=bin= directory, then its name should be the program name.
|
|
||||||
|
|
||||||
**** install
|
**** install
|
||||||
|
|
||||||
The =install= stanza is what lets you describe what Jbuilder should
|
The =install= stanza is what lets you describe what Jbuilder should
|
||||||
|
@ -650,20 +612,24 @@ In addition, =(action ...)= fields support the following special variables:
|
||||||
- =@= expands to the list of target, separated by spaces
|
- =@= expands to the list of target, separated by spaces
|
||||||
- =<= expands to the first dependency, or the empty string if there are no dependencies
|
- =<= expands to the first dependency, or the empty string if there are no dependencies
|
||||||
- =^= expands to the list of dependencies, separated by spaces
|
- =^= expands to the list of dependencies, separated by spaces
|
||||||
- =exe:<path>= expands to =<path>=, except when cross-compiling, in
|
- =path:<path>= expands to =<path>=
|
||||||
|
- =exe:<path>= is the same as =<path>=, except when cross-compiling, in
|
||||||
which case it will expand to =<path>= from the host build context
|
which case it will expand to =<path>= from the host build context
|
||||||
- =bin:<program>= expands to a path to =program=. If =program= is
|
- =bin:<program>= expands to a path to =program=. If =program= is
|
||||||
provided by a jbuild in the workspace (see [[provides][provides stanzas]]), the
|
installed by a package in the workspace (see [[install][install stanzas]]), the
|
||||||
locally built binary will be used, otherwise it will be searched in
|
locally built binary will be used, otherwise it will be searched in
|
||||||
the =PATH= of the current build context
|
the =PATH= of the current build context
|
||||||
- =findlib:<public-library-name>:<file>= expands to a path to file
|
- =lib:<public-library-name>:<file>= expands to a path to file
|
||||||
=<file>= of library =<public-library-name>=. If
|
=<file>= of library =<public-library-name>=. If
|
||||||
=<public-library-name>= is available in the current workspace, the
|
=<public-library-name>= is available in the current workspace, the
|
||||||
local file will be used, otherwise the one from the installed world
|
local file will be used, otherwise the one from the installed world
|
||||||
will be used
|
will be used
|
||||||
|
- =libexec:<public-library-name>:<file>= is the same as =lib:...=
|
||||||
|
except when cross-compiling, in which case it will expand to the
|
||||||
|
file from the host build context
|
||||||
|
|
||||||
The last two forms of variable are what allows you to write custom
|
The =${<kind>:...}= forms are what allows you to write custom rules
|
||||||
rules that work transparently whether things are installed or not.
|
that work transparently whether things are installed or not.
|
||||||
|
|
||||||
***** Alternative dependencies
|
***** Alternative dependencies
|
||||||
|
|
||||||
|
@ -832,9 +798,8 @@ of these two forms:
|
||||||
In both case, all atoms in the argument of this field supports
|
In both case, all atoms in the argument of this field supports
|
||||||
[[Variables
|
[[Variables
|
||||||
expansion][variables expansion]]. Moreover, you don't need to specify dependencies
|
expansion][variables expansion]]. Moreover, you don't need to specify dependencies
|
||||||
explicitly for the special =${exe:...}=, =${bin:...}= or
|
explicitly for the special =${<kind>:...}= forms, these are recognized
|
||||||
=${findlib:...}= forms, these are recognized automatically by
|
automatically handled by Jbuilder.
|
||||||
Jbuilder.
|
|
||||||
|
|
||||||
The DSL is preferable in general as it will make your package more
|
The DSL is preferable in general as it will make your package more
|
||||||
portable. It is currently quite limited, so the recommendation is to
|
portable. It is currently quite limited, so the recommendation is to
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
open Import
|
||||||
|
open Jbuild_types
|
||||||
|
|
||||||
|
type t =
|
||||||
|
{ context : Context.t
|
||||||
|
; provides : Path.t String_map.t
|
||||||
|
; local_bins : String_set.t
|
||||||
|
; local_libs : String_set.t
|
||||||
|
}
|
||||||
|
|
||||||
|
let create context stanzas =
|
||||||
|
let local_bins, local_libs, provides =
|
||||||
|
List.fold_left stanzas ~init:(String_set.empty, String_set.empty, String_map.empty)
|
||||||
|
~f:(fun acc (dir, stanzas) ->
|
||||||
|
List.fold_left stanzas ~init:acc
|
||||||
|
~f:(fun (local_bins, local_libs, provides) stanza ->
|
||||||
|
match (stanza : Stanza.t) with
|
||||||
|
| Provides { name; file } ->
|
||||||
|
(local_bins,
|
||||||
|
local_libs,
|
||||||
|
String_map.add provides ~key:name ~data:(Path.relative dir file))
|
||||||
|
| Install { section = Bin; files; _ } ->
|
||||||
|
(List.fold_left files ~init:local_bins ~f:(fun acc { Install_conf. src; dst } ->
|
||||||
|
let name =
|
||||||
|
match dst with
|
||||||
|
| Some s -> s
|
||||||
|
| None -> Filename.basename src
|
||||||
|
in
|
||||||
|
String_set.add name acc),
|
||||||
|
local_libs,
|
||||||
|
provides)
|
||||||
|
| Library { public_name = Some name; _ } ->
|
||||||
|
(local_bins,
|
||||||
|
String_set.add name local_libs,
|
||||||
|
provides)
|
||||||
|
| _ ->
|
||||||
|
(local_bins, local_libs, provides)))
|
||||||
|
in
|
||||||
|
{ context
|
||||||
|
; provides
|
||||||
|
; local_bins
|
||||||
|
; local_libs
|
||||||
|
}
|
||||||
|
|
||||||
|
let binary t name =
|
||||||
|
if String_set.mem name t.local_bins then
|
||||||
|
Ok (Path.relative (Config.local_install_dir ~context:t.context.name) name)
|
||||||
|
else
|
||||||
|
match String_map.find name t.provides with
|
||||||
|
| Some p -> Ok p
|
||||||
|
| None ->
|
||||||
|
match Context.which t.context name with
|
||||||
|
| Some p -> Ok p
|
||||||
|
| None ->
|
||||||
|
Error
|
||||||
|
{ fail = fun () ->
|
||||||
|
die "Program %s not found in the tree or in the PATH" name
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_of_lib ?(use_provides=false) t ~lib ~file =
|
||||||
|
if String_set.mem lib t.local_libs then
|
||||||
|
let lib_install_dir =
|
||||||
|
Path.relative (Config.local_install_dir ~context:t.context.name)
|
||||||
|
(Findlib.root_package_name lib)
|
||||||
|
in
|
||||||
|
Ok (Path.relative lib_install_dir file)
|
||||||
|
else
|
||||||
|
match Findlib.find t.context.findlib lib with
|
||||||
|
| Some pkg ->
|
||||||
|
Ok (Path.relative pkg.dir file)
|
||||||
|
| None ->
|
||||||
|
match
|
||||||
|
if use_provides then
|
||||||
|
String_map.find (sprintf "%s:%s" lib file) t.provides
|
||||||
|
else
|
||||||
|
None
|
||||||
|
with
|
||||||
|
| Some p -> Ok p
|
||||||
|
| None ->
|
||||||
|
Error
|
||||||
|
{ fail = fun () ->
|
||||||
|
die "Library %s not found in the tree or in the PATH" lib
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
(** [Named_artifact] provides a way to reference artifacts in jbuild rules without having
|
||||||
|
to hardcode their exact locations. These named artifacts will be looked up
|
||||||
|
appropriately (in the tree, or for the public release, possibly in the PATH or in
|
||||||
|
findlib). *)
|
||||||
|
|
||||||
|
open! Import
|
||||||
|
|
||||||
|
type t
|
||||||
|
|
||||||
|
val create : Context.t -> (Path.t * Jbuild_types.Stanza.t list) list -> t
|
||||||
|
|
||||||
|
(** A named artifact that is looked up in the PATH if not found in the tree *)
|
||||||
|
val binary : t -> string -> (Path.t, fail) result
|
||||||
|
|
||||||
|
(** A named artifact that is looked up in the given library. *)
|
||||||
|
val file_of_lib
|
||||||
|
: ?use_provides:bool
|
||||||
|
-> t
|
||||||
|
-> lib:string
|
||||||
|
-> file:string
|
||||||
|
-> (Path.t, fail) result
|
12
src/build.ml
12
src/build.ml
|
@ -270,8 +270,8 @@ module Shexp = struct
|
||||||
exec t ~dir ~env ~env_extra:String_map.empty ~stdout_to:None ~tail:true ~f
|
exec t ~dir ~env ~env_extra:String_map.empty ~stdout_to:None ~tail:true ~f
|
||||||
end
|
end
|
||||||
|
|
||||||
let action action ~dir ~env ~targets =
|
let action action ~dir ~env ~targets ~expand:f =
|
||||||
prim ~targets (fun f ->
|
prim ~targets (fun () ->
|
||||||
match (action : _ Action.t) with
|
match (action : _ Action.t) with
|
||||||
| Bash cmd ->
|
| Bash cmd ->
|
||||||
Future.run Strict ~dir:(Path.to_string dir) ~env
|
Future.run Strict ~dir:(Path.to_string dir) ~env
|
||||||
|
@ -288,6 +288,14 @@ let copy ~src ~dst =
|
||||||
create_file ~target:dst (fun () ->
|
create_file ~target:dst (fun () ->
|
||||||
copy_file ~src:(Path.to_string src) ~dst:(Path.to_string dst))
|
copy_file ~src:(Path.to_string src) ~dst:(Path.to_string dst))
|
||||||
|
|
||||||
|
let symlink ~src ~dst =
|
||||||
|
if Sys.win32 then
|
||||||
|
copy ~src ~dst
|
||||||
|
else
|
||||||
|
path src >>>
|
||||||
|
create_file ~target:dst (fun () ->
|
||||||
|
Unix.symlink (Path.to_string src) (Path.to_string dst))
|
||||||
|
|
||||||
let touch target =
|
let touch target =
|
||||||
create_file ~target (fun _ ->
|
create_file ~target (fun _ ->
|
||||||
Unix.close
|
Unix.close
|
||||||
|
|
|
@ -83,13 +83,16 @@ val action
|
||||||
-> dir:Path.t
|
-> dir:Path.t
|
||||||
-> env:string array
|
-> env:string array
|
||||||
-> targets:Path.t list
|
-> targets:Path.t list
|
||||||
-> (dir:Path.t -> 'a -> string, unit) t
|
-> expand:(dir:Path.t -> 'a -> string)
|
||||||
|
-> (unit, unit) t
|
||||||
|
|
||||||
(** Create a file with the given contents. *)
|
(** Create a file with the given contents. *)
|
||||||
val echo : Path.t -> (string, unit) t
|
val echo : Path.t -> (string, unit) t
|
||||||
|
|
||||||
val copy : src:Path.t -> dst:Path.t -> (unit, unit) t
|
val copy : src:Path.t -> dst:Path.t -> (unit, unit) t
|
||||||
|
|
||||||
|
val symlink : src:Path.t -> dst:Path.t -> (unit, unit) t
|
||||||
|
|
||||||
val touch : Path.t -> (unit, unit) t
|
val touch : Path.t -> (unit, unit) t
|
||||||
|
|
||||||
type lib_dep_kind =
|
type lib_dep_kind =
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
open! Import
|
||||||
|
|
||||||
|
let local_install_dir =
|
||||||
|
let dir = Path.(relative root) "install" in
|
||||||
|
fun ~context -> Path.relative dir context
|
|
@ -0,0 +1,6 @@
|
||||||
|
(** Configuration parameters *)
|
||||||
|
|
||||||
|
open! Import
|
||||||
|
|
||||||
|
(** Local installation directory *)
|
||||||
|
val local_install_dir : context:string -> Path.t
|
|
@ -63,6 +63,7 @@ type t =
|
||||||
; ast_intf_magic_number : string
|
; ast_intf_magic_number : string
|
||||||
; cmxs_magic_number : string
|
; cmxs_magic_number : string
|
||||||
; cmt_magic_number : string
|
; cmt_magic_number : string
|
||||||
|
; which_cache : (string, Path.t option) Hashtbl.t
|
||||||
}
|
}
|
||||||
|
|
||||||
let compare a b = compare a.name b.name
|
let compare a b = compare a.name b.name
|
||||||
|
@ -102,6 +103,9 @@ let get_env env var =
|
||||||
in
|
in
|
||||||
loop 0
|
loop 0
|
||||||
|
|
||||||
|
let which ~cache ~path x =
|
||||||
|
Hashtbl.find_or_add cache x ~f:(Bin.which ~path)
|
||||||
|
|
||||||
let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
||||||
let opam_var_cache = Hashtbl.create 128 in
|
let opam_var_cache = Hashtbl.create 128 in
|
||||||
(match kind with
|
(match kind with
|
||||||
|
@ -111,7 +115,8 @@ let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
||||||
let prog_not_found_in_path prog =
|
let prog_not_found_in_path prog =
|
||||||
die "Program %s not found in PATH (context: %s)" prog name
|
die "Program %s not found in PATH (context: %s)" prog name
|
||||||
in
|
in
|
||||||
let which x = Bin.which ~path x in
|
let which_cache = Hashtbl.create 128 in
|
||||||
|
let which x = which ~cache:which_cache ~path x in
|
||||||
let ocamlc =
|
let ocamlc =
|
||||||
match which "ocamlc" with
|
match which "ocamlc" with
|
||||||
| None -> prog_not_found_in_path "ocamlc"
|
| None -> prog_not_found_in_path "ocamlc"
|
||||||
|
@ -248,6 +253,8 @@ let create ~(kind : Kind.t) ~path ~env ~name ~merlin =
|
||||||
; ast_intf_magic_number = get "ast_intf_magic_number"
|
; ast_intf_magic_number = get "ast_intf_magic_number"
|
||||||
; cmxs_magic_number = get "cmxs_magic_number"
|
; cmxs_magic_number = get "cmxs_magic_number"
|
||||||
; cmt_magic_number = get "cmt_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
|
||||||
|
@ -310,7 +317,7 @@ let create_for_opam ?root ~switch ~name ?(merlin=false) () =
|
||||||
create ~kind:(Opam { root; switch }) ~path ~env:(extend_env ~vars ~env)
|
create ~kind:(Opam { root; switch }) ~path ~env:(extend_env ~vars ~env)
|
||||||
~name ~merlin
|
~name ~merlin
|
||||||
|
|
||||||
let which t s = Bin.which ~path:t.path s
|
let which t s = which ~cache:t.which_cache ~path:t.path s
|
||||||
|
|
||||||
let install_prefix t =
|
let install_prefix t =
|
||||||
opam_config_var t "prefix" >>| function
|
opam_config_var t "prefix" >>| function
|
||||||
|
|
|
@ -103,6 +103,8 @@ type t =
|
||||||
; ast_intf_magic_number : string
|
; ast_intf_magic_number : string
|
||||||
; cmxs_magic_number : string
|
; cmxs_magic_number : string
|
||||||
; cmt_magic_number : string
|
; cmt_magic_number : string
|
||||||
|
|
||||||
|
; which_cache : (string, Path.t option) Hashtbl.t
|
||||||
}
|
}
|
||||||
|
|
||||||
(** Compare the context names *)
|
(** Compare the context names *)
|
||||||
|
|
|
@ -392,6 +392,11 @@ let find_exn t name =
|
||||||
Hashtbl.add t.packages ~key:name ~data:Absent;
|
Hashtbl.add t.packages ~key:name ~data:Absent;
|
||||||
raise (Package_not_found name)
|
raise (Package_not_found name)
|
||||||
|
|
||||||
|
let find t name =
|
||||||
|
match find_exn t name with
|
||||||
|
| exception (Package_not_found _) -> None
|
||||||
|
| x -> Some x
|
||||||
|
|
||||||
let available t name =
|
let available t name =
|
||||||
match find_exn t name with
|
match find_exn t name with
|
||||||
| _ -> true
|
| _ -> true
|
||||||
|
|
|
@ -22,6 +22,7 @@ type package =
|
||||||
; has_headers : bool
|
; has_headers : bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val find : t -> string -> package option
|
||||||
val find_exn : t -> string -> package
|
val find_exn : t -> string -> package
|
||||||
|
|
||||||
val available : t -> string -> bool
|
val available : t -> string -> bool
|
||||||
|
|
160
src/gen_rules.ml
160
src/gen_rules.ml
|
@ -240,21 +240,21 @@ module Gen(P : Params) = struct
|
||||||
let _ = t
|
let _ = t
|
||||||
end
|
end
|
||||||
|
|
||||||
module Named_artifacts = struct
|
module Artifacts = struct
|
||||||
open Named_artifacts
|
open Artifacts
|
||||||
|
|
||||||
let t = create ~path:ctx.path findlib (List.map P.stanzas ~f:(fun d -> (d.ctx_dir, d.stanzas)))
|
let t = create ctx (List.map P.stanzas ~f:(fun d -> (d.ctx_dir, d.stanzas)))
|
||||||
|
|
||||||
let binary name = Build.arr (fun _ -> binary t name)
|
let binary name = binary t name
|
||||||
let in_findlib ~dir ~dep_kind name =
|
let file_of_lib ?use_provides ~dir name =
|
||||||
let pkg =
|
let lib, file =
|
||||||
match String.lsplit2 name ~on:':' with
|
match String.lsplit2 name ~on:':' with
|
||||||
| None -> invalid_arg "Named_artifacts.in_findlib"
|
| None ->
|
||||||
| Some (pkg, _) -> pkg
|
Loc.fail (Loc.in_file (Path.to_string (Path.relative dir "jbuild")))
|
||||||
|
"invalid ${lib:...} form: %s" name
|
||||||
|
| Some x -> x
|
||||||
in
|
in
|
||||||
Build.record_lib_deps ~dir ~kind:dep_kind [Direct pkg]
|
(lib, file_of_lib t ~lib ~file ?use_provides)
|
||||||
>>>
|
|
||||||
(Build.arr (fun () -> in_findlib t name))
|
|
||||||
|
|
||||||
(* Hides [t] so that we don't resolve things statically *)
|
(* Hides [t] so that we don't resolve things statically *)
|
||||||
let t = ()
|
let t = ()
|
||||||
|
@ -1270,53 +1270,78 @@ module Gen(P : Params) = struct
|
||||||
+-----------------------------------------------------------------+ *)
|
+-----------------------------------------------------------------+ *)
|
||||||
|
|
||||||
module Action_interpret : sig
|
module Action_interpret : sig
|
||||||
type expander
|
val run
|
||||||
|
|
||||||
val expand
|
|
||||||
: Action.Unexpanded.t
|
: Action.Unexpanded.t
|
||||||
-> dir:Path.t
|
-> dir:Path.t
|
||||||
-> dep_kind:Build.lib_dep_kind
|
-> dep_kind:Build.lib_dep_kind
|
||||||
-> targets:Path.t list
|
-> targets:Path.t list
|
||||||
-> deps:Dep_conf.t list
|
-> deps:Dep_conf.t list
|
||||||
-> (unit, expander) Build.t
|
-> (unit, unit) Build.t
|
||||||
|
|
||||||
val run
|
|
||||||
: Action.Unexpanded.t
|
|
||||||
-> dir:Path.t
|
|
||||||
-> targets:Path.t list
|
|
||||||
-> (expander, unit) Build.t
|
|
||||||
end = struct
|
end = struct
|
||||||
module U = Action.Unexpanded
|
module U = Action.Unexpanded
|
||||||
|
|
||||||
type expander = dir:Path.t -> String_with_vars.t -> string
|
type resolved_forms =
|
||||||
|
{ (* Mapping from ${...} forms to their resolutions *)
|
||||||
|
artifacts : Path.t String_map.t
|
||||||
|
; (* Failed resolutions *)
|
||||||
|
failures : fail list
|
||||||
|
; (* All "name" for ${lib:name:...} forms *)
|
||||||
|
lib_deps : String_set.t
|
||||||
|
}
|
||||||
|
|
||||||
type artefact =
|
let add_artifact ?lib_dep acc ~var result =
|
||||||
| Direct of Path.t
|
let lib_deps =
|
||||||
| Dyn of (unit, Path.t) Build.t
|
match lib_dep with
|
||||||
|
| None -> acc.lib_deps
|
||||||
|
| Some lib -> String_set.add lib acc.lib_deps
|
||||||
|
in
|
||||||
|
match result with
|
||||||
|
| Ok path ->
|
||||||
|
{ acc with
|
||||||
|
artifacts = String_map.add acc.artifacts ~key:var ~data:path
|
||||||
|
; lib_deps
|
||||||
|
}
|
||||||
|
| Error fail ->
|
||||||
|
{ acc with
|
||||||
|
failures = fail :: acc.failures
|
||||||
|
; lib_deps
|
||||||
|
}
|
||||||
|
|
||||||
let extract_artifacts ~dir ~dep_kind t =
|
let extract_artifacts ~dir t =
|
||||||
U.fold t ~init:String_map.empty ~f:(fun acc var ->
|
let init =
|
||||||
let module N = Named_artifacts in
|
{ artifacts = String_map.empty
|
||||||
|
; failures = []
|
||||||
|
; lib_deps = String_set.empty
|
||||||
|
}
|
||||||
|
in
|
||||||
|
U.fold t ~init ~f:(fun acc var ->
|
||||||
|
let module A = Artifacts in
|
||||||
match String.lsplit2 var ~on:':' with
|
match String.lsplit2 var ~on:':' with
|
||||||
(* CR-someday jdimino: map the exe to the host exe here *)
|
| Some ("exe" , s) -> add_artifact acc ~var (Ok (Path.relative dir s))
|
||||||
| Some ("exe", s) ->
|
| Some ("path" , s) -> add_artifact acc ~var (Ok (Path.relative dir s))
|
||||||
String_map.add acc ~key:var ~data:(Direct (Path.relative dir s))
|
| Some ("bin" , s) -> add_artifact acc ~var (A.binary s)
|
||||||
| Some ("bin", s) -> String_map.add acc ~key:var ~data:(Dyn (N.binary s))
|
| Some ("lib" , s)
|
||||||
|
| Some ("libexec" , s) ->
|
||||||
|
let lib_dep, res = A.file_of_lib ~dir s in
|
||||||
|
add_artifact acc ~var ~lib_dep res
|
||||||
|
(* CR-someday jdimino: allow this only for (jbuild_version jane_street) *)
|
||||||
| Some ("findlib" , s) ->
|
| Some ("findlib" , s) ->
|
||||||
String_map.add acc ~key:var ~data:(Dyn (N.in_findlib ~dir ~dep_kind s))
|
let lib_dep, res = A.file_of_lib ~dir s ~use_provides:true in
|
||||||
|
add_artifact acc ~var ~lib_dep res
|
||||||
| _ -> acc)
|
| _ -> acc)
|
||||||
|
|
||||||
let expand_string_with_vars ~artifact_map ~targets ~deps : expander =
|
let expand_string_with_vars ~artifacts ~targets ~deps =
|
||||||
let dep_exn ~dir name = function
|
let dep_exn ~dir name = function
|
||||||
| Some dep -> Path.reach ~from:dir dep
|
| Some dep -> Path.reach ~from:dir dep
|
||||||
| None -> die "cannot use ${%s} with files_recursively_in" name
|
| None -> die "cannot use ${%s} with files_recursively_in" name
|
||||||
in
|
in
|
||||||
let lookup ~dir var_name =
|
let lookup ~dir var_name =
|
||||||
match String_map.find var_name artifact_map with
|
match String_map.find var_name artifacts with
|
||||||
| Some path -> Some (Path.reach ~from:dir path)
|
| Some path -> Some (Path.reach ~from:dir path)
|
||||||
| None ->
|
| None ->
|
||||||
match var_name with
|
match var_name with
|
||||||
| "@" -> Some (String.concat ~sep:" " (List.map targets ~f:(Path.reach ~from:dir)))
|
| "@" -> Some (String.concat ~sep:" "
|
||||||
|
(List.map targets ~f:(Path.reach ~from:dir)))
|
||||||
| "<" -> Some (match deps with [] -> "" | dep1::_ -> dep_exn ~dir var_name dep1)
|
| "<" -> Some (match deps with [] -> "" | dep1::_ -> dep_exn ~dir var_name dep1)
|
||||||
| "^" ->
|
| "^" ->
|
||||||
let deps = List.map deps ~f:(dep_exn ~dir var_name) in
|
let deps = List.map deps ~f:(dep_exn ~dir var_name) in
|
||||||
|
@ -1326,42 +1351,26 @@ module Gen(P : Params) = struct
|
||||||
fun ~dir str ->
|
fun ~dir str ->
|
||||||
String_with_vars.expand str ~f:(lookup ~dir)
|
String_with_vars.expand str ~f:(lookup ~dir)
|
||||||
|
|
||||||
let expand t ~dir ~dep_kind ~targets ~deps =
|
let run t ~dir ~dep_kind ~targets ~deps =
|
||||||
let deps =
|
let deps =
|
||||||
List.map deps ~f:(fun dep ->
|
List.map deps ~f:(fun dep ->
|
||||||
Option.map (Dep_conf_interpret.only_plain_file ~dir dep)
|
Option.map (Dep_conf_interpret.only_plain_file ~dir dep)
|
||||||
~f:(Path.relative dir))
|
~f:(Path.relative dir))
|
||||||
in
|
in
|
||||||
let needed_artifacts = extract_artifacts ~dir ~dep_kind t in
|
let forms = extract_artifacts ~dir t in
|
||||||
if String_map.is_empty needed_artifacts then
|
let build =
|
||||||
let expand = expand_string_with_vars ~artifact_map:String_map.empty ~targets ~deps in
|
Build.record_lib_deps ~dir ~kind:dep_kind
|
||||||
Build.return expand
|
(String_set.elements forms.lib_deps
|
||||||
else begin
|
|> List.map ~f:(fun s -> Lib_dep.Direct s))
|
||||||
let directs, dyns =
|
|
||||||
String_map.bindings needed_artifacts
|
|
||||||
|> List.partition_map ~f:(function
|
|
||||||
| (name, Direct x) -> Inl (name, x)
|
|
||||||
| (name, Dyn x) -> Inr (name, x))
|
|
||||||
in
|
|
||||||
Build.fanout
|
|
||||||
(Build.paths (List.map directs ~f:snd))
|
|
||||||
(Build.all (List.map dyns ~f:(fun (name, artifact) ->
|
|
||||||
artifact
|
|
||||||
>>>
|
|
||||||
Build.arr (fun path -> (name, path)))))
|
|
||||||
>>^ snd
|
|
||||||
>>>
|
>>>
|
||||||
Build.dyn_paths (Build.arr (List.map ~f:snd))
|
Build.paths (String_map.values forms.artifacts)
|
||||||
>>>
|
>>>
|
||||||
Build.arr (fun artifacts ->
|
Build.action t ~dir ~env:ctx.env ~targets
|
||||||
let artifact_map =
|
~expand:(expand_string_with_vars ~artifacts:forms.artifacts ~targets ~deps)
|
||||||
String_map.of_alist_exn (List.rev_append directs artifacts)
|
in
|
||||||
in
|
match forms.failures with
|
||||||
expand_string_with_vars ~artifact_map ~targets ~deps)
|
| [] -> build
|
||||||
end
|
| fail :: _ -> Build.fail fail >>> build
|
||||||
|
|
||||||
let run action ~dir ~targets =
|
|
||||||
Build.action action ~dir ~env:ctx.env ~targets
|
|
||||||
end
|
end
|
||||||
|
|
||||||
(* +-----------------------------------------------------------------+
|
(* +-----------------------------------------------------------------+
|
||||||
|
@ -1373,17 +1382,12 @@ module Gen(P : Params) = struct
|
||||||
add_rule
|
add_rule
|
||||||
(Dep_conf_interpret.dep_of_list ~dir rule.deps
|
(Dep_conf_interpret.dep_of_list ~dir rule.deps
|
||||||
>>>
|
>>>
|
||||||
Action_interpret.expand
|
Action_interpret.run
|
||||||
rule.action
|
rule.action
|
||||||
~dir
|
~dir
|
||||||
~dep_kind:Required
|
~dep_kind:Required
|
||||||
~targets
|
~targets
|
||||||
~deps:rule.deps
|
~deps:rule.deps)
|
||||||
>>>
|
|
||||||
Action_interpret.run
|
|
||||||
rule.action
|
|
||||||
~dir
|
|
||||||
~targets)
|
|
||||||
|
|
||||||
let alias_rules (alias_conf : Alias_conf.t) ~dir =
|
let alias_rules (alias_conf : Alias_conf.t) ~dir =
|
||||||
let digest =
|
let digest =
|
||||||
|
@ -1407,13 +1411,13 @@ module Gen(P : Params) = struct
|
||||||
| None -> deps
|
| None -> deps
|
||||||
| Some action ->
|
| Some action ->
|
||||||
deps
|
deps
|
||||||
>>> Action_interpret.expand
|
>>> Action_interpret.run
|
||||||
action
|
action
|
||||||
~dir
|
~dir
|
||||||
~dep_kind:Required
|
~dep_kind:Required
|
||||||
~targets:[]
|
~targets:[]
|
||||||
~deps:alias_conf.deps
|
~deps:alias_conf.deps
|
||||||
>>> Action_interpret.run action ~dir ~targets:[] in
|
in
|
||||||
add_rule (deps >>> dummy)
|
add_rule (deps >>> dummy)
|
||||||
|
|
||||||
(* +-----------------------------------------------------------------+
|
(* +-----------------------------------------------------------------+
|
||||||
|
@ -1708,6 +1712,13 @@ module Gen(P : Params) = struct
|
||||||
List.exists [ "README"; "LICENSE"; "CHANGE"; "HISTORY"]
|
List.exists [ "README"; "LICENSE"; "CHANGE"; "HISTORY"]
|
||||||
~f:(fun prefix -> String.is_prefix fn ~prefix)
|
~f:(fun prefix -> String.is_prefix fn ~prefix)
|
||||||
|
|
||||||
|
let local_install_rules (entries : Install.Entry.t list) ~package =
|
||||||
|
let install_dir = Config.local_install_dir ~context:ctx.name in
|
||||||
|
List.iter entries ~f:(fun entry ->
|
||||||
|
let dst = Install.Entry.relative_installed_path entry ~package in
|
||||||
|
add_rule
|
||||||
|
(Build.symlink ~src:entry.src ~dst:(Path.append install_dir dst)))
|
||||||
|
|
||||||
let install_file package_path package =
|
let install_file package_path package =
|
||||||
let entries =
|
let entries =
|
||||||
List.concat_map stanzas_to_consider_for_install ~f:(fun (dir, stanza) ->
|
List.concat_map stanzas_to_consider_for_install ~f:(fun (dir, stanza) ->
|
||||||
|
@ -1749,6 +1760,7 @@ module Gen(P : Params) = struct
|
||||||
let fn =
|
let fn =
|
||||||
Path.relative (Path.append ctx.build_dir package_path) (package ^ ".install")
|
Path.relative (Path.append ctx.build_dir package_path) (package ^ ".install")
|
||||||
in
|
in
|
||||||
|
local_install_rules entries ~package;
|
||||||
add_rule
|
add_rule
|
||||||
(Build.path_set (Install.files entries) >>>
|
(Build.path_set (Install.files entries) >>>
|
||||||
Build.create_file ~target:fn (fun () ->
|
Build.create_file ~target:fn (fun () ->
|
||||||
|
|
|
@ -97,7 +97,7 @@ module Hashtbl = struct
|
||||||
match find t key with
|
match find t key with
|
||||||
| Some x -> x
|
| Some x -> x
|
||||||
| None ->
|
| None ->
|
||||||
let x = f () in
|
let x = f key in
|
||||||
add t ~key ~data:x;
|
add t ~key ~data:x;
|
||||||
x
|
x
|
||||||
end
|
end
|
||||||
|
@ -227,6 +227,14 @@ module String = struct
|
||||||
(sub s ~pos:0 ~len:i,
|
(sub s ~pos:0 ~len:i,
|
||||||
sub s ~pos:(i + 1) ~len:(String.length s - i - 1))
|
sub s ~pos:(i + 1) ~len:(String.length s - i - 1))
|
||||||
|
|
||||||
|
let rsplit2 s ~on =
|
||||||
|
match rindex s on with
|
||||||
|
| exception Not_found -> None
|
||||||
|
| i ->
|
||||||
|
Some
|
||||||
|
(sub s ~pos:0 ~len:i,
|
||||||
|
sub s ~pos:(i + 1) ~len:(String.length s - i - 1))
|
||||||
|
|
||||||
let index s ch =
|
let index s ch =
|
||||||
match index s ch with
|
match index s ch with
|
||||||
| i -> Some i
|
| i -> Some i
|
||||||
|
|
|
@ -61,6 +61,51 @@ module Entry = struct
|
||||||
; dst
|
; dst
|
||||||
; section
|
; section
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module Paths = struct
|
||||||
|
let lib = Path.(relative root) "lib"
|
||||||
|
let libexec = Path.(relative root) "libexec"
|
||||||
|
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 main_dir =
|
||||||
|
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 =
|
||||||
|
match t.dst with
|
||||||
|
| Some x -> x
|
||||||
|
| None ->
|
||||||
|
let dst = Path.basename t.src in
|
||||||
|
match t.section with
|
||||||
|
| Man -> begin
|
||||||
|
match String.rsplit2 dst ~on:'.' with
|
||||||
|
| None -> dst
|
||||||
|
| Some (_, sec) -> sprintf "man%s/%s" sec dst
|
||||||
|
end
|
||||||
|
| _ -> dst
|
||||||
|
in
|
||||||
|
Path.relative main_dir dst
|
||||||
end
|
end
|
||||||
|
|
||||||
module SMap = Map.Make(Section)
|
module SMap = Map.Make(Section)
|
||||||
|
|
|
@ -26,6 +26,8 @@ module Entry : sig
|
||||||
}
|
}
|
||||||
|
|
||||||
val make : Section.t -> ?dst:string -> Path.t -> t
|
val make : Section.t -> ?dst:string -> Path.t -> t
|
||||||
|
|
||||||
|
val relative_installed_path : t -> package:string -> Path.t
|
||||||
end
|
end
|
||||||
|
|
||||||
val files : Entry.t list -> Path.Set.t
|
val files : Entry.t list -> Path.Set.t
|
||||||
|
|
|
@ -701,7 +701,6 @@ module Stanza = struct
|
||||||
; cstr "rule" (Rule.v1 @> nil) (fun x -> [Rule x])
|
; cstr "rule" (Rule.v1 @> nil) (fun x -> [Rule x])
|
||||||
; cstr "ocamllex" (list string @> nil) (fun x -> rules (Rule.ocamllex_v1 x))
|
; cstr "ocamllex" (list string @> nil) (fun x -> rules (Rule.ocamllex_v1 x))
|
||||||
; cstr "ocamlyacc" (list string @> nil) (fun x -> rules (Rule.ocamlyacc_v1 x))
|
; cstr "ocamlyacc" (list string @> nil) (fun x -> rules (Rule.ocamlyacc_v1 x))
|
||||||
; cstr "provides" (Provides.v1 @> nil) (fun x -> [Provides x])
|
|
||||||
; cstr "install" (Install_conf.v1 @> nil) (fun x -> [Install x])
|
; cstr "install" (Install_conf.v1 @> nil) (fun x -> [Install x])
|
||||||
; cstr "alias" (Alias_conf.v1 @> nil) (fun x -> [Alias x])
|
; cstr "alias" (Alias_conf.v1 @> nil) (fun x -> [Alias x])
|
||||||
(* Just for validation and error messages *)
|
(* Just for validation and error messages *)
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
open Import
|
|
||||||
open Jbuild_types
|
|
||||||
|
|
||||||
type t =
|
|
||||||
{ path : Path.t list
|
|
||||||
; findlib : Findlib.t
|
|
||||||
; artifacts : (string, Path.t) Hashtbl.t
|
|
||||||
}
|
|
||||||
|
|
||||||
let create ~path findlib stanzas =
|
|
||||||
let artifacts : (string, Path.t) Hashtbl.t = Hashtbl.create 1024 in
|
|
||||||
List.iter stanzas ~f:(fun (dir, stanzas) ->
|
|
||||||
List.iter stanzas ~f:(fun stanza ->
|
|
||||||
match (stanza : Stanza.t) with
|
|
||||||
| Provides { name; file } ->
|
|
||||||
Hashtbl.add artifacts ~key:name ~data:(Path.relative dir file)
|
|
||||||
| _ -> ()));
|
|
||||||
{ path; findlib; artifacts }
|
|
||||||
|
|
||||||
let binary t name =
|
|
||||||
match Hashtbl.find t.artifacts name with
|
|
||||||
| Some p -> p
|
|
||||||
| None ->
|
|
||||||
match Bin.which ~path:t.path name with
|
|
||||||
| Some p ->
|
|
||||||
Hashtbl.add t.artifacts ~key:name ~data:p;
|
|
||||||
p
|
|
||||||
| None ->
|
|
||||||
die "Program %s not found in the tree or in the PATH" name
|
|
||||||
|
|
||||||
let in_findlib t name =
|
|
||||||
match Hashtbl.find t.artifacts name with
|
|
||||||
| Some p -> p
|
|
||||||
| None ->
|
|
||||||
match String.lsplit2 name ~on:':' with
|
|
||||||
| None -> invalid_arg "Named_artifacts.in_findlib"
|
|
||||||
| Some (pkg, file) ->
|
|
||||||
let p = Path.relative (Findlib.find_exn t.findlib pkg).dir file in
|
|
||||||
Hashtbl.add t.artifacts ~key:name ~data:p;
|
|
||||||
p
|
|
|
@ -1,20 +0,0 @@
|
||||||
(** [Named_artifact] provides a way to reference artifacts in jbuild rules without having
|
|
||||||
to hardcode their exact locations. These named artifacts will be looked up
|
|
||||||
appropriately (in the tree, or for the public release, possibly in the PATH or in
|
|
||||||
findlib). *)
|
|
||||||
|
|
||||||
open! Import
|
|
||||||
|
|
||||||
type t
|
|
||||||
|
|
||||||
val create : path:Path.t list -> Findlib.t -> (Path.t * Jbuild_types.Stanza.t list) list -> t
|
|
||||||
|
|
||||||
(** In the three following functions, the string argument matches the first argument of
|
|
||||||
the [(provides ...)] stanza in the jbuild. *)
|
|
||||||
|
|
||||||
(** A named artifact that is looked up in the PATH if not found in the tree *)
|
|
||||||
val binary : t -> string -> Path.t
|
|
||||||
|
|
||||||
(** A named artifact that is looked up in the given findlib package if not found in the
|
|
||||||
tree. Syntax is: ["<findlib_package>:<filename>"]. *)
|
|
||||||
val in_findlib : t -> string -> Path.t
|
|
|
@ -51,6 +51,7 @@ let t sexps =
|
||||||
if name = "" ||
|
if name = "" ||
|
||||||
String.is_prefix name ~prefix:"." ||
|
String.is_prefix name ~prefix:"." ||
|
||||||
name = "log" ||
|
name = "log" ||
|
||||||
|
name = "install" ||
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue