(* 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 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