Add (copy_files <glob>) stanza (#35)
Add (copy_files <glob>) and (copy_files# <glob>) stanzas. These stanzas setup rules for copying files from a sub-directory to the current directory. This provides a reasonable way to support multi-directory library/executables in jbuilder.
This commit is contained in:
parent
d55c807d51
commit
cecf0a2aaf
|
@ -511,6 +511,22 @@ stanza, if the source file has extension ``.exe`` or ``.bc``, then
|
||||||
Jbuilder implicitly adds the ``.exe`` extension to the destination, if
|
Jbuilder implicitly adds the ``.exe`` extension to the destination, if
|
||||||
not already present.
|
not already present.
|
||||||
|
|
||||||
|
copy_files
|
||||||
|
----------
|
||||||
|
|
||||||
|
The ``copy_files`` and ``copy_files#`` stanzas allow to specify that
|
||||||
|
files from another directory could be copied if needed to the current
|
||||||
|
directory.
|
||||||
|
|
||||||
|
The syntax is as follows:
|
||||||
|
|
||||||
|
.. code:: scheme
|
||||||
|
|
||||||
|
(copy_files <glob>)
|
||||||
|
|
||||||
|
``<glob>`` represents the set of files to copy, see the :ref:`glob
|
||||||
|
<glob>` for details.
|
||||||
|
|
||||||
Common items
|
Common items
|
||||||
============
|
============
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,8 @@ struct
|
||||||
; cstr_loc "copy-and-add-line-directive" (path @> path @> nil) (fun loc src dst ->
|
; cstr_loc "copy-and-add-line-directive" (path @> path @> nil) (fun loc src dst ->
|
||||||
Loc.warn loc "copy-and-add-line-directive is deprecated, use copy# instead";
|
Loc.warn loc "copy-and-add-line-directive is deprecated, use copy# instead";
|
||||||
Copy_and_add_line_directive (src, dst))
|
Copy_and_add_line_directive (src, dst))
|
||||||
|
; cstr "copy#" (path @> path @> nil) (fun src dst ->
|
||||||
|
Copy_and_add_line_directive (src, dst))
|
||||||
; cstr "system" (string @> nil) (fun cmd -> System cmd)
|
; cstr "system" (string @> nil) (fun cmd -> System cmd)
|
||||||
; cstr "bash" (string @> nil) (fun cmd -> Bash cmd)
|
; cstr "bash" (string @> nil) (fun cmd -> Bash cmd)
|
||||||
; cstr "write-file" (path @> string @> nil) (fun fn s -> Write_file (fn, s))
|
; cstr "write-file" (path @> string @> nil) (fun fn s -> Write_file (fn, s))
|
||||||
|
|
|
@ -255,6 +255,11 @@ let copy ~src ~dst =
|
||||||
path src >>>
|
path src >>>
|
||||||
action ~targets:[dst] (Copy (src, dst))
|
action ~targets:[dst] (Copy (src, dst))
|
||||||
|
|
||||||
|
let copy_and_add_line_directive ~src ~dst =
|
||||||
|
path src >>>
|
||||||
|
action ~targets:[dst]
|
||||||
|
(Copy_and_add_line_directive (src, dst))
|
||||||
|
|
||||||
let symlink ~src ~dst =
|
let symlink ~src ~dst =
|
||||||
path src >>>
|
path src >>>
|
||||||
action ~targets:[dst] (Symlink (src, dst))
|
action ~targets:[dst] (Symlink (src, dst))
|
||||||
|
|
|
@ -108,6 +108,7 @@ val write_file : Path.t -> string -> (unit, Action.t) t
|
||||||
val write_file_dyn : Path.t -> (string, Action.t) t
|
val write_file_dyn : Path.t -> (string, Action.t) t
|
||||||
|
|
||||||
val copy : src:Path.t -> dst:Path.t -> (unit, Action.t) t
|
val copy : src:Path.t -> dst:Path.t -> (unit, Action.t) t
|
||||||
|
val copy_and_add_line_directive : src:Path.t -> dst:Path.t -> (unit, Action.t) t
|
||||||
|
|
||||||
val symlink : src:Path.t -> dst:Path.t -> (unit, Action.t) t
|
val symlink : src:Path.t -> dst:Path.t -> (unit, Action.t) t
|
||||||
|
|
||||||
|
|
|
@ -421,6 +421,7 @@ module Gen(P : Params) = struct
|
||||||
; flags
|
; flags
|
||||||
; preprocess = Buildable.single_preprocess lib.buildable
|
; preprocess = Buildable.single_preprocess lib.buildable
|
||||||
; libname = Some lib.name
|
; libname = Some lib.name
|
||||||
|
; source_dirs = Path.Set.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
(* +-----------------------------------------------------------------+
|
(* +-----------------------------------------------------------------+
|
||||||
|
@ -539,6 +540,7 @@ module Gen(P : Params) = struct
|
||||||
; flags = Ocaml_flags.common flags
|
; flags = Ocaml_flags.common flags
|
||||||
; preprocess = Buildable.single_preprocess exes.buildable
|
; preprocess = Buildable.single_preprocess exes.buildable
|
||||||
; libname = None
|
; libname = None
|
||||||
|
; source_dirs = Path.Set.empty
|
||||||
}
|
}
|
||||||
|
|
||||||
(* +-----------------------------------------------------------------+
|
(* +-----------------------------------------------------------------+
|
||||||
|
@ -599,6 +601,51 @@ module Gen(P : Params) = struct
|
||||||
; Build.create_file digest_path
|
; Build.create_file digest_path
|
||||||
])
|
])
|
||||||
|
|
||||||
|
let copy_files_rules (def: Copy_files.t) ~src_dir ~dir ~scope =
|
||||||
|
let loc = String_with_vars.loc def.glob in
|
||||||
|
let glob_in_src =
|
||||||
|
let src_glob = SC.expand_vars sctx ~dir def.glob ~scope in
|
||||||
|
Path.relative src_dir src_glob ~error_loc:loc
|
||||||
|
in
|
||||||
|
(* The following condition is required for merlin to work.
|
||||||
|
Additionally, the order in which the rules are evaluated only
|
||||||
|
ensures that [sources_and_targets_known_so_far] returns the
|
||||||
|
right answer for sub-directories only. *)
|
||||||
|
if not (Path.is_descendant glob_in_src ~of_:src_dir) then
|
||||||
|
Loc.fail loc "%s is not a sub-directory of %s"
|
||||||
|
(Path.to_string_maybe_quoted glob_in_src) (Path.to_string_maybe_quoted src_dir);
|
||||||
|
let glob = Path.basename glob_in_src in
|
||||||
|
let src_in_src = Path.parent glob_in_src in
|
||||||
|
let re =
|
||||||
|
match Glob_lexer.parse_string glob with
|
||||||
|
| Ok re ->
|
||||||
|
Re.compile re
|
||||||
|
| Error (_pos, msg) ->
|
||||||
|
Loc.fail (String_with_vars.loc def.glob) "invalid glob: %s" msg
|
||||||
|
in
|
||||||
|
(* add rules *)
|
||||||
|
let files = SC.sources_and_targets_known_so_far sctx ~src_path:src_in_src in
|
||||||
|
let src_in_build = Path.append ctx.build_dir src_in_src in
|
||||||
|
String_set.iter files ~f:(fun basename ->
|
||||||
|
let matches = Re.execp re basename in
|
||||||
|
if matches then
|
||||||
|
let file_src = Path.relative src_in_build basename in
|
||||||
|
let file_dst = Path.relative dir basename in
|
||||||
|
SC.add_rule sctx
|
||||||
|
((if def.add_line_directive
|
||||||
|
then Build.copy_and_add_line_directive
|
||||||
|
else Build.copy)
|
||||||
|
~src:file_src
|
||||||
|
~dst:file_dst)
|
||||||
|
);
|
||||||
|
{ Merlin.requires = Build.return []
|
||||||
|
; flags = Build.return []
|
||||||
|
; preprocess = Jbuild.Preprocess.No_preprocessing
|
||||||
|
; libname = None
|
||||||
|
; source_dirs = Path.Set.singleton src_in_src
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
(* +-----------------------------------------------------------------+
|
(* +-----------------------------------------------------------------+
|
||||||
| Modules listing |
|
| Modules listing |
|
||||||
+-----------------------------------------------------------------+ *)
|
+-----------------------------------------------------------------+ *)
|
||||||
|
@ -704,12 +751,16 @@ Add it to your jbuild file to remove this warning.
|
||||||
let rules { SC.Dir_with_jbuild. src_dir; ctx_dir; stanzas; scope } =
|
let rules { SC.Dir_with_jbuild. src_dir; ctx_dir; stanzas; scope } =
|
||||||
(* Interpret user rules and other simple stanzas first in order to populate the known
|
(* Interpret user rules and other simple stanzas first in order to populate the known
|
||||||
target table, which is needed for guessing the list of modules. *)
|
target table, which is needed for guessing the list of modules. *)
|
||||||
List.iter stanzas ~f:(fun stanza ->
|
let merlins =
|
||||||
let dir = ctx_dir in
|
List.filter_map stanzas ~f:(fun stanza ->
|
||||||
match (stanza : Stanza.t) with
|
let dir = ctx_dir in
|
||||||
| Rule rule -> user_rule rule ~dir ~scope
|
match (stanza : Stanza.t) with
|
||||||
| Alias alias -> alias_rules alias ~dir ~scope
|
| Rule rule -> user_rule rule ~dir ~scope; None
|
||||||
| Library _ | Executables _ | Provides _ | Install _ -> ());
|
| Alias alias -> alias_rules alias ~dir ~scope; None
|
||||||
|
| Copy_files def ->
|
||||||
|
Some (copy_files_rules def ~src_dir ~dir ~scope)
|
||||||
|
| Library _ | Executables _ | Provides _ | Install _ -> None)
|
||||||
|
in
|
||||||
let files = lazy (
|
let files = lazy (
|
||||||
let files = SC.sources_and_targets_known_so_far sctx ~src_path:src_dir in
|
let files = SC.sources_and_targets_known_so_far sctx ~src_path:src_dir in
|
||||||
(* Manually add files generated by the (select ...) dependencies since we haven't
|
(* Manually add files generated by the (select ...) dependencies since we haven't
|
||||||
|
@ -727,15 +778,17 @@ Add it to your jbuild file to remove this warning.
|
||||||
guess_modules ~dir:src_dir
|
guess_modules ~dir:src_dir
|
||||||
~files:(Lazy.force files))
|
~files:(Lazy.force files))
|
||||||
in
|
in
|
||||||
List.filter_map stanzas ~f:(fun stanza ->
|
List.fold_left stanzas ~init:merlins ~f:(fun merlins stanza ->
|
||||||
let dir = ctx_dir in
|
let dir = ctx_dir in
|
||||||
match (stanza : Stanza.t) with
|
match (stanza : Stanza.t) with
|
||||||
| Library lib ->
|
| Library lib ->
|
||||||
Some (library_rules lib ~dir ~all_modules:(Lazy.force all_modules)
|
library_rules lib ~dir ~all_modules:(Lazy.force all_modules)
|
||||||
~files:(Lazy.force files) ~scope)
|
~files:(Lazy.force files) ~scope
|
||||||
|
:: merlins
|
||||||
| Executables exes ->
|
| Executables exes ->
|
||||||
Some (executables_rules exes ~dir ~all_modules:(Lazy.force all_modules) ~scope)
|
executables_rules exes ~dir ~all_modules:(Lazy.force all_modules) ~scope
|
||||||
| _ -> None)
|
:: merlins
|
||||||
|
| _ -> merlins)
|
||||||
|> Merlin.merge_all
|
|> Merlin.merge_all
|
||||||
|> Option.iter ~f:(Merlin.add_rules sctx ~dir:ctx_dir);
|
|> Option.iter ~f:(Merlin.add_rules sctx ~dir:ctx_dir);
|
||||||
Option.iter (Utop.exe_stanzas stanzas) ~f:(fun (exe, all_modules) ->
|
Option.iter (Utop.exe_stanzas stanzas) ~f:(fun (exe, all_modules) ->
|
||||||
|
@ -745,7 +798,15 @@ Add it to your jbuild file to remove this warning.
|
||||||
Utop.add_module_rules sctx ~dir merlin.requires;
|
Utop.add_module_rules sctx ~dir merlin.requires;
|
||||||
)
|
)
|
||||||
|
|
||||||
let () = List.iter (SC.stanzas sctx) ~f:rules
|
let () =
|
||||||
|
(* Sort the list of stanzas by directory so that we traverse
|
||||||
|
subdirectories first.
|
||||||
|
|
||||||
|
This is required for correctly interpreting [copy_files]. *)
|
||||||
|
let subtree_smaller x y =
|
||||||
|
Path.compare y.SC.Dir_with_jbuild.src_dir x.SC.Dir_with_jbuild.src_dir in
|
||||||
|
let stanzas = List.sort ~cmp:subtree_smaller (SC.stanzas sctx) in
|
||||||
|
List.iter stanzas ~f:rules
|
||||||
let () =
|
let () =
|
||||||
SC.add_rules sctx (Js_of_ocaml_rules.setup_separate_compilation_rules sctx)
|
SC.add_rules sctx (Js_of_ocaml_rules.setup_separate_compilation_rules sctx)
|
||||||
let () = Odoc.setup_css_rule sctx
|
let () = Odoc.setup_css_rule sctx
|
||||||
|
|
|
@ -886,6 +886,14 @@ module Alias_conf = struct
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Copy_files = struct
|
||||||
|
type t = { add_line_directive : bool
|
||||||
|
; glob : String_with_vars.t
|
||||||
|
}
|
||||||
|
|
||||||
|
let v1 = String_with_vars.t
|
||||||
|
end
|
||||||
|
|
||||||
module Stanza = struct
|
module Stanza = struct
|
||||||
type t =
|
type t =
|
||||||
| Library of Library.t
|
| Library of Library.t
|
||||||
|
@ -894,6 +902,7 @@ module Stanza = struct
|
||||||
| Provides of Provides.t
|
| Provides of Provides.t
|
||||||
| Install of Install_conf.t
|
| Install of Install_conf.t
|
||||||
| Alias of Alias_conf.t
|
| Alias of Alias_conf.t
|
||||||
|
| Copy_files of Copy_files.t
|
||||||
|
|
||||||
let rules l = List.map l ~f:(fun x -> Rule x)
|
let rules l = List.map l ~f:(fun x -> Rule x)
|
||||||
|
|
||||||
|
@ -913,6 +922,10 @@ module Stanza = struct
|
||||||
; cstr_loc "menhir" (Menhir.v1 @> nil) (fun loc x -> rules (Menhir.v1_to_rule loc x))
|
; cstr_loc "menhir" (Menhir.v1 @> nil) (fun loc x -> rules (Menhir.v1_to_rule loc x))
|
||||||
; cstr "install" (Install_conf.v1 pkgs @> nil) (fun x -> [Install x])
|
; cstr "install" (Install_conf.v1 pkgs @> nil) (fun x -> [Install x])
|
||||||
; cstr "alias" (Alias_conf.v1 pkgs @> nil) (fun x -> [Alias x])
|
; cstr "alias" (Alias_conf.v1 pkgs @> nil) (fun x -> [Alias x])
|
||||||
|
; cstr "copy_files" (Copy_files.v1 @> nil)
|
||||||
|
(fun glob -> [Copy_files {add_line_directive = false; glob}])
|
||||||
|
; cstr "copy_files#" (Copy_files.v1 @> nil)
|
||||||
|
(fun glob -> [Copy_files {add_line_directive = true; glob}])
|
||||||
(* Just for validation and error messages *)
|
(* Just for validation and error messages *)
|
||||||
; cstr "jbuild_version" (Jbuild_version.t @> nil) (fun _ -> [])
|
; cstr "jbuild_version" (Jbuild_version.t @> nil) (fun _ -> [])
|
||||||
]
|
]
|
||||||
|
|
|
@ -222,6 +222,13 @@ module Alias_conf : sig
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Copy_files : sig
|
||||||
|
type t =
|
||||||
|
{ add_line_directive : bool
|
||||||
|
; glob : String_with_vars.t
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
module Stanza : sig
|
module Stanza : sig
|
||||||
type t =
|
type t =
|
||||||
| Library of Library.t
|
| Library of Library.t
|
||||||
|
@ -230,6 +237,7 @@ module Stanza : sig
|
||||||
| Provides of Provides.t
|
| Provides of Provides.t
|
||||||
| Install of Install_conf.t
|
| Install of Install_conf.t
|
||||||
| Alias of Alias_conf.t
|
| Alias of Alias_conf.t
|
||||||
|
| Copy_files of Copy_files.t
|
||||||
end
|
end
|
||||||
|
|
||||||
module Stanzas : sig
|
module Stanzas : sig
|
||||||
|
|
|
@ -9,6 +9,7 @@ type t =
|
||||||
; flags : (unit, string list) Build.t
|
; flags : (unit, string list) Build.t
|
||||||
; preprocess : Jbuild.Preprocess.t
|
; preprocess : Jbuild.Preprocess.t
|
||||||
; libname : string option
|
; libname : string option
|
||||||
|
; source_dirs: Path.Set.t
|
||||||
}
|
}
|
||||||
|
|
||||||
(* This must be forced after we change the cwd to the workspace root *)
|
(* This must be forced after we change the cwd to the workspace root *)
|
||||||
|
@ -55,6 +56,12 @@ let dot_merlin sctx ~dir ({ requires; flags; _ } as t) =
|
||||||
internals, ("PKG " ^ pkg.name) :: externals
|
internals, ("PKG " ^ pkg.name) :: externals
|
||||||
)
|
)
|
||||||
in
|
in
|
||||||
|
let source_dirs =
|
||||||
|
Path.Set.fold t.source_dirs ~init:[] ~f:(fun path acc ->
|
||||||
|
let path = Path.reach path ~from:remaindir in
|
||||||
|
("S " ^ path)::acc
|
||||||
|
)
|
||||||
|
in
|
||||||
let flags =
|
let flags =
|
||||||
match flags with
|
match flags with
|
||||||
| [] -> []
|
| [] -> []
|
||||||
|
@ -65,6 +72,7 @@ let dot_merlin sctx ~dir ({ requires; flags; _ } as t) =
|
||||||
let dot_merlin =
|
let dot_merlin =
|
||||||
List.concat
|
List.concat
|
||||||
[ [ "B " ^ (Path.reach dir ~from:remaindir) ]
|
[ [ "B " ^ (Path.reach dir ~from:remaindir) ]
|
||||||
|
; source_dirs
|
||||||
; internals
|
; internals
|
||||||
; externals
|
; externals
|
||||||
; flags
|
; flags
|
||||||
|
@ -94,9 +102,10 @@ let merge_two a b =
|
||||||
else
|
else
|
||||||
No_preprocessing
|
No_preprocessing
|
||||||
; libname =
|
; libname =
|
||||||
match a.libname with
|
(match a.libname with
|
||||||
| Some _ as x -> x
|
| Some _ as x -> x
|
||||||
| None -> b.libname
|
| None -> b.libname)
|
||||||
|
; source_dirs = Path.Set.union a.source_dirs b.source_dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
let merge_all = function
|
let merge_all = function
|
||||||
|
|
|
@ -5,6 +5,7 @@ type t =
|
||||||
; flags : (unit, string list) Build.t
|
; flags : (unit, string list) Build.t
|
||||||
; preprocess : Jbuild.Preprocess.t
|
; preprocess : Jbuild.Preprocess.t
|
||||||
; libname : string option
|
; libname : string option
|
||||||
|
; source_dirs: Path.Set.t
|
||||||
}
|
}
|
||||||
|
|
||||||
val merge_all : t list -> t option
|
val merge_all : t list -> t option
|
||||||
|
|
|
@ -37,6 +37,7 @@ val t : t Sexp.Of_sexp.t
|
||||||
val sexp_of_t : t Sexp.To_sexp.t
|
val sexp_of_t : t Sexp.To_sexp.t
|
||||||
|
|
||||||
val compare : t -> t -> int
|
val compare : t -> t -> int
|
||||||
|
(** a directory is smaller than its descendants *)
|
||||||
|
|
||||||
module Set : Set.S with type elt = t
|
module Set : Set.S with type elt = t
|
||||||
module Map : Map.S with type key = t
|
module Map : Map.S with type key = t
|
||||||
|
|
|
@ -64,3 +64,10 @@
|
||||||
(action
|
(action
|
||||||
(chdir test-cases/lib-available
|
(chdir test-cases/lib-available
|
||||||
(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/copy_files)))
|
||||||
|
(action
|
||||||
|
(chdir test-cases/copy_files
|
||||||
|
(setenv JBUILDER ${bin:jbuilder} (run ${exe:cram.exe} run.t))))))
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
(jbuild_version 1)
|
||||||
|
|
||||||
|
(copy_files# lexers/*.ml{,i})
|
||||||
|
|
||||||
|
(executables
|
||||||
|
((names (test))))
|
|
@ -0,0 +1,3 @@
|
||||||
|
(jbuild_version 1)
|
||||||
|
|
||||||
|
(ocamllex (lexer1))
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
rule lex = parse
|
||||||
|
| _ { true }
|
||||||
|
| eof { false }
|
|
@ -0,0 +1,8 @@
|
||||||
|
$ $JBUILDER build -j1 test.exe .merlin --root . --debug-dependency-path
|
||||||
|
ocamllex lexers/lexer1.ml
|
||||||
|
ocamldep test.depends.ocamldep-output
|
||||||
|
ocamlc lexer1.{cmi,cmo,cmt}
|
||||||
|
ocamlopt lexer1.{cmx,o}
|
||||||
|
ocamlc test.{cmi,cmo,cmt}
|
||||||
|
ocamlopt test.{cmx,o}
|
||||||
|
ocamlopt test.exe
|
|
@ -0,0 +1 @@
|
||||||
|
include Lexer1
|
Loading…
Reference in New Issue