2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 OpenEmbedded 'Build' implementation
7 Core code for function execution and task handling in the
8 OpenEmbedded (http://openembedded.org) build infrastructure.
10 Copyright: (c) 2003 Chris Larson
12 Based on functions from the base oe module, Copyright 2003 Holger Schurig
15 from oe import debug, data, fetch, fatal, error, note, event, mkdirhier
18 # data holds flags and function name for a given task
19 _task_data = data.init()
21 # graph represents task interdependencies
22 _task_graph = oe.digraph()
24 # stack represents execution order, excepting dependencies
28 class FuncFailed(Exception):
29 """Executed function failed"""
31 class EventException(Exception):
32 """Exception which is associated with an Event."""
34 def __init__(self, msg, event):
40 def setEvent(self, event):
43 event = property(getEvent, setEvent, None, "event property")
45 class TaskBase(event.Event):
46 """Base class for task events"""
48 def __init__(self, t, d = {}):
55 def setTask(self, task):
58 task = property(getTask, setTask, None, "task property")
63 def setData(self, data):
66 data = property(getData, setData, None, "data property")
68 class TaskStarted(TaskBase):
69 """Task execution started"""
71 class TaskSucceeded(TaskBase):
72 """Task execution completed"""
74 class TaskFailed(TaskBase):
75 """Task execution failed"""
77 class InvalidTask(TaskBase):
83 global _task_data, _task_graph, _task_stack
84 _task_data = data.init()
85 _task_graph = oe.digraph()
89 def exec_func(func, d, dirs = None):
90 """Execute an OE 'function'"""
92 body = data.getVar(func, d)
97 dirs = (data.getVarFlag(func, 'dirs', d) or "").split()
99 adir = data.expand(adir, d)
105 adir = data.getVar('B', d, 1)
107 adir = data.expand(adir, d)
110 prevdir = os.getcwd()
112 prevdir = data.expand('${TOPDIR}', d)
113 if adir and os.access(adir, os.F_OK):
116 if data.getVarFlag(func, "python", d):
117 exec_func_python(func, d)
119 exec_func_shell(func, d)
122 def exec_func_python(func, d):
123 """Execute a python OE 'function'"""
126 tmp = "def " + func + "():\n%s" % data.getVar(func, d)
127 comp = compile(tmp + '\n' + func + '()', oe.data.getVar('FILE', d, 1) + ':' + func, "exec")
128 prevdir = os.getcwd()
134 if os.path.exists(prevdir):
137 def exec_func_shell(func, d):
138 """Execute a shell OE 'function' Returns true if execution was successful.
140 For this, it creates a bash shell script in the tmp dectory, writes the local
141 data into it and finally executes. The output of the shell will end in a log file and stdout.
143 Note on directory behavior. The 'dirs' varflag should contain a list
144 of the directories you need created prior to execution. The last
145 item in the list is where we will chdir/cd to.
149 deps = data.getVarFlag(func, 'deps', d)
150 check = data.getVarFlag(func, 'check', d)
151 if check in globals():
152 if globals()[check](func, deps):
156 t = data.getVar('T', d, 1)
160 logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
161 runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
163 f = open(runfile, "w")
164 f.write("#!/bin/sh -e\n")
165 if data.getVar("OEDEBUG", d): f.write("set -x\n")
168 f.write("cd %s\n" % os.getcwd())
169 if func: f.write("%s\n" % func)
171 os.chmod(runfile, 0775)
173 error("Function not specified")
177 si = file('/dev/null', 'r')
179 if data.getVar("OEDEBUG", d):
180 so = os.popen("tee \"%s\"" % logfile, "w")
182 so = file(logfile, 'w')
184 oe.error("opening log file: %s" % e)
189 # dup the existing fds so we dont lose them
190 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
191 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
192 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
194 # replace those fds with our own
195 os.dup2(si.fileno(), osi[1])
196 os.dup2(so.fileno(), oso[1])
197 os.dup2(se.fileno(), ose[1])
200 prevdir = os.getcwd()
201 if data.getVarFlag(func, "fakeroot", d):
202 maybe_fakeroot = "PATH=\"%s\" fakeroot " % oe.data.getVar("PATH", d, 1)
205 ret = os.system('%ssh -e %s' % (maybe_fakeroot, runfile))
208 # restore the backups
209 os.dup2(osi[0], osi[1])
210 os.dup2(oso[0], oso[1])
211 os.dup2(ose[0], ose[1])
218 # close the backup fds
224 if not data.getVar("OEDEBUG", d):
229 error("function %s failed" % func)
230 if data.getVar("OEINCLUDELOGS", d):
231 error("log data follows (%s)" % logfile)
232 f = open(logfile, "r")
241 error("see log in %s" % logfile)
247 def exec_task(task, d):
248 """Execute an OE 'task'
250 The primary difference between executing a task versus executing
251 a function is that a task exists in the task digraph, and therefore
252 has dependencies amongst other tasks."""
254 # check if the task is in the graph..
255 task_graph = data.getVar('_task_graph', d)
257 task_graph = oe.digraph()
258 data.setVar('_task_graph', task_graph, d)
259 task_cache = data.getVar('_task_cache', d)
262 data.setVar('_task_cache', task_cache, d)
263 if not task_graph.hasnode(task):
264 raise EventException("", InvalidTask(task, d))
266 # check whether this task needs executing..
267 if not data.getVarFlag(task, 'force', d):
268 if stamp_is_current(task, d):
271 # follow digraph path up, then execute our way back down
272 def execute(graph, item):
273 if data.getVarFlag(item, 'task', d):
274 if item in task_cache:
278 # deeper than toplevel, exec w/ deps
283 debug(1, "Executing task %s" % item)
284 event.fire(TaskStarted(item, d))
285 from copy import deepcopy
286 localdata = deepcopy(d)
287 oe.data.setVar('OVERRIDES', "%s:%s" % (item.replace('_', '-'), oe.data.getVar('OVERRIDES', localdata)), localdata)
288 oe.data.update_data(localdata)
289 exec_func(item, localdata)
290 event.fire(TaskSucceeded(item, d))
291 task_cache.append(item)
292 except FuncFailed, reason:
293 note( "Task failed: %s" % reason )
294 failedevent = TaskFailed(item, d)
295 event.fire(failedevent)
296 raise EventException(None, failedevent)
299 task_graph.walkdown(task, execute)
301 # make stamp, or cause event and raise exception
302 if not data.getVarFlag(task, 'nostamp', d):
306 def stamp_is_current(task, d, checkdeps = 1):
307 """Check status of a given task's stamp. returns 0 if it is not current and needs updating."""
308 task_graph = data.getVar('_task_graph', d)
310 task_graph = oe.digraph()
311 data.setVar('_task_graph', task_graph, d)
312 stamp = data.getVar('STAMP', d)
315 stampfile = "%s.%s" % (data.expand(stamp, d), task)
316 if not os.access(stampfile, os.F_OK):
323 tasktime = os.stat(stampfile)[stat.ST_MTIME]
326 def checkStamp(graph, task):
327 # check for existance
328 if data.getVarFlag(task, 'nostamp', d):
331 if not stamp_is_current(task, d, 0):
334 depfile = "%s.%s" % (data.expand(stamp, d), task)
335 deptime = os.stat(depfile)[stat.ST_MTIME]
336 if deptime > tasktime:
340 return task_graph.walkdown(task, checkStamp)
343 def md5_is_current(task):
344 """Check if a md5 file for a given task is current"""
347 def mkstamp(task, d):
348 """Creates/updates a stamp for a given task"""
349 mkdirhier(data.expand('${TMPDIR}/stamps', d));
350 stamp = data.getVar('STAMP', d)
353 stamp = "%s.%s" % (data.expand(stamp, d), task)
357 def add_task(task, deps, d):
358 task_graph = data.getVar('_task_graph', d)
360 task_graph = oe.digraph()
361 data.setVar('_task_graph', task_graph, d)
362 data.setVarFlag(task, 'task', 1, d)
363 task_graph.addnode(task, None)
365 if not task_graph.hasnode(dep):
366 task_graph.addnode(dep, None)
367 task_graph.addnode(task, dep)
370 def remove_task(task, kill, d):
371 """Remove an OE 'task'.
373 If kill is 1, also remove tasks that depend on this task."""
375 task_graph = data.getVar('_task_graph', d)
377 task_graph = oe.digraph()
378 data.setVar('_task_graph', task_graph, d)
379 if not task_graph.hasnode(task):
382 data.delVarFlag(task, 'task', d)
386 task_graph.delnode(task, ref)
388 def task_exists(task, d):
389 task_graph = data.getVar('_task_graph', d)
391 task_graph = oe.digraph()
392 data.setVar('_task_graph', task_graph, d)
393 return task_graph.hasnode(task)