build: Fix 'goals clean' rule.
[goals.git] / src / run.ml
index 7d48a7e..0290124 100644 (file)
@@ -30,7 +30,7 @@ and run_target env = function
   (* Call a goal. *)
   | Ast.ECallGoal (loc, name, args) ->
      let goal = Ast.getgoal env loc name in
-     run_goal env loc name args goal
+     run_goal env loc name args goal []
 
   (* Call a tactic. *)
   | Ast.ETacticCtor (loc, name, args) ->
@@ -68,9 +68,12 @@ and run_target env = function
      run_tactic env loc "*file" [c]
 
 (* Run a goal by name. *)
-and run_goal env loc name args (params, patterns, deps, code) =
-  Cmdline.debug "%a: running goal %s %a"
-    Ast.string_loc loc name Ast.string_expr (Ast.EList (Ast.noloc, args));
+and run_goal env loc name args (params, patterns, deps, code) extra_deps =
+  (* This is used to print the goal in debug and error messages only. *)
+  let debug_goal =
+    sprintf "%s (%s)" name
+      (String.concat ", " (List.map (Ast.string_expr ()) args)) in
+  Cmdline.debug "%a: running goal %s" Ast.string_loc loc debug_goal;
 
   (* This is the point where we evaluate the goal arguments.  We must
    * do this before creating the new environment, because variables
@@ -85,18 +88,20 @@ and run_goal env loc name args (params, patterns, deps, code) =
     let params =
       try List.combine params args
       with Invalid_argument _ ->
-        failwithf "%a: calling goal ā€˜%sā€™ with wrong number of arguments"
-          Ast.string_loc loc name in
+        failwithf "%a: calling goal %s with wrong number of arguments, expecting %d args but got %d args"
+          Ast.string_loc loc debug_goal
+          (List.length params) (List.length args) in
     List.fold_left (fun env (k, v) -> Ast.Env.add k v env) env params in
 
   (* Check all dependencies have been updated. *)
-  run_targets env deps;
+  run_targets env (deps @ extra_deps);
 
   (* Check if any target (ie. pattern) needs to be rebuilt.
    * As with make, a goal with no targets is always run.
    *)
   let rebuild =
-    patterns = [] || List.exists (needs_rebuild env loc deps) patterns in
+    patterns = [] ||
+    List.exists (needs_rebuild env loc deps extra_deps) patterns in
 
   if rebuild then (
     (* Run the code (if any). *)
@@ -114,6 +119,7 @@ and run_goal env loc name args (params, patterns, deps, code) =
         let env = Ast.Env.add "@" (Ast.EList (Ast.noloc, pexprs)) env in
         let env = Ast.Env.add "<" (Ast.EList (Ast.noloc, deps)) env in
         let env =
+          (* NB: extra_deps are not added to %^ *)
           match deps with
           | [] -> env
           | d :: _ -> Ast.Env.add "^" d env in
@@ -129,20 +135,20 @@ and run_goal env loc name args (params, patterns, deps, code) =
          * run (else it's an error).
          *)
         let pattern_still_needs_rebuild =
-          try Some (List.find (needs_rebuild env loc deps) patterns)
-          with Not_found -> None in
+          try
+            Some (List.find (needs_rebuild env loc deps extra_deps) patterns)
+          with
+            Not_found -> None in
         match pattern_still_needs_rebuild with
         | None -> ()
         | Some pattern ->
-           failwithf "%a: goal ā€˜%sā€™ ran successfully but it did not rebuild %a"
-             Ast.string_loc loc
-             name
-             Ast.string_pattern pattern
+           failwithf "%a: goal %s ran successfully but it did not rebuild %a"
+             Ast.string_loc loc debug_goal Ast.string_pattern pattern
     )
   )
 
 (* Return whether the target (pattern) needs to be rebuilt. *)
-and needs_rebuild env loc deps pattern =
+and needs_rebuild env loc deps extra_deps pattern =
   Cmdline.debug "%a: testing if %a needs rebuild"
     Ast.string_loc loc Ast.string_pattern pattern;
 
