Remove (provides ...) and uses (install ...) instead

This commit is contained in:
Jeremie Dimino 2017-03-01 11:04:32 +00:00
parent 60be8f7734
commit 6e25a7dfe5
19 changed files with 300 additions and 187 deletions

View File

@ -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

83
src/artifacts.ml Normal file
View File

@ -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
}

21
src/artifacts.mli Normal file
View File

@ -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

View File

@ -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

View File

@ -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 =

5
src/config.ml Normal file
View File

@ -0,0 +1,5 @@
open! Import
let local_install_dir =
let dir = Path.(relative root) "install" in
fun ~context -> Path.relative dir context

6
src/config.mli Normal file
View File

@ -0,0 +1,6 @@
(** Configuration parameters *)
open! Import
(** Local installation directory *)
val local_install_dir : context:string -> Path.t

View File

@ -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

View File

@ -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 *)

View File

@ -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

View File

@ -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

View File

@ -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 () ->

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 *)

View File

@ -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

View File

@ -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

View File

@ -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;