build.py: Fix debug_level references causing excessive debug output
[vuplus_bitbake] / lib / bb / 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 BitBake 'Build' implementation
6
7 Core code for function execution and task handling in the
8 BitBake build tools.
9
10 Copyright (C) 2003, 2004  Chris Larson
11
12 Based on Gentoo's portage.py.
13
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
17 version.
18
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.
22
23 You should have received a copy of the GNU General Public License along with
24
25 Based on functions from the base bb module, Copyright 2003 Holger Schurig
26 """
27
28 from bb import data, fetch, event, mkdirhier, utils
29 import bb, os
30
31 # events
32 class FuncFailed(Exception):
33     """Executed function failed"""
34
35 class EventException(Exception):
36     """Exception which is associated with an Event."""
37
38     def __init__(self, msg, event):
39         self.args = msg, event
40
41 class TaskBase(event.Event):
42     """Base class for task events"""
43
44     def __init__(self, t, d ):
45         self._task = t
46         event.Event.__init__(self, d)
47
48     def getTask(self):
49         return self._task
50
51     def setTask(self, task):
52         self._task = task
53
54     task = property(getTask, setTask, None, "task property")
55
56 class TaskStarted(TaskBase):
57     """Task execution started"""
58
59 class TaskSucceeded(TaskBase):
60     """Task execution completed"""
61
62 class TaskFailed(TaskBase):
63     """Task execution failed"""
64
65 class InvalidTask(TaskBase):
66     """Invalid Task"""
67
68 # functions
69
70 def exec_func(func, d, dirs = None):
71     """Execute an BB 'function'"""
72
73     body = data.getVar(func, d)
74     if not body:
75         return
76
77     if not dirs:
78         dirs = (data.getVarFlag(func, 'dirs', d) or "").split()
79     for adir in dirs:
80         adir = data.expand(adir, d)
81         mkdirhier(adir)
82
83     if len(dirs) > 0:
84         adir = dirs[-1]
85     else:
86         adir = data.getVar('B', d, 1)
87
88     adir = data.expand(adir, d)
89
90     try:
91         prevdir = os.getcwd()
92     except OSError:
93         prevdir = data.expand('${TOPDIR}', d)
94     if adir and os.access(adir, os.F_OK):
95         os.chdir(adir)
96
97     if data.getVarFlag(func, "python", d):
98         exec_func_python(func, d)
99     else:
100         exec_func_shell(func, d)
101
102     if os.path.exists(prevdir):
103         os.chdir(prevdir)
104
105 def exec_func_python(func, d):
106     """Execute a python BB 'function'"""
107     import re, os
108
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()
113     g = {} # globals
114     g['bb'] = bb
115     g['os'] = os
116     g['d'] = d
117     utils.better_exec(comp,g,tmp, bb.data.getVar('FILE',d,1))
118     if os.path.exists(prevdir):
119         os.chdir(prevdir)
120
121 def exec_func_shell(func, d):
122     """Execute a shell BB 'function' Returns true if execution was successful.
123
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.
126
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.
130     """
131     import sys
132
133     deps = data.getVarFlag(func, 'deps', d)
134     check = data.getVarFlag(func, 'check', d)
135     interact = data.getVarFlag(func, 'interactive', d)
136     if check in globals():
137         if globals()[check](func, deps):
138             return
139
140     global logfile
141     t = data.getVar('T', d, 1)
142     if not t:
143         return 0
144     mkdirhier(t)
145     logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
146     runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
147
148     f = open(runfile, "w")
149     f.write("#!/bin/sh -e\n")
150     if bb.msg.debug_level['general'] > 0: f.write("set -x\n")
151     data.emit_env(f, d)
152
153     f.write("cd %s\n" % os.getcwd())
154     if func: f.write("%s\n" % func)
155     f.close()
156     os.chmod(runfile, 0775)
157     if not func:
158         bb.msg.error(bb.msg.domain.Build, "Function not specified")
159         raise FuncFailed()
160
161     # open logs
162     si = file('/dev/null', 'r')
163     try:
164         if bb.msg.debug_level['general'] > 0:
165             so = os.popen("tee \"%s\"" % logfile, "w")
166         else:
167             so = file(logfile, 'w')
168     except OSError, e:
169         bb.msg.error(bb.msg.domain.Build, "opening log file: %s" % e)
170         pass
171
172     se = so
173
174     if not interact:
175         # dup the existing fds so we dont lose them
176         osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
177         oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
178         ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
179
180         # replace those fds with our own
181         os.dup2(si.fileno(), osi[1])
182         os.dup2(so.fileno(), oso[1])
183         os.dup2(se.fileno(), ose[1])
184
185     # execute function
186     prevdir = os.getcwd()
187     if data.getVarFlag(func, "fakeroot", d):
188         maybe_fakeroot = "PATH=\"%s\" fakeroot " % bb.data.getVar("PATH", d, 1)
189     else:
190         maybe_fakeroot = ''
191     ret = os.system('%ssh -e %s' % (maybe_fakeroot, runfile))
192     try:
193         os.chdir(prevdir)
194     except:
195         pass
196
197     if not interact:
198         # restore the backups
199         os.dup2(osi[0], osi[1])
200         os.dup2(oso[0], oso[1])
201         os.dup2(ose[0], ose[1])
202
203         # close our logs
204         si.close()
205         so.close()
206         se.close()
207
208         # close the backup fds
209         os.close(osi[0])
210         os.close(oso[0])
211         os.close(ose[0])
212
213     if ret==0:
214         if bb.msg.debug_level['general'] > 0:
215             os.remove(runfile)
216 #            os.remove(logfile)
217         return
218     else:
219         bb.msg.error(bb.msg.domain.Build, "function %s failed" % func)
220         if data.getVar("BBINCLUDELOGS", d):
221             bb.msg.error(bb.msg.domain.Build, "log data follows (%s)" % logfile)
222             f = open(logfile, "r")
223             while True:
224                 l = f.readline()
225                 if l == '':
226                     break
227                 l = l.rstrip()
228                 print '| %s' % l
229             f.close()
230         else:
231             bb.msg.error(bb.msg.domain.Build, "see log in %s" % logfile)
232         raise FuncFailed( logfile )
233
234
235 def exec_task(task, d):
236     """Execute an BB 'task'
237
238        The primary difference between executing a task versus executing
239        a function is that a task exists in the task digraph, and therefore
240        has dependencies amongst other tasks."""
241
242     # check if the task is in the graph..
243     task_graph = data.getVar('_task_graph', d)
244     if not task_graph:
245         task_graph = bb.digraph()
246         data.setVar('_task_graph', task_graph, d)
247     task_cache = data.getVar('_task_cache', d)
248     if not task_cache:
249         task_cache = []
250         data.setVar('_task_cache', task_cache, d)
251     if not task_graph.hasnode(task):
252         raise EventException("Missing node in task graph", InvalidTask(task, d))
253
254     # check whether this task needs executing..
255     if not data.getVarFlag(task, 'force', d):
256         if stamp_is_current(task, d):
257             return 1
258
259     # follow digraph path up, then execute our way back down
260     def execute(graph, item):
261         if data.getVarFlag(item, 'task', d):
262             if item in task_cache:
263                 return 1
264
265             if task != item:
266                 # deeper than toplevel, exec w/ deps
267                 exec_task(item, d)
268                 return 1
269
270             try:
271                 bb.msg.debug(1, bb.msg.domain.Build, "Executing task %s" % item)
272                 old_overrides = data.getVar('OVERRIDES', d, 0)
273                 localdata = data.createCopy(d)
274                 data.setVar('OVERRIDES', 'task_%s:%s' % (item, old_overrides), localdata)
275                 data.update_data(localdata)
276                 event.fire(TaskStarted(item, localdata))
277                 exec_func(item, localdata)
278                 event.fire(TaskSucceeded(item, localdata))
279                 task_cache.append(item)
280                 data.setVar('_task_cache', task_cache, d)
281             except FuncFailed, reason:
282                 bb.msg.note(1, bb.msg.domain.Build, "Task failed: %s" % reason )
283                 failedevent = TaskFailed(item, d)
284                 event.fire(failedevent)
285                 raise EventException("Function failed in task: %s" % reason, failedevent)
286
287     if data.getVarFlag(task, 'dontrundeps', d):
288         execute(None, task)
289     else:
290         task_graph.walkdown(task, execute)
291
292     # make stamp, or cause event and raise exception
293     if not data.getVarFlag(task, 'nostamp', d):
294         mkstamp(task, d)
295
296 def stamp_is_current_cache(dataCache, file_name, task, checkdeps = 1):
297     """
298     Check status of a given task's stamp. 
299     Returns 0 if it is not current and needs updating.
300     Same as stamp_is_current but works against the dataCache instead of d
301     """
302     task_graph = dataCache.task_queues[file_name]
303
304     if not dataCache.stamp[file_name]:
305         return 0
306
307     stampfile = "%s.%s" % (dataCache.stamp[file_name], task)
308     if not os.access(stampfile, os.F_OK):
309         return 0
310
311     if checkdeps == 0:
312         return 1
313
314     import stat
315     tasktime = os.stat(stampfile)[stat.ST_MTIME]
316
317     _deps = []
318     def checkStamp(graph, task):
319         # check for existance
320         if 'nostamp' in dataCache.task_deps[file_name] and task in dataCache.task_deps[file_name]['nostamp']:
321             return 1
322
323         if not stamp_is_current_cache(dataCache, file_name, task, 0):
324             return 0
325
326         depfile = "%s.%s" % (dataCache.stamp[file_name], task)
327         deptime = os.stat(depfile)[stat.ST_MTIME]
328         if deptime > tasktime:
329             return 0
330         return 1
331
332     return task_graph.walkdown(task, checkStamp)
333
334 def stamp_is_current(task, d, checkdeps = 1):
335     """
336     Check status of a given task's stamp. 
337     Returns 0 if it is not current and needs updating.
338     """
339     task_graph = data.getVar('_task_graph', d)
340     if not task_graph:
341         task_graph = bb.digraph()
342         data.setVar('_task_graph', task_graph, d)
343     stamp = data.getVar('STAMP', d)
344     if not stamp:
345         return 0
346     stampfile = "%s.%s" % (data.expand(stamp, d), task)
347     if not os.access(stampfile, os.F_OK):
348         return 0
349
350     if checkdeps == 0:
351         return 1
352
353     import stat
354     tasktime = os.stat(stampfile)[stat.ST_MTIME]
355
356     _deps = []
357     def checkStamp(graph, task):
358         # check for existance
359         if data.getVarFlag(task, 'nostamp', d):
360             return 1
361
362         if not stamp_is_current(task, d, 0):
363             return 0
364
365         depfile = "%s.%s" % (data.expand(stamp, d), task)
366         deptime = os.stat(depfile)[stat.ST_MTIME]
367         if deptime > tasktime:
368             return 0
369         return 1
370
371     return task_graph.walkdown(task, checkStamp)
372
373
374 def md5_is_current(task):
375     """Check if a md5 file for a given task is current"""
376
377
378 def mkstamp(task, d):
379     """Creates/updates a stamp for a given task"""
380     stamp = data.getVar('STAMP', d)
381     if not stamp:
382         return
383     stamp = "%s.%s" % (data.expand(stamp, d), task)
384     mkdirhier(os.path.dirname(stamp))
385     # Remove the file and recreate to force timestamp
386     # change on broken NFS filesystems
387     if os.access(stamp, os.F_OK):
388         os.remove(stamp)
389     f = open(stamp, "w")
390     f.close()
391
392 def add_task(task, deps, d):
393     task_graph = data.getVar('_task_graph', d)
394     if not task_graph:
395         task_graph = bb.digraph()
396     data.setVarFlag(task, 'task', 1, d)
397     task_graph.addnode(task, None)
398     for dep in deps:
399         if not task_graph.hasnode(dep):
400             task_graph.addnode(dep, None)
401         task_graph.addnode(task, dep)
402     # don't assume holding a reference
403     data.setVar('_task_graph', task_graph, d)
404
405     task_deps = data.getVar('_task_deps', d)
406     if not task_deps:
407         task_deps = {}
408     def getTask(name):
409         deptask = data.getVarFlag(task, name, d)
410         if deptask:
411             if not name in task_deps:
412                 task_deps[name] = {}
413             task_deps[name][task] = deptask
414     getTask('deptask')
415     getTask('rdeptask')
416     getTask('recrdeptask')
417     getTask('nostamp')
418
419     data.setVar('_task_deps', task_deps, d)
420
421 def remove_task(task, kill, d):
422     """Remove an BB 'task'.
423
424        If kill is 1, also remove tasks that depend on this task."""
425
426     task_graph = data.getVar('_task_graph', d)
427     if not task_graph:
428         task_graph = bb.digraph()
429     if not task_graph.hasnode(task):
430         return
431
432     data.delVarFlag(task, 'task', d)
433     ref = 1
434     if kill == 1:
435         ref = 2
436     task_graph.delnode(task, ref)
437     data.setVar('_task_graph', task_graph, d)
438
439 def task_exists(task, d):
440     task_graph = data.getVar('_task_graph', d)
441     if not task_graph:
442         task_graph = bb.digraph()
443         data.setVar('_task_graph', task_graph, d)
444     return task_graph.hasnode(task)