@@ -174,11 +180,12 @@ and needs_rebuild env loc deps pattern =
      (* Add some standard variables to the environment. *)
      let env = Ast.Env.add "<" (Ast.EList (Ast.noloc, deps)) env in
      let env =
+       (* NB: extra_deps are not added to %^ *)
        match deps with
        | [] -> env
        | d :: _ -> Ast.Env.add "^" d env in
      let code = Eval.to_shell_script env loc code in
-     let code = "set -e\nset -x\n\n" ^ code in
+     let code = "set -e\n" (*^ "set -x\n"*) ^ "\n" ^ code in
      let r = Sys.command code in
      if r = 99 (* means "needs rebuild" *) then true
      else if r = 0 (* means "doesn't need rebuild" *) then false
@@ -191,7 +198,12 @@ and needs_rebuild env loc deps pattern =
  * cargs is a list of parameters (all constants).
  *)
 and run_tactic env loc tactic cargs =
-  Cmdline.debug "%a: running tactic %s" Ast.string_loc loc tactic;
+  (* This is used to print the tactic in debug and error messages only. *)
+  let debug_tactic =
+    Ast.string_expr ()
+      (Ast.ETacticCtor (loc, tactic,
+                        List.map (fun c -> Ast.EConstant (loc, c)) cargs)) in
+  Cmdline.debug "%a: running tactic %s" Ast.string_loc loc debug_tactic;
 
   (* Find all goals in the environment.  Returns a list of (name, goal). *)
   let goals =
@@ -226,22 +238,50 @@ and run_tactic env loc tactic cargs =
       *)
      let targs = List.map (function Ast.CString s -> [Ast.SString s]) cargs in
      let p = Ast.PTactic (loc, tactic, targs) in
-     if needs_rebuild env loc [] p then (
-       let t = Ast.ETacticCtor (loc, tactic,
-                                List.map (fun c -> Ast.EConstant (loc, c))
-                                  cargs) in
-       failwithf "%a: don't know how to build %a"
-         Ast.string_loc loc Ast.string_expr t
-     )
+     if needs_rebuild env loc [] [] p then
+       failwithf "%a: don't know how to build %s"
+         Ast.string_loc loc debug_tactic
+
+  | [_, name, goal, args] ->
+     (* Single goal matches, run it. *)
+     run_goal env loc name args goal []
 
   | goals ->
-     (* One or more goals match.  We run them all (although if
-      * one of them succeeds in rebuilding, it will cut short the rest).
+     (* Two or more goals match.  Only one must have a CODE section,
+      * and we combine the dependencies into a "supergoal".
       *)
-     List.iter (
-       fun (_, name, goal, args) ->
-         run_goal env loc name args goal
-     ) goals
+     let with_code, without_code =
+       List.partition (
+         fun (_, _, (_, _, _, code), _) -> code <> None
+       ) goals in
+
+     let (_, name, goal, args), extra_deps =
+       match with_code with
+       | [g] ->
+          let extra_deps =
+            List.flatten (
+              List.map (fun (_, _, (_, _, deps, _), _) -> deps) without_code
+            ) in
+          (g, extra_deps)
+
+       | [] ->
+          (* This is OK, it means we'll rebuild all dependencies
+           * but there is no code to run.  Pick the first goal
+           * without code and the dependencies from the other goals.
+           *)
+          let g = List.hd without_code in
+          let extra_deps =
+            List.flatten (
+              List.map (fun (_, _, (_, _, deps, _), _) -> deps)
+                (List.tl without_code)
+            ) in
+          (g, extra_deps)
+
+       | _ :: _ ->
+          failwithf "%a: multiple goals found which match tactic %s, but more than one of these goals have {code} sections which is not allowed"
+            Ast.string_loc loc debug_tactic in
+
+     run_goal env loc name args goal extra_deps
 
 (* Test if pattern matches *tactic(cargs).  If it does
  * then we return Some args where args is the arguments that must