feat(run): run blocks for commands the build does not produce
This commit is contained in:
5 files changed
+99
-15
No files matched your search
+46
-13
@@ -8,7 +8,7 @@ let usage =
|
|||||||
"";
|
"";
|
||||||
Style.bold " usage";
|
Style.bold " usage";
|
||||||
" meowc [build] [target...] build everything, or the named targets";
|
" meowc [build] [target...] build everything, or the named targets";
|
||||||
" meowc run <bin> [args...] build a bin target, then run it";
|
" meowc run <name> [args...] run a bin target, or a run block";
|
||||||
" meowc test [filter] build and run test targets";
|
" meowc test [filter] build and run test targets";
|
||||||
" meowc install copy installable outputs under the prefix";
|
" meowc install copy installable outputs under the prefix";
|
||||||
" meowc configure run the checks and write the config header";
|
" meowc configure run the checks and write the config header";
|
||||||
@@ -259,24 +259,46 @@ let cmd_build names =
|
|||||||
summarise r (Unix.gettimeofday () -. t0);
|
summarise r (Unix.gettimeofday () -. t0);
|
||||||
p
|
p
|
||||||
|
|
||||||
|
let runnable_names p =
|
||||||
|
List.filter_map (fun (t : target) -> if t.kind = Bin then Some t.name else None) p.targets
|
||||||
|
@ List.map (fun (s : script) -> s.sname) p.scripts
|
||||||
|
|
||||||
|
let hand_over cmd =
|
||||||
|
flush stdout;
|
||||||
|
try Unix.execvp cmd.(0) cmd
|
||||||
|
with Unix.Unix_error (e, _, _) -> Diag.error "cannot run %s: %s" cmd.(0) (Unix.error_message e)
|
||||||
|
|
||||||
let cmd_run = function
|
let cmd_run = function
|
||||||
| [] -> Diag.error ~hint:"try: meowc run <bin>" "run needs a target name"
|
| [] -> Diag.error ~hint:"try: meowc run <bin>" "run needs a name"
|
||||||
| name :: args ->
|
| name :: args -> (
|
||||||
let p = configure () in
|
let p = configure () in
|
||||||
let t = match find p name with
|
let build_first names =
|
||||||
| Some ({ kind = Bin; _ } as t) -> t
|
|
||||||
| Some t -> Diag.error "%s is a %s, not a bin" name (kind_name t.kind)
|
|
||||||
| None -> unknown_target p name
|
|
||||||
in
|
|
||||||
banner p;
|
banner p;
|
||||||
with_config_header p;
|
with_config_header p;
|
||||||
let t0 = Unix.gettimeofday () in
|
let t0 = Unix.gettimeofday () in
|
||||||
let _, r = execute p ~names:[ name ] in
|
let _, r = execute p ~names in
|
||||||
summarise r (Unix.gettimeofday () -. t0);
|
summarise r (Unix.gettimeofday () -. t0)
|
||||||
|
in
|
||||||
|
match List.find_opt (fun (s : script) -> s.sname = name) p.scripts with
|
||||||
|
| Some s ->
|
||||||
|
(* no names means everything, the same as a bare meowc build *)
|
||||||
|
build_first s.suses;
|
||||||
|
let cmd = Array.of_list (s.scmd @ args) in
|
||||||
|
if not fl.quiet then Printf.printf "\n %s\n" (Style.dim (Exec.show cmd));
|
||||||
|
hand_over cmd
|
||||||
|
| None ->
|
||||||
|
let t =
|
||||||
|
match find p name with
|
||||||
|
| Some ({ kind = Bin; _ } as t) -> t
|
||||||
|
| Some t -> Diag.error "%s is a %s, not a bin" name (kind_name t.kind)
|
||||||
|
| None ->
|
||||||
|
Diag.error ~hint:(Suggest.hint name (runnable_names p))
|
||||||
|
"no bin target or run block named %S" name
|
||||||
|
in
|
||||||
|
build_first [ name ];
|
||||||
let exe = Build.bin_of p t in
|
let exe = Build.bin_of p t in
|
||||||
if not fl.quiet then Printf.printf "\n %s\n" (Style.dim exe);
|
if not fl.quiet then Printf.printf "\n %s\n" (Style.dim exe);
|
||||||
flush stdout;
|
hand_over (Array.of_list (exe :: args)))
|
||||||
Unix.execv exe (Array.of_list (exe :: args))
|
|
||||||
|
|
||||||
let cmd_test filter =
|
let cmd_test filter =
|
||||||
let p = configure () in
|
let p = configure () in
|
||||||
@@ -344,7 +366,18 @@ let cmd_targets () =
|
|||||||
(Style.pad 20 (Style.dim (Style.plural (List.length (all_srcs t)) "source")))
|
(Style.pad 20 (Style.dim (Style.plural (List.length (all_srcs t)) "source")))
|
||||||
(Style.dim (if t.uses = [] then "" else "uses " ^ String.concat " " t.uses)))
|
(Style.dim (if t.uses = [] then "" else "uses " ^ String.concat " " t.uses)))
|
||||||
p.targets;
|
p.targets;
|
||||||
Printf.printf "\n %s\n" (Style.dim (Style.plural (List.length p.targets) "target"))
|
List.iter
|
||||||
|
(fun (s : script) ->
|
||||||
|
Printf.printf " %s%s%s\n"
|
||||||
|
(Style.yellow (Style.pad 9 "run"))
|
||||||
|
(Style.pad 22 s.sname)
|
||||||
|
(Style.dim (if s.suses = [] then "" else "uses " ^ String.concat " " s.suses)))
|
||||||
|
p.scripts;
|
||||||
|
Printf.printf "\n %s\n"
|
||||||
|
(Style.dim
|
||||||
|
(String.concat ", "
|
||||||
|
(Style.plural (List.length p.targets) "target"
|
||||||
|
:: (if p.scripts = [] then [] else [ Style.plural (List.length p.scripts) "run block" ]))))
|
||||||
|
|
||||||
let cmd_graph () =
|
let cmd_graph () =
|
||||||
let p = configure () in
|
let p = configure () in
|
||||||
|
|||||||
@@ -113,6 +113,13 @@ bin rtbench {
|
|||||||
use rtscene
|
use rtscene
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# A canned invocation, for a quick low sample preview. ${builddir} follows the
|
||||||
|
# target triple, so this still points at the right binary under --target.
|
||||||
|
run preview {
|
||||||
|
use render
|
||||||
|
command ${builddir}/bin/render --scene rings --width 320 --height 180 --samples 8
|
||||||
|
}
|
||||||
|
|
||||||
test math_test {
|
test math_test {
|
||||||
srcs tests/math_test.c
|
srcs tests/math_test.c
|
||||||
include include
|
include include
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ let stmt_keywords =
|
|||||||
[ "project"; "set"; "append"; "option"; "include"; "subdir"; "if"; "check"; "pkg";
|
[ "project"; "set"; "append"; "option"; "include"; "subdir"; "if"; "check"; "pkg";
|
||||||
"config_header"; "message" ]
|
"config_header"; "message" ]
|
||||||
|
|
||||||
let block_kinds = [ "toolchain"; "lib"; "shared"; "bin"; "test"; "rule"; "install" ]
|
let block_kinds = [ "toolchain"; "lib"; "shared"; "bin"; "test"; "rule"; "install"; "run" ]
|
||||||
let check_kinds = [ "header"; "func"; "symbol"; "sizeof"; "compiles" ]
|
let check_kinds = [ "header"; "func"; "symbol"; "sizeof"; "compiles" ]
|
||||||
|
|
||||||
let peek p = p.toks.(p.pos)
|
let peek p = p.toks.(p.pos)
|
||||||
|
|||||||
+40
-1
@@ -7,6 +7,7 @@ let target_fields =
|
|||||||
|
|
||||||
let rule_fields = [ "inputs"; "outputs"; "command"; "description" ]
|
let rule_fields = [ "inputs"; "outputs"; "command"; "description" ]
|
||||||
let install_fields = [ "files"; "to" ]
|
let install_fields = [ "files"; "to" ]
|
||||||
|
let script_fields = [ "use"; "command" ]
|
||||||
|
|
||||||
let fields_of (b : block) =
|
let fields_of (b : block) =
|
||||||
List.filter_map (function FField f -> Some f | FIf _ -> None) b.items
|
List.filter_map (function FField f -> Some f | FIf _ -> None) b.items
|
||||||
@@ -156,6 +157,20 @@ let installs_of blocks =
|
|||||||
end)
|
end)
|
||||||
blocks
|
blocks
|
||||||
|
|
||||||
|
let scripts_of blocks =
|
||||||
|
List.concat_map
|
||||||
|
(fun ((b : block), _) ->
|
||||||
|
if b.kind <> "run" then []
|
||||||
|
else begin
|
||||||
|
check_fields b script_fields;
|
||||||
|
let cmd = texts (get b "command") in
|
||||||
|
if cmd = [] then
|
||||||
|
Diag.error ~span:b.nspan ~hint:"add a line like: command ./tool --flag"
|
||||||
|
"run %s declares no command" b.bname;
|
||||||
|
[ { sname = b.bname; suses = texts (get b "use"); scmd = cmd; sspan = b.nspan } ]
|
||||||
|
end)
|
||||||
|
blocks
|
||||||
|
|
||||||
let project (env : Eval.env) =
|
let project (env : Eval.env) =
|
||||||
let blocks = env.blocks in
|
let blocks = env.blocks in
|
||||||
let rules = rules_of blocks in
|
let rules = rules_of blocks in
|
||||||
@@ -168,7 +183,8 @@ let project (env : Eval.env) =
|
|||||||
in
|
in
|
||||||
List.iter
|
List.iter
|
||||||
(fun (b : block) ->
|
(fun (b : block) ->
|
||||||
if not (List.mem b.kind [ "lib"; "shared"; "bin"; "test"; "rule"; "install"; "toolchain" ]) then
|
if not (List.mem b.kind [ "lib"; "shared"; "bin"; "test"; "rule"; "install"; "toolchain"; "run" ])
|
||||||
|
then
|
||||||
Diag.error ~span:b.kspan "unknown block %S" b.kind)
|
Diag.error ~span:b.kspan "unknown block %S" b.kind)
|
||||||
(List.map fst blocks);
|
(List.map fst blocks);
|
||||||
let seen = Hashtbl.create 8 in
|
let seen = Hashtbl.create 8 in
|
||||||
@@ -186,6 +202,7 @@ let project (env : Eval.env) =
|
|||||||
tc = env.tc;
|
tc = env.tc;
|
||||||
targets;
|
targets;
|
||||||
rules;
|
rules;
|
||||||
|
scripts = scripts_of blocks;
|
||||||
defines = env.defines;
|
defines = env.defines;
|
||||||
config_header = env.config_header;
|
config_header = env.config_header;
|
||||||
installs = installs_of blocks;
|
installs = installs_of blocks;
|
||||||
@@ -208,6 +225,28 @@ let project (env : Eval.env) =
|
|||||||
| Some _ -> ())
|
| Some _ -> ())
|
||||||
t.uses)
|
t.uses)
|
||||||
targets;
|
targets;
|
||||||
|
let seen_runs = Hashtbl.create 4 in
|
||||||
|
List.iter
|
||||||
|
(fun (s : script) ->
|
||||||
|
(match Hashtbl.find_opt seen_runs s.sname with
|
||||||
|
| Some (prev : script) ->
|
||||||
|
Diag.error ~span:s.sspan ~notes:[ (prev.sspan, "first declared here") ]
|
||||||
|
"run %S is declared twice" s.sname
|
||||||
|
| None -> Hashtbl.add seen_runs s.sname s);
|
||||||
|
(* meowc run takes one name, so a run block and a target cannot share one *)
|
||||||
|
(match find p s.sname with
|
||||||
|
| Some (t : target) ->
|
||||||
|
Diag.error ~span:s.sspan ~notes:[ (t.span, "the target is here") ]
|
||||||
|
"run %S has the same name as a target" s.sname
|
||||||
|
| None -> ());
|
||||||
|
List.iter
|
||||||
|
(fun u ->
|
||||||
|
if find p u = None then
|
||||||
|
Diag.error ~span:s.sspan
|
||||||
|
~hint:(Suggest.hint u (List.map (fun (x : target) -> x.name) targets))
|
||||||
|
"run %s uses %S, which is not a target" s.sname u)
|
||||||
|
s.suses)
|
||||||
|
p.scripts;
|
||||||
let rec cyc seen t =
|
let rec cyc seen t =
|
||||||
if List.mem t.name seen then
|
if List.mem t.name seen then
|
||||||
Diag.error ~span:t.span "dependency cycle: %s" (String.concat " -> " (List.rev (t.name :: seen)));
|
Diag.error ~span:t.span "dependency cycle: %s" (String.concat " -> " (List.rev (t.name :: seen)));
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ type toolchain = {
|
|||||||
builddir : string;
|
builddir : string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(* A command the project can be asked to run, once whatever it names has been
|
||||||
|
built. Unlike a rule it produces no file, so it is never part of the graph. *)
|
||||||
|
type script = { sname : string; suses : string list; scmd : string list; sspan : Span.t }
|
||||||
|
|
||||||
type install_item = { from : string; dest : string }
|
type install_item = { from : string; dest : string }
|
||||||
|
|
||||||
type define = { dname : string; dvalue : string option }
|
type define = { dname : string; dvalue : string option }
|
||||||
@@ -55,6 +59,7 @@ type project = {
|
|||||||
tc : toolchain;
|
tc : toolchain;
|
||||||
targets : target list;
|
targets : target list;
|
||||||
rules : rule list;
|
rules : rule list;
|
||||||
|
scripts : script list;
|
||||||
defines : define list;
|
defines : define list;
|
||||||
config_header : string option;
|
config_header : string option;
|
||||||
installs : install_item list;
|
installs : install_item list;
|
||||||
|
|||||||
Reference in new issue
Block a user