Update the way we execute python functions, so on failure we actually see what functi...
[vuplus_bitbake] / bin / oe / build.py
1 #!usr/bin/python
2 """
3 OpenEmbedded 'Build' implementation
4
5 Core code for function execution and task handling in the
6 OpenEmbedded (http://openembedded.org) build infrastructure.
7
8 Copyright: (c) 2003 Chris Larson
9
10 Based on functions from the base oe module, Copyright 2003 Holger Schurig
11 """
12
13 from oe import debug, data, fetch, fatal, error, note, event, mkdirhier
14 import oe, os, string
15
16 # data holds flags and function name for a given task
17 _task_data = data.init()
18
19 # graph represents task interdependencies
20 _task_graph = oe.digraph()
21
22 # stack represents execution order, excepting dependencies
23 _task_stack = []
24
25 # events
26 class FuncFailed(Exception):
27         """Executed function failed"""
28
29 class EventException(Exception):
30         """Exception which is associated with an Event."""
31
32         def __init__(self, msg, event):
33                 self.event = event
34
35         def getEvent(self):
36                 return self._event
37
38         def setEvent(self, event):
39                 self._event = event
40
41         event = property(getEvent, setEvent, None, "event property")
42
43 class TaskBase(event.Event):
44         """Base class for task events"""
45
46         def __init__(self, t, d = {}):
47                 self.task = t
48                 self.data = d
49
50         def getTask(self):
51                 return self._task
52
53         def setTask(self, task):
54                 self._task = task
55
56         task = property(getTask, setTask, None, "task property")
57
58         def getData(self):
59                 return self._data
60
61         def setData(self, data):
62                 self._data = data
63
64         data = property(getData, setData, None, "data property")
65
66 class TaskStarted(TaskBase):
67         """Task execution started"""
68         
69 class TaskSucceeded(TaskBase):
70         """Task execution completed"""
71
72 class TaskFailed(TaskBase):
73         """Task execution failed"""
74
75 class InvalidTask(TaskBase):
76         """Invalid Task"""
77
78 # functions
79
80 def init(data):
81         global _task_data, _task_graph, _task_stack
82         _task_data = data.init()
83         _task_graph = oe.digraph()
84         _task_stack = []
85
86
87 def exec_func(func, d, dirs = None):
88         """Execute an OE 'function'"""
89
90         if not dirs:
91                 dirs = string.split(data.getVarFlag(func, 'dirs', d) or "")
92         for adir in dirs:
93                 adir = data.expand(adir, d)
94                 mkdirhier(adir) 
95
96         if len(dirs) > 0:
97                 adir = dirs[-1]
98         else:
99                 adir = data.getVar('S', d)
100
101         adir = data.expand(adir, d)
102
103         try:
104                 prevdir = os.getcwd()
105         except OSError:
106                 prevdir = data.expand('${TOPDIR}')
107         if adir and os.access(adir, os.F_OK):
108                 os.chdir(adir)
109
110         if data.getVarFlag(func, "python", d):
111                 exec_func_python(func, d)
112         else:
113                 exec_func_shell(func, d)
114         os.chdir(prevdir)
115
116 def tmpFunction(d):
117         """Default function for python code blocks"""
118         return 1
119
120 def exec_func_python(func, d):
121         """Execute a python OE 'function'"""
122         import re, os
123
124         body = data.getVar(func, d)
125         if not body:
126                 return
127         tmp = "def " + func + "():\n%s" % body
128         comp = compile(tmp + '\n' + func + '()', oe.data.getVar('FILE', d, 1) + ':' + func, "exec")
129         prevdir = os.getcwd()
130         g = {} # globals
131         g['oe'] = oe
132         g['os'] = os
133         g['d'] = d
134         exec comp in g
135         os.chdir(prevdir)
136
137 def exec_func_shell(func, d):
138         """Execute a shell OE 'function' Returns true if execution was successful.
139
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.
142
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.
146         """
147         import sys
148
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):
153                         return
154
155         global logfile
156         t = data.getVar('T', d)
157         if not t:
158                 return 0
159         t = data.expand(t, d)
160         mkdirhier(t)
161         logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
162         runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
163
164         f = open(runfile, "w")
165         f.write("#!/bin/sh -e\n")
166         if data.getVar("OEDEBUG", d): f.write("set -x\n")
167         data.emit_env(f, d)
168
169         f.write("cd %s\n" % os.getcwd())
170         if func: f.write("%s || exit $?\n" % func)
171         f.close()
172         os.chmod(runfile, 0775)
173         if not func:
174                 error("Function not specified")
175                 raise FuncFailed()
176
177         # open logs
178         si = file('/dev/null', 'r')
179         so = file(logfile, 'a')
180         se = file(logfile, 'a+', 0)
181
182         # dup the existing fds so we dont lose them
183         osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
184         oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
185         ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
186
187         # replace those fds with our own
188         os.dup2(si.fileno(), osi[1])
189         os.dup2(so.fileno(), oso[1])
190         os.dup2(se.fileno(), ose[1])
191
192         # execute function
193         prevdir = os.getcwd()
194         ret = os.system('sh -e %s' % runfile)
195         os.chdir(prevdir)
196
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])
201
202         # close our logs
203         si.close()
204         so.close()
205         se.close()
206
207         # close the backup fds
208         os.close(osi[0])
209         os.close(oso[0])
210         os.close(ose[0])
211
212         if ret==0:
213                 if not data.getVar("OEDEBUG"):
214                         os.remove(runfile)
215 #                       os.remove(logfile)
216                 return
217         else:
218                 error("function %s failed" % func)
219                 error("see log in %s" % logfile)
220                 raise FuncFailed()
221
222
223 _task_cache = []
224
225 def exec_task(task, d):
226         """Execute an OE 'task'
227
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."""
231
232         # check if the task is in the graph..
233         task_graph = data.getVar('_task_graph', d)
234         if not task_graph:
235                 task_graph = oe.digraph()
236                 data.setVar('_task_graph', task_graph, d)
237         task_cache = data.getVar('_task_cache', d)
238         if not task_cache:
239                 task_cache = []
240                 data.setVar('_task_cache', task_cache, d)
241         if not task_graph.hasnode(task):
242                 raise EventException("", InvalidTask(task, d))
243
244         # check whether this task needs executing..
245         if not data.getVarFlag(task, 'force', d):
246                 if stamp_is_current(task, d):
247                         return 1
248
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:
253                                 return 1
254
255                         if task != item:
256                                 # deeper than toplevel, exec w/ deps
257                                 exec_task(item, d)
258                                 return 1
259
260                         try:
261                                 debug(1, "Executing task %s" % item)
262                                 event.fire(TaskStarted(item, d))
263                                 exec_func(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)
271
272         # execute
273         task_graph.walkdown(task, execute)
274
275         # make stamp, or cause event and raise exception
276         if not data.getVarFlag(task, 'nostamp', d):
277                 mkstamp(task, d)
278
279
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)
283         if not task_graph:
284                 task_graph = oe.digraph()
285                 data.setVar('_task_graph', task_graph, d)
286         stamp = data.getVar('STAMP', d)
287         if not stamp:
288                 return 0
289         stampfile = "%s.%s" % (data.expand(stamp, d), task)
290         if not os.access(stampfile, os.F_OK):
291                 return 0
292
293         if checkdeps == 0:
294                 return 1
295
296         import stat
297         tasktime = os.stat(stampfile)[stat.ST_MTIME]
298
299         _deps = []
300         def checkStamp(graph, task):
301                 # check for existance
302                 if data.getVarFlag(task, 'nostamp', d):
303                         return 1
304
305                 if not stamp_is_current(task, d, 0):
306                         return 0
307
308                 depfile = "%s.%s" % (data.expand(stamp, d), task)
309                 deptime = os.stat(depfile)[stat.ST_MTIME]
310                 if deptime > tasktime:
311                         return 0
312                 return 1
313
314         return task_graph.walkdown(task, checkStamp)
315
316
317 def md5_is_current(task):
318         """Check if a md5 file for a given task is current""" 
319
320
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)
325         if not stamp:
326                 return
327         stamp = "%s.%s" % (data.expand(stamp, d), task)
328         open(stamp, "w+")
329
330
331 def add_task(task, deps, d):
332         task_graph = data.getVar('_task_graph', d)
333         if not task_graph:
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)
338         for dep in deps:
339                 if not task_graph.hasnode(dep):
340                         task_graph.addnode(dep, None)
341                 task_graph.addnode(task, dep)
342
343
344 def remove_task(task, kill, d):
345         """Remove an OE 'task'.
346
347            If kill is 1, also remove tasks that depend on this task."""
348
349         task_graph = data.getVar('_task_graph', d)
350         if not task_graph:
351                 task_graph = oe.digraph()
352                 data.setVar('_task_graph', task_graph, d)
353         if not task_graph.hasnode(task):
354                 return
355
356         data.delVarFlag(task, 'task', d)
357         ref = 1
358         if kill == 1:
359                 ref = 2
360         task_graph.delnode(task, ref)
361
362 def task_exists(task, d):
363         task_graph = data.getVar('_task_graph', d)
364         if not task_graph:
365                 task_graph = oe.digraph()
366                 data.setVar('_task_graph', task_graph, d)
367         return task_graph.hasnode(task)
368
369 def get_task_data():
370         return _task_data