3 OpenEmbedded 'Build' implementation
5 Core code for function execution and task handling in the
6 OpenEmbedded (http://openembedded.org) build infrastructure.
8 Copyright: (c) 2003 Chris Larson
10 Based on functions from the base oe module, Copyright 2003 Holger Schurig
13 from oe import debug, data, fetch, fatal, error, note, event, mkdirhier
16 # data holds flags and function name for a given task
17 _task_data = data.init()
19 # graph represents task interdependencies
20 _task_graph = oe.digraph()
22 # stack represents execution order, excepting dependencies
26 class FuncFailed(Exception):
27 """Executed function failed"""
29 class EventException(Exception):
30 """Exception which is associated with an Event."""
32 def __init__(self, msg, event):
38 def setEvent(self, event):
41 event = property(getEvent, setEvent, None, "event property")
43 class TaskBase(event.Event):
44 """Base class for task events"""
46 def __init__(self, t, d = {}):
53 def setTask(self, task):
56 task = property(getTask, setTask, None, "task property")
61 def setData(self, data):
64 data = property(getData, setData, None, "data property")
66 class TaskStarted(TaskBase):
67 """Task execution started"""
69 class TaskSucceeded(TaskBase):
70 """Task execution completed"""
72 class TaskFailed(TaskBase):
73 """Task execution failed"""
75 class InvalidTask(TaskBase):
81 global _task_data, _task_graph, _task_stack
82 _task_data = data.init()
83 _task_graph = oe.digraph()
87 def exec_func(func, d, dirs = None):
88 """Execute an OE 'function'"""
91 dirs = string.split(data.getVarFlag(func, 'dirs', d) or "")
93 adir = data.expand(adir, d)
99 adir = data.getVar('S', d)
101 adir = data.expand(adir, d)
104 prevdir = os.getcwd()
106 prevdir = data.expand('${TOPDIR}', d)
107 if adir and os.access(adir, os.F_OK):
110 if data.getVarFlag(func, "python", d):
111 exec_func_python(func, d)
113 exec_func_shell(func, d)
116 def exec_func_python(func, d):
117 """Execute a python OE 'function'"""
120 body = data.getVar(func, d)
123 tmp = "def " + func + "():\n%s" % body
124 comp = compile(tmp + '\n' + func + '()', oe.data.getVar('FILE', d, 1) + ':' + func, "exec")
125 prevdir = os.getcwd()
133 def exec_func_shell(func, d):
134 """Execute a shell OE 'function' Returns true if execution was successful.
136 For this, it creates a bash shell script in the tmp dectory, writes the local
137 data into it and finally executes. The output of the shell will end in a log file and stdout.
139 Note on directory behavior. The 'dirs' varflag should contain a list
140 of the directories you need created prior to execution. The last
141 item in the list is where we will chdir/cd to.
145 deps = data.getVarFlag(func, 'deps', d)
146 check = data.getVarFlag(func, 'check', d)
147 if check in globals():
148 if globals()[check](func, deps):
152 t = data.getVar('T', d, 1)
154 oe.error("T variable not set")
157 logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
158 runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
160 f = open(runfile, "w")
161 f.write("#!/bin/sh -e\n")
162 if data.getVar("OEDEBUG", d): f.write("set -x\n")
165 f.write("cd %s\n" % os.getcwd())
166 if func: f.write("%s || exit $?\n" % func)
168 os.chmod(runfile, 0775)
170 error("Function not specified")
174 si = file('/dev/null', 'r')
175 so = file(logfile, 'a')
176 se = file(logfile, 'a+', 0)
178 # dup the existing fds so we dont lose them
179 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
180 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
181 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
183 # replace those fds with our own
184 os.dup2(si.fileno(), osi[1])
185 os.dup2(so.fileno(), oso[1])
186 os.dup2(se.fileno(), ose[1])
189 prevdir = os.getcwd()
190 if data.getVarFlag(func, "fakeroot", d):
191 maybe_fakeroot = oe.data.expand("${STAGING_BINDIR}/fakeroot ",d)
194 ret = os.system('%ssh -e %s' % (maybe_fakeroot, runfile))
197 # restore the backups
198 os.dup2(osi[0], osi[1])
199 os.dup2(oso[0], oso[1])
200 os.dup2(ose[0], ose[1])
207 # close the backup fds
213 if not data.getVar("OEDEBUG"):
218 error("function %s failed" % func)
219 error("see log in %s" % logfile)
225 def exec_task(task, d):
226 """Execute an OE 'task'
228 The primary difference between executing a task versus executing
229 a function is that a task exists in the task digraph, and therefore
230 has dependencies amongst other tasks."""
232 # check if the task is in the graph..
233 task_graph = data.getVar('_task_graph', d)
235 task_graph = oe.digraph()
236 data.setVar('_task_graph', task_graph, d)
237 task_cache = data.getVar('_task_cache', d)
240 data.setVar('_task_cache', task_cache, d)
241 if not task_graph.hasnode(task):
242 raise EventException("", InvalidTask(task, d))
244 # check whether this task needs executing..
245 if not data.getVarFlag(task, 'force', d):
246 if stamp_is_current(task, d):
249 # follow digraph path up, then execute our way back down
250 def execute(graph, item):
251 if data.getVarFlag(item, 'task', d):
252 if item in task_cache:
256 # deeper than toplevel, exec w/ deps
261 debug(1, "Executing task %s" % item)
262 event.fire(TaskStarted(item, d))
264 event.fire(TaskSucceeded(item, d))
265 task_cache.append(item)
266 except FuncFailed, reason:
267 note( "Task failed: %s" % reason )
268 failedevent = TaskFailed(item, d)
269 event.fire(failedevent)
270 raise EventException(None, failedevent)
273 task_graph.walkdown(task, execute)
275 # make stamp, or cause event and raise exception
276 if not data.getVarFlag(task, 'nostamp', d):
280 def stamp_is_current(task, d, checkdeps = 1):
281 """Check status of a given task's stamp. returns 0 if it is not current and needs updating."""
282 task_graph = data.getVar('_task_graph', d)
284 task_graph = oe.digraph()
285 data.setVar('_task_graph', task_graph, d)
286 stamp = data.getVar('STAMP', d)
289 stampfile = "%s.%s" % (data.expand(stamp, d), task)
290 if not os.access(stampfile, os.F_OK):
297 tasktime = os.stat(stampfile)[stat.ST_MTIME]
300 def checkStamp(graph, task):
301 # check for existance
302 if data.getVarFlag(task, 'nostamp', d):
305 if not stamp_is_current(task, d, 0):
308 depfile = "%s.%s" % (data.expand(stamp, d), task)
309 deptime = os.stat(depfile)[stat.ST_MTIME]
310 if deptime > tasktime:
314 return task_graph.walkdown(task, checkStamp)
317 def md5_is_current(task):
318 """Check if a md5 file for a given task is current"""
321 def mkstamp(task, d):
322 """Creates/updates a stamp for a given task"""
323 mkdirhier(data.expand('${TMPDIR}/stamps', d));
324 stamp = data.getVar('STAMP', d)
327 stamp = "%s.%s" % (data.expand(stamp, d), task)
331 def add_task(task, deps, d):
332 task_graph = data.getVar('_task_graph', d)
334 task_graph = oe.digraph()
335 data.setVar('_task_graph', task_graph, d)
336 data.setVarFlag(task, 'task', 1, d)
337 task_graph.addnode(task, None)
339 if not task_graph.hasnode(dep):
340 task_graph.addnode(dep, None)
341 task_graph.addnode(task, dep)
344 def remove_task(task, kill, d):
345 """Remove an OE 'task'.
347 If kill is 1, also remove tasks that depend on this task."""
349 task_graph = data.getVar('_task_graph', d)
351 task_graph = oe.digraph()
352 data.setVar('_task_graph', task_graph, d)
353 if not task_graph.hasnode(task):
356 data.delVarFlag(task, 'task', d)
360 task_graph.delnode(task, ref)
362 def task_exists(task, d):
363 task_graph = data.getVar('_task_graph', d)
365 task_graph = oe.digraph()
366 data.setVar('_task_graph', task_graph, d)
367 return task_graph.hasnode(task)