That can be simplified as well and then run a simple parser over it that takes a simpler representation and turns it into that dict. Then:
def make(rules, rule):
(deps, action) = rules[rule]
run_rule = len(deps) == 0 # if there are no dependencies, the rule always runs
for dep in deps:
make(rules, dep)
if os.stat(rule).st_mtime < os.stat(dep).st_mtime: run_rule = True
if run_rule: action()
Of course, this doesn't actually validate the DAG itself.
----------
An amendment: You'll probably want to pass both the rule name and the dependencies into the action function/lambda/object so that you can parameterize it and maybe reuse the action (like a common compiler command):
----------
An amendment: You'll probably want to pass both the rule name and the dependencies into the action function/lambda/object so that you can parameterize it and maybe reuse the action (like a common compiler command):