(* whenjobs
* Copyright (C) 2012 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
open Whenutils
open Whenexpr
type t = {
(* Variables. *)
variables : variables;
(* Loaded jobs. *)
jobs : job list;
(*--- below here is "internal" state ---*)
jobmap : job StringMap.t; (* job name -> job structure *)
(* Map variable names to jobs which depend on that variable. This
* gives us a quick way to tell which jobs might need to be reevaluated
* when a variable is set.
*)
dependencies : string list StringMap.t; (* variable -> list of job names *)
(* For each job, if it has run, we store the previous variables
* at that time. This is used to implement {i previous}, {i changes} etc.
*)
prev_variables : variables StringMap.t; (* job name -> variables *)
(* For each job, if it has been evaluated before (see {!job_evaluate})
* then we store the previous result of evaluation here. This is
* used to implement edge-triggering.
*)
prev_eval_result : bool StringMap.t; (* job name -> bool *)
}
let empty = {
variables = StringMap.empty;
jobs = [];
jobmap = StringMap.empty;
dependencies = StringMap.empty;
prev_variables = StringMap.empty;
prev_eval_result = StringMap.empty;
}
let add_job t job =
let deps = dependencies_of_job job in
let dependencies' = List.fold_left (
fun map d ->
let names = try StringMap.find d map with Not_found -> [] in
StringMap.add d (job.job_name :: names) map
) t.dependencies deps in
{ t with
jobs = job :: t.jobs;
jobmap = StringMap.add job.job_name job t.jobmap;
dependencies = dependencies'
}
let set_variable t name value =
{ t with variables = StringMap.add name value t.variables }
let copy_variables old t =
{ t with variables = StringMap.fold StringMap.add old.variables t.variables }
let copy_prev_state old t =
let is_explicit jobname =
String.length jobname < 4 || String.sub jobname 0 4 <> "job$"
in
let prev_variables = StringMap.fold (
fun jobname _ map ->
try
if not (is_explicit jobname) then raise Not_found;
(* See if we can find a job with the same name in the old state. *)
let old_vars = StringMap.find jobname old.prev_variables in
StringMap.add jobname old_vars map
with
Not_found -> map
) t.jobmap t.prev_variables in
let prev_eval_result = StringMap.fold (
fun jobname _ map ->
try
if not (is_explicit jobname) then raise Not_found;
(* See if we can find a job with the same name in the old state. *)
let old_result = StringMap.find jobname old.prev_eval_result in
StringMap.add jobname old_result map
with
Not_found -> map
) t.jobmap t.prev_eval_result in
{ t with
prev_variables = prev_variables; prev_eval_result = prev_eval_result }
let get_variable t name =
try StringMap.find name t.variables with Not_found -> T_string ""
let get_variables t =
StringMap.fold (
fun name value xs ->
if value <> T_string "" then (name, value) :: xs else xs
) t.variables []
let get_variable_names t =
StringMap.fold (
fun name value xs -> if value <> T_string "" then name :: xs else xs
) t.variables []
let nr_jobs t = List.length t.jobs
let get_dependencies t names =
(* Get all job names that depend on these variables. *)
let jobnames =
List.map (
fun name ->
try StringMap.find name t.dependencies with Not_found -> []
) names in
(* Flatten the list and remove duplicates. *)
let set = List.fold_left (
fun set jn -> StringSet.add jn set
) StringSet.empty (List.flatten jobnames) in
let jobnames = StringSet.elements set in
(* Convert job names to jobs. *)
List.map (fun jn ->
try
let j = StringMap.find jn t.jobmap in
(* If this asserts false, then there is a bug in {!add_job}. *)
assert (match j.job_cond with When_job _ -> true | _ -> false);
j
with Not_found ->
(* This should never happen. It would indicate some bug in the
* {!add_job} function.
*)
assert false
) jobnames
let get_whenjobs t =
List.filter (function { job_cond = When_job _ } -> true | _ -> false) t.jobs
let get_everyjobs t =
List.filter (function { job_cond = Every_job _ } -> true | _ -> false) t.jobs
let get_job t jobname =
StringMap.find jobname t.jobmap
let get_job_names t =
List.map (function { job_name = name } -> name) t.jobs
let evaluate_whenjob ?(onload = false) t job =
match job with
| { job_cond = Every_job _ } -> assert false
| { job_cond = When_job whenexpr; job_name = jobname } ->
let prev_variables =
try Some (StringMap.find jobname t.prev_variables)
with Not_found -> None in
let result =
eval_whenexpr_as_bool t.variables prev_variables onload whenexpr in
let prev_eval_result =
try Some (StringMap.find jobname t.prev_eval_result)
with Not_found -> None in
let t = { t with prev_eval_result =
StringMap.add jobname result t.prev_eval_result } in
(* Because jobs are edge-triggered, we're only interested in the
* case where the evaluation state changes from false -> true.
*)
match prev_eval_result, result with
| None, false
| Some false, false
| Some true, true
| Some true, false ->
false, t
| None, true
| Some false, true ->
let t = { t with prev_variables =
StringMap.add jobname t.variables t.prev_variables } in
true, t