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 debug, 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.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.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))
192 # restore the backups
193 os.dup2(osi[0], osi[1])
194 os.dup2(oso[0], oso[1])
195 os.dup2(ose[0], ose[1])
202 # close the backup fds
208 if bb.debug_level > 0:
213 error("function %s failed" % func)
214 if data.getVar("BBINCLUDELOGS", d):
215 error("log data follows (%s)" % logfile)
216 f = open(logfile, "r")
225 error("see log in %s" % logfile)
226 raise FuncFailed( logfile )
229 def exec_task(task, d):
230 """Execute an BB 'task'
232 The primary difference between executing a task versus executing
233 a function is that a task exists in the task digraph, and therefore
234 has dependencies amongst other tasks."""
236 # check if the task is in the graph..
237 task_graph = data.getVar('_task_graph', d)
239 task_graph = bb.digraph()
240 data.setVar('_task_graph', task_graph, d)
241 task_cache = data.getVar('_task_cache', d)
244 data.setVar('_task_cache', task_cache, d)
245 if not task_graph.hasnode(task):
246 raise EventException("Missing node in task graph", InvalidTask(task, d))
248 # check whether this task needs executing..
249 if not data.getVarFlag(task, 'force', d):
250 if stamp_is_current(task, d):
253 # follow digraph path up, then execute our way back down
254 def execute(graph, item):
255 if data.getVarFlag(item, 'task', d):
256 if item in task_cache:
260 # deeper than toplevel, exec w/ deps
265 debug(1, "Executing task %s" % item)
266 old_overrides = data.getVar('OVERRIDES', d, 0)
267 localdata = data.createCopy(d)
268 data.setVar('OVERRIDES', 'task_%s:%s' % (item, old_overrides), localdata)
269 data.update_data(localdata)
270 event.fire(TaskStarted(item, localdata))
271 exec_func(item, localdata)
272 event.fire(TaskSucceeded(item, localdata))
273 task_cache.append(item)
274 data.setVar('_task_cache', task_cache, d)
275 except FuncFailed, reason:
276 note( "Task failed: %s" % reason )
277 failedevent = TaskFailed(item, d)
278 event.fire(failedevent)
279 raise EventException("Function failed in task: %s" % reason, failedevent)
282 task_graph.walkdown(task, execute)
284 # make stamp, or cause event and raise exception
285 if not data.getVarFlag(task, 'nostamp', d):
289 def stamp_is_current(task, d, checkdeps = 1):
290 """Check status of a given task's stamp. returns 0 if it is not current and needs updating."""
291 task_graph = data.getVar('_task_graph', d)
293 task_graph = bb.digraph()
294 data.setVar('_task_graph', task_graph, d)
295 stamp = data.getVar('STAMP', d)
298 stampfile = "%s.%s" % (data.expand(stamp, d), task)
299 if not os.access(stampfile, os.F_OK):
306 tasktime = os.stat(stampfile)[stat.ST_MTIME]
309 def checkStamp(graph, task):
310 # check for existance
311 if data.getVarFlag(task, 'nostamp', d):
314 if not stamp_is_current(task, d, 0):
317 depfile = "%s.%s" % (data.expand(stamp, d), task)
318 deptime = os.stat(depfile)[stat.ST_MTIME]
319 if deptime > tasktime:
323 return task_graph.walkdown(task, checkStamp)
326 def md5_is_current(task):
327 """Check if a md5 file for a given task is current"""
330 def mkstamp(task, d):
331 """Creates/updates a stamp for a given task"""
332 stamp = data.getVar('STAMP', d)
335 stamp = "%s.%s" % (data.expand(stamp, d), task)
336 mkdirhier(os.path.dirname(stamp))
340 def add_task(task, deps, d):
341 task_graph = data.getVar('_task_graph', d)
343 task_graph = bb.digraph()
344 data.setVarFlag(task, 'task', 1, d)
345 task_graph.addnode(task, None)
347 if not task_graph.hasnode(dep):
348 task_graph.addnode(dep, None)
349 task_graph.addnode(task, dep)
350 # don't assume holding a reference
351 data.setVar('_task_graph', task_graph, d)
354 def remove_task(task, kill, d):
355 """Remove an BB 'task'.
357 If kill is 1, also remove tasks that depend on this task."""
359 task_graph = data.getVar('_task_graph', d)
361 task_graph = bb.digraph()
362 if not task_graph.hasnode(task):
365 data.delVarFlag(task, 'task', d)
369 task_graph.delnode(task, ref)
370 data.setVar('_task_graph', task_graph, d)
372 def task_exists(task, d):
373 task_graph = data.getVar('_task_graph', d)
375 task_graph = bb.digraph()
376 data.setVar('_task_graph', task_graph, d)
377 return task_graph.hasnode(task)