(* 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 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 name = let jobnames = try StringMap.find name t.dependencies with Not_found -> [] in 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 = try StringMap.find jobname t.jobmap with Not_found -> assert false 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