2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 BitBake 'Build' implementation
7 Core code for function execution and task handling in the
10 Copyright (C) 2003, 2004 Chris Larson
12 Based on Gentoo's portage.py.
14 This program is free software; you can redistribute it and/or modify it under
15 the terms of the GNU General Public License as published by the Free Software
16 Foundation; either version 2 of the License, or (at your option) any later
19 This program is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License along with
25 Based on functions from the base bb module, Copyright 2003 Holger Schurig
28 from bb import data, fetch, fatal, error, note, event, mkdirhier, utils
32 class FuncFailed(Exception):
33 """Executed function failed"""
35 class EventException(Exception):
36 """Exception which is associated with an Event."""
38 def __init__(self, msg, event):
39 self.args = msg, event
41 class TaskBase(event.Event):
42 """Base class for task events"""
44 def __init__(self, t, d ):
46 event.Event.__init__(self, d)
51 def setTask(self, task):
54 task = property(getTask, setTask, None, "task property")
56 class TaskStarted(TaskBase):
57 """Task execution started"""
59 class TaskSucceeded(TaskBase):
60 """Task execution completed"""
62 class TaskFailed(TaskBase):
63 """Task execution failed"""
65 class InvalidTask(TaskBase):
70 def exec_func(func, d, dirs = None):
71 """Execute an BB 'function'"""
73 body = data.getVar(func, d)
78 dirs = (data.getVarFlag(func, 'dirs', d) or "").split()
80 adir = data.expand(adir, d)
86 adir = data.getVar('B', d, 1)
88 adir = data.expand(adir, d)
93 prevdir = data.expand('${TOPDIR}', d)
94 if adir and os.access(adir, os.F_OK):
97 if data.getVarFlag(func, "python", d):
98 exec_func_python(func, d)
100 exec_func_shell(func, d)
102 if os.path.exists(prevdir):
105 def exec_func_python(func, d):
106 """Execute a python BB 'function'"""
109 tmp = "def " + func + "():\n%s" % data.getVar(func, d)
110 tmp += '\n' + func + '()'
111 comp = utils.better_compile(tmp, func, bb.data.getVar('FILE', d, 1) )
112 prevdir = os.getcwd()
117 utils.better_exec(comp,g,tmp, bb.data.getVar('FILE',d,1))
118 if os.path.exists(prevdir):
121 def exec_func_shell(func, d):
122 """Execute a shell BB 'function' Returns true if execution was successful.
124 For this, it creates a bash shell script in the tmp dectory, writes the local
125 data into it and finally executes. The output of the shell will end in a log file and stdout.
127 Note on directory behavior. The 'dirs' varflag should contain a list
128 of the directories you need created prior to execution. The last
129 item in the list is where we will chdir/cd to.
133 deps = data.getVarFlag(func, 'deps', d)
134 check = data.getVarFlag(func, 'check', d)
135 if check in globals():
136 if globals()[check](func, deps):
140 t = data.getVar('T', d, 1)
144 logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
145 runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
147 f = open(runfile, "w")
148 f.write("#!/bin/sh -e\n")
149 if bb.msg.debug_level > 0: f.write("set -x\n")
152 f.write("cd %s\n" % os.getcwd())
153 if func: f.write("%s\n" % func)
155 os.chmod(runfile, 0775)
157 error("Function not specified")
161 si = file('/dev/null', 'r')
163 if bb.msg.debug_level > 0:
164 so = os.popen("tee \"%s\"" % logfile, "w")
166 so = file(logfile, 'w')
168 bb.error("opening log file: %s" % e)
173 # dup the existing fds so we dont lose them
174 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
175 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
176 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
178 # replace those fds with our own
179 os.dup2(si.fileno(), osi[1])
180 os.dup2(so.fileno(), oso[1])
181 os.dup2(se.fileno(), ose[1])
184 prevdir = os.getcwd()
185 if data.getVarFlag(func, "fakeroot", d):
186 maybe_fakeroot = "PATH=\"%s\" fakeroot " % bb.data.getVar("PATH", d, 1)
189 ret = os.system('%ssh -e %s' % (maybe_fakeroot, runfile))
195 # restore the backups
196 os.dup2(osi[0], osi[1])
197 os.dup2(oso[0], oso[1])
198 os.dup2(ose[0], ose[1])
205 # close the backup fds
211 if bb.msg.debug_level > 0:
216 error("function %s failed" % func)
217 if data.getVar("BBINCLUDELOGS", d):
218 error("log data follows (%s)" % logfile)
219 f = open(logfile, "r")
228 error("see log in %s" % logfile)
229 raise FuncFailed( logfile )
232 def exec_task(task, d):
233 """Execute an BB 'task'
235 The primary difference between executing a task versus executing
236 a function is that a task exists in the task digraph, and therefore
237 has dependencies amongst other tasks."""
239 # check if the task is in the graph..
240 task_graph = data.getVar('_task_graph', d)
242 task_graph = bb.digraph()
243 data.setVar('_task_graph', task_graph, d)
244 task_cache = data.getVar('_task_cache', d)
247 data.setVar('_task_cache', task_cache, d)
248 if not task_graph.hasnode(task):
249 raise EventException("Missing node in task graph", InvalidTask(task, d))
251 # check whether this task needs executing..
252 if not data.getVarFlag(task, 'force', d):
253 if stamp_is_current(task, d):
256 # follow digraph path up, then execute our way back down
257 def execute(graph, item):
258 if data.getVarFlag(item, 'task', d):
259 if item in task_cache:
263 # deeper than toplevel, exec w/ deps
268 bb.msg.debug(1, bb.msg.domain.Build, "Executing task %s" % item)
269 old_overrides = data.getVar('OVERRIDES', d, 0)
270 localdata = data.createCopy(d)
271 data.setVar('OVERRIDES', 'task_%s:%s' % (item, old_overrides), localdata)
272 data.update_data(localdata)
273 event.fire(TaskStarted(item, localdata))
274 exec_func(item, localdata)
275 event.fire(TaskSucceeded(item, localdata))
276 task_cache.append(item)
277 data.setVar('_task_cache', task_cache, d)
278 except FuncFailed, reason:
279 note( "Task failed: %s" % reason )
280 failedevent = TaskFailed(item, d)
281 event.fire(failedevent)
282 raise EventException("Function failed in task: %s" % reason, failedevent)
285 task_graph.walkdown(task, execute)
287 # make stamp, or cause event and raise exception
288 if not data.getVarFlag(task, 'nostamp', d):
292 def stamp_is_current(task, d, checkdeps = 1):
293 """Check status of a given task's stamp. returns 0 if it is not current and needs updating."""
294 task_graph = data.getVar('_task_graph', d)
296 task_graph = bb.digraph()
297 data.setVar('_task_graph', task_graph, d)
298 stamp = data.getVar('STAMP', d)
301 stampfile = "%s.%s" % (data.expand(stamp, d), task)
302 if not os.access(stampfile, os.F_OK):
309 tasktime = os.stat(stampfile)[stat.ST_MTIME]
312 def checkStamp(graph, task):
313 # check for existance
314 if data.getVarFlag(task, 'nostamp', d):
317 if not stamp_is_current(task, d, 0):
320 depfile = "%s.%s" % (data.expand(stamp, d), task)
321 deptime = os.stat(depfile)[stat.ST_MTIME]
322 if deptime > tasktime:
326 return task_graph.walkdown(task, checkStamp)
329 def md5_is_current(task):
330 """Check if a md5 file for a given task is current"""
333 def mkstamp(task, d):
334 """Creates/updates a stamp for a given task"""
335 stamp = data.getVar('STAMP', d)
338 stamp = "%s.%s" % (data.expand(stamp, d), task)
339 mkdirhier(os.path.dirname(stamp))
343 def add_task(task, deps, d):
344 task_graph = data.getVar('_task_graph', d)
346 task_graph = bb.digraph()
347 data.setVarFlag(task, 'task', 1, d)
348 task_graph.addnode(task, None)
350 if not task_graph.hasnode(dep):
351 task_graph.addnode(dep, None)
352 task_graph.addnode(task, dep)
353 # don't assume holding a reference
354 data.setVar('_task_graph', task_graph, d)
357 def remove_task(task, kill, d):
358 """Remove an BB 'task'.
360 If kill is 1, also remove tasks that depend on this task."""
362 task_graph = data.getVar('_task_graph', d)
364 task_graph = bb.digraph()
365 if not task_graph.hasnode(task):
368 data.delVarFlag(task, 'task', d)
372 task_graph.delnode(task, ref)
373 data.setVar('_task_graph', task_graph, d)
375 def task_exists(task, d):
376 task_graph = data.getVar('_task_graph', d)
378 task_graph = bb.digraph()
379 data.setVar('_task_graph', task_graph, d)
380 return task_graph.hasnode(task)