add current task to OVERRIDES during execution to facilitate "EXTRA_OEMAKE_prepend_do...
[vuplus_bitbake] / bin / oe / build.py
1 #!/usr/bin/env python
2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4 """
5 OpenEmbedded 'Build' implementation
6
7 Core code for function execution and task handling in the
8 OpenEmbedded (http://openembedded.org) build infrastructure.
9
10 Copyright: (c) 2003 Chris Larson
11
12 Based on functions from the base oe module, Copyright 2003 Holger Schurig
13 """
14
15 from oe import debug, data, fetch, fatal, error, note, event, mkdirhier
16 import oe, os
17
18 # data holds flags and function name for a given task
19 _task_data = data.init()
20
21 # graph represents task interdependencies
22 _task_graph = oe.digraph()
23
24 # stack represents execution order, excepting dependencies
25 _task_stack = []
26
27 # events
28 class FuncFailed(Exception):
29     """Executed function failed"""
30
31 class EventException(Exception):
32     """Exception which is associated with an Event."""
33
34     def __init__(self, msg, event):
35         self.event = event
36
37     def getEvent(self):
38         return self._event
39
40     def setEvent(self, event):
41         self._event = event
42
43     event = property(getEvent, setEvent, None, "event property")
44
45 class TaskBase(event.Event):
46     """Base class for task events"""
47
48     def __init__(self, t, d = {}):
49         self.task = t
50         self.data = d
51
52     def getTask(self):
53         return self._task
54
55     def setTask(self, task):
56         self._task = task
57
58     task = property(getTask, setTask, None, "task property")
59
60     def getData(self):
61         return self._data
62
63     def setData(self, data):
64         self._data = data
65
66     data = property(getData, setData, None, "data property")
67
68 class TaskStarted(TaskBase):
69     """Task execution started"""
70
71 class TaskSucceeded(TaskBase):
72     """Task execution completed"""
73
74 class TaskFailed(TaskBase):
75     """Task execution failed"""
76
77 class InvalidTask(TaskBase):
78     """Invalid Task"""
79
80 # functions
81
82 def init(data):
83     global _task_data, _task_graph, _task_stack
84     _task_data = data.init()
85     _task_graph = oe.digraph()
86     _task_stack = []
87
88
89 def exec_func(func, d, dirs = None):
90     """Execute an OE 'function'"""
91
92     body = data.getVar(func, d)
93     if not body:
94         return
95
96     if not dirs:
97         dirs = (data.getVarFlag(func, 'dirs', d) or "").split()
98     for adir in dirs:
99         adir = data.expand(adir, d)
100         mkdirhier(adir)
101
102     if len(dirs) > 0:
103         adir = dirs[-1]
104     else:
105         adir = data.getVar('B', d, 1)
106
107     adir = data.expand(adir, d)
108
109     try:
110         prevdir = os.getcwd()
111     except OSError:
112         prevdir = data.expand('${TOPDIR}', d)
113     if adir and os.access(adir, os.F_OK):
114         os.chdir(adir)
115
116     if data.getVarFlag(func, "python", d):
117         exec_func_python(func, d)
118     else:
119         exec_func_shell(func, d)
120     os.chdir(prevdir)
121
122 def exec_func_python(func, d):
123     """Execute a python OE 'function'"""
124     import re, os
125
126     tmp = "def " + func + "():\n%s" % data.getVar(func, d)
127     comp = compile(tmp + '\n' + func + '()', oe.data.getVar('FILE', d, 1) + ':' + func, "exec")
128     prevdir = os.getcwd()
129     g = {} # globals
130     g['oe'] = oe
131     g['os'] = os
132     g['d'] = d
133     exec comp in g
134     if os.path.exists(prevdir):
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, 1)
157     if not t:
158         return 0
159     mkdirhier(t)
160     logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
161     runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
162
163     f = open(runfile, "w")
164     f.write("#!/bin/sh -e\n")
165     if data.getVar("OEDEBUG", d): f.write("set -x\n")
166     data.emit_env(f, d)
167
168     f.write("cd %s\n" % os.getcwd())
169     if func: f.write("%s\n" % func)
170     f.close()
171     os.chmod(runfile, 0775)
172     if not func:
173         error("Function not specified")
174         raise FuncFailed()
175
176     # open logs
177     si = file('/dev/null', 'r')
178     try:
179         if data.getVar("OEDEBUG", d):
180             so = os.popen("tee \"%s\"" % logfile, "w")
181         else:
182             so = file(logfile, 'w')
183     except OSError, e:
184         oe.error("opening log file: %s" % e)
185         pass
186
187     se = so
188
189     # dup the existing fds so we dont lose them
190     osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
191     oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
192     ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
193
194     # replace those fds with our own
195     os.dup2(si.fileno(), osi[1])
196     os.dup2(so.fileno(), oso[1])
197     os.dup2(se.fileno(), ose[1])
198
199     # execute function
200     prevdir = os.getcwd()
201     if data.getVarFlag(func, "fakeroot", d):
202         maybe_fakeroot = "PATH=\"%s\" fakeroot " % oe.data.getVar("PATH", d, 1)
203     else:
204         maybe_fakeroot = ''
205     ret = os.system('%ssh -e %s' % (maybe_fakeroot, runfile))
206     os.chdir(prevdir)
207
208     # restore the backups
209     os.dup2(osi[0], osi[1])
210     os.dup2(oso[0], oso[1])
211     os.dup2(ose[0], ose[1])
212
213     # close our logs
214     si.close()
215     so.close()
216     se.close()
217
218     # close the backup fds
219     os.close(osi[0])
220     os.close(oso[0])
221     os.close(ose[0])
222
223     if ret==0:
224         if not data.getVar("OEDEBUG", d):
225             os.remove(runfile)
226 #            os.remove(logfile)
227         return
228     else:
229         error("function %s failed" % func)
230         if data.getVar("OEINCLUDELOGS", d):
231             error("log data follows (%s)" % logfile)
232             f = open(logfile, "r")
233             while True:
234                 l = f.readline()
235                 if l == '':
236                     break
237                 l = l.rstrip()
238                 print '| %s' % l
239             f.close()
240         else:
241             error("see log in %s" % logfile)
242         raise FuncFailed()
243
244
245 _task_cache = []
246
247 def exec_task(task, d):
248     """Execute an OE 'task'
249
250        The primary difference between executing a task versus executing
251        a function is that a task exists in the task digraph, and therefore
252        has dependencies amongst other tasks."""
253
254     # check if the task is in the graph..
255     task_graph = data.getVar('_task_graph', d)
256     if not task_graph:
257         task_graph = oe.digraph()
258         data.setVar('_task_graph', task_graph, d)
259     task_cache = data.getVar('_task_cache', d)
260     if not task_cache:
261         task_cache = []
262         data.setVar('_task_cache', task_cache, d)
263     if not task_graph.hasnode(task):
264         raise EventException("", InvalidTask(task, d))
265
266     # check whether this task needs executing..
267     if not data.getVarFlag(task, 'force', d):
268         if stamp_is_current(task, d):
269             return 1
270
271     # follow digraph path up, then execute our way back down
272     def execute(graph, item):
273         if data.getVarFlag(item, 'task', d):
274             if item in task_cache:
275                 return 1
276
277             if task != item:
278                 # deeper than toplevel, exec w/ deps
279                 exec_task(item, d)
280                 return 1
281
282             try:
283                 debug(1, "Executing task %s" % item)
284                 event.fire(TaskStarted(item, d))
285                 from copy import deepcopy
286                 localdata = deepcopy(d)
287                 oe.data.setVar('OVERRIDES', "%s:%s" % (item.replace('_', '-'), oe.data.getVar('OVERRIDES', localdata)), localdata)
288                 oe.data.update_data(localdata)
289                 exec_func(item, localdata)
290                 event.fire(TaskSucceeded(item, d))
291                 task_cache.append(item)
292             except FuncFailed, reason:
293                 note( "Task failed: %s" % reason )
294                 failedevent = TaskFailed(item, d)
295                 event.fire(failedevent)
296                 raise EventException(None, failedevent)
297
298     # execute
299     task_graph.walkdown(task, execute)
300
301     # make stamp, or cause event and raise exception
302     if not data.getVarFlag(task, 'nostamp', d):
303         mkstamp(task, d)
304
305
306 def stamp_is_current(task, d, checkdeps = 1):
307     """Check status of a given task's stamp. returns 0 if it is not current and needs updating."""
308     task_graph = data.getVar('_task_graph', d)
309     if not task_graph:
310         task_graph = oe.digraph()
311         data.setVar('_task_graph', task_graph, d)
312     stamp = data.getVar('STAMP', d)
313     if not stamp:
314         return 0
315     stampfile = "%s.%s" % (data.expand(stamp, d), task)
316     if not os.access(stampfile, os.F_OK):
317         return 0
318
319     if checkdeps == 0:
320         return 1
321
322     import stat
323     tasktime = os.stat(stampfile)[stat.ST_MTIME]
324
325     _deps = []
326     def checkStamp(graph, task):
327         # check for existance
328         if data.getVarFlag(task, 'nostamp', d):
329             return 1
330
331         if not stamp_is_current(task, d, 0):
332             return 0
333
334         depfile = "%s.%s" % (data.expand(stamp, d), task)
335         deptime = os.stat(depfile)[stat.ST_MTIME]
336         if deptime > tasktime:
337             return 0
338         return 1
339
340     return task_graph.walkdown(task, checkStamp)
341
342
343 def md5_is_current(task):
344     """Check if a md5 file for a given task is current"""
345
346
347 def mkstamp(task, d):
348     """Creates/updates a stamp for a given task"""
349     mkdirhier(data.expand('${TMPDIR}/stamps', d));
350     stamp = data.getVar('STAMP', d)
351     if not stamp:
352         return
353     stamp = "%s.%s" % (data.expand(stamp, d), task)
354     open(stamp, "w+")
355
356
357 def add_task(task, deps, d):
358     task_graph = data.getVar('_task_graph', d)
359     if not task_graph:
360         task_graph = oe.digraph()
361         data.setVar('_task_graph', task_graph, d)
362     data.setVarFlag(task, 'task', 1, d)
363     task_graph.addnode(task, None)
364     for dep in deps:
365         if not task_graph.hasnode(dep):
366             task_graph.addnode(dep, None)
367         task_graph.addnode(task, dep)
368
369
370 def remove_task(task, kill, d):
371     """Remove an OE 'task'.
372
373        If kill is 1, also remove tasks that depend on this task."""
374
375     task_graph = data.getVar('_task_graph', d)
376     if not task_graph:
377         task_graph = oe.digraph()
378         data.setVar('_task_graph', task_graph, d)
379     if not task_graph.hasnode(task):
380         return
381
382     data.delVarFlag(task, 'task', d)
383     ref = 1
384     if kill == 1:
385         ref = 2
386     task_graph.delnode(task, ref)
387
388 def task_exists(task, d):
389     task_graph = data.getVar('_task_graph', d)
390     if not task_graph:
391         task_graph = oe.digraph()
392         data.setVar('_task_graph', task_graph, d)
393     return task_graph.hasnode(task)
394
395 def get_task_data():
396     return _task_data