Move some variable sets out of the parser, add support for anonymous functions (execu...
[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}', d)
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 exec_func_python(func, d):
117         """Execute a python OE 'function'"""
118         import re, os
119
120         body = data.getVar(func, d)
121         if not body:
122                 return
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()
126         g = {} # globals
127         g['oe'] = oe
128         g['os'] = os
129         g['d'] = d
130         exec comp in g
131         os.chdir(prevdir)
132
133 def exec_func_shell(func, d):
134         """Execute a shell OE 'function' Returns true if execution was successful.
135
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.
138
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.
142         """
143         import sys
144
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):
149                         return
150
151         global logfile
152         t = data.getVar('T', d, 1)
153         if not t:
154                 oe.error("T variable not set")
155                 return 0
156         mkdirhier(t)
157         logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
158         runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
159
160         f = open(runfile, "w")
161         f.write("#!/bin/sh -e\n")
162         if data.getVar("OEDEBUG", d): f.write("set -x\n")
163         data.emit_env(f, d)
164
165         f.write("cd %s\n" % os.getcwd())
166         if func: f.write("%s || exit $?\n" % func)
167         f.close()
168         os.chmod(runfile, 0775)
169         if not func:
170                 error("Function not specified")
171                 raise FuncFailed()
172
173         # open logs
174         si = file('/dev/null', 'r')
175         so = file(logfile, 'a')
176         se = file(logfile, 'a+', 0)
177
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()]
182
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])
187
188         # execute function
189         prevdir = os.getcwd()
190         if data.getVarFlag(func, "fakeroot", d):
191                 maybe_fakeroot = oe.data.expand("${STAGING_BINDIR}/fakeroot ",d)
192         else:
193                 maybe_fakeroot = ''
194         ret = os.system('%ssh -e %s' % (maybe_fakeroot, 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