summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/build.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/build.py')
-rw-r--r--bitbake/lib/bb/build.py395
1 files changed, 395 insertions, 0 deletions
diff --git a/bitbake/lib/bb/build.py b/bitbake/lib/bb/build.py
new file mode 100644
index 0000000000..599b45d9d3
--- /dev/null
+++ b/bitbake/lib/bb/build.py
@@ -0,0 +1,395 @@
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"""
5BitBake 'Build' implementation
6
7Core code for function execution and task handling in the
8BitBake build tools.
9
10Copyright (C) 2003, 2004 Chris Larson
11
12Based on Gentoo's portage.py.
13
14This program is free software; you can redistribute it and/or modify it under
15the terms of the GNU General Public License as published by the Free Software
16Foundation; either version 2 of the License, or (at your option) any later
17version.
18
19This program is distributed in the hope that it will be useful, but WITHOUT
20ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
22
23You should have received a copy of the GNU General Public License along with
24
25Based on functions from the base bb module, Copyright 2003 Holger Schurig
26"""
27
28from bb import debug, data, fetch, fatal, error, note, event, mkdirhier
29import bb, os
30
31# data holds flags and function name for a given task
32_task_data = data.init()
33
34# graph represents task interdependencies
35_task_graph = bb.digraph()
36
37# stack represents execution order, excepting dependencies
38_task_stack = []
39
40# events
41class FuncFailed(Exception):
42 """Executed function failed"""
43
44class EventException(Exception):
45 """Exception which is associated with an Event."""
46
47 def __init__(self, msg, event):
48 self.args = msg, event
49
50class TaskBase(event.Event):
51 """Base class for task events"""
52
53 def __init__(self, t, d ):
54 self._task = t
55 event.Event.__init__(self, d)
56
57 def getTask(self):
58 return self._task
59
60 def setTask(self, task):
61 self._task = task
62
63 task = property(getTask, setTask, None, "task property")
64
65class TaskStarted(TaskBase):
66 """Task execution started"""
67
68class TaskSucceeded(TaskBase):
69 """Task execution completed"""
70
71class TaskFailed(TaskBase):
72 """Task execution failed"""
73
74class InvalidTask(TaskBase):
75 """Invalid Task"""
76
77# functions
78
79def init(data):
80 global _task_data, _task_graph, _task_stack
81 _task_data = data.init()
82 _task_graph = bb.digraph()
83 _task_stack = []
84
85
86def exec_func(func, d, dirs = None):
87 """Execute an BB 'function'"""
88
89 body = data.getVar(func, d)
90 if not body:
91 return
92
93 if not dirs:
94 dirs = (data.getVarFlag(func, 'dirs', d) or "").split()
95 for adir in dirs:
96 adir = data.expand(adir, d)
97 mkdirhier(adir)
98
99 if len(dirs) > 0:
100 adir = dirs[-1]
101 else:
102 adir = data.getVar('B', d, 1)
103
104 adir = data.expand(adir, d)
105
106 try:
107 prevdir = os.getcwd()
108 except OSError:
109 prevdir = data.expand('${TOPDIR}', d)
110 if adir and os.access(adir, os.F_OK):
111 os.chdir(adir)
112
113 if data.getVarFlag(func, "python", d):
114 exec_func_python(func, d)
115 else:
116 exec_func_shell(func, d)
117
118 if os.path.exists(prevdir):
119 os.chdir(prevdir)
120
121def exec_func_python(func, d):
122 """Execute a python BB 'function'"""
123 import re, os
124
125 tmp = "def " + func + "():\n%s" % data.getVar(func, d)
126 comp = compile(tmp + '\n' + func + '()', bb.data.getVar('FILE', d, 1) + ':' + func, "exec")
127 prevdir = os.getcwd()
128 g = {} # globals
129 g['bb'] = bb
130 g['os'] = os
131 g['d'] = d
132 exec comp in g
133 if os.path.exists(prevdir):
134 os.chdir(prevdir)
135
136def exec_func_shell(func, d):
137 """Execute a shell BB 'function' Returns true if execution was successful.
138
139 For this, it creates a bash shell script in the tmp dectory, writes the local
140 data into it and finally executes. The output of the shell will end in a log file and stdout.
141
142 Note on directory behavior. The 'dirs' varflag should contain a list
143 of the directories you need created prior to execution. The last
144 item in the list is where we will chdir/cd to.
145 """
146 import sys
147
148 deps = data.getVarFlag(func, 'deps', d)
149 check = data.getVarFlag(func, 'check', d)
150 if check in globals():
151 if globals()[check](func, deps):
152 return
153
154 global logfile
155 t = data.getVar('T', d, 1)
156 if not t:
157 return 0
158 mkdirhier(t)
159 logfile = "%s/log.%s.%s" % (t, func, str(os.getpid()))
160 runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
161
162 f = open(runfile, "w")
163 f.write("#!/bin/sh -e\n")
164 if bb.debug_level > 0: f.write("set -x\n")
165 data.emit_env(f, d)
166
167 f.write("cd %s\n" % os.getcwd())
168 if func: f.write("%s\n" % func)
169 f.close()
170 os.chmod(runfile, 0775)
171 if not func:
172 error("Function not specified")
173 raise FuncFailed()
174
175 # open logs
176 si = file('/dev/null', 'r')
177 try:
178 if bb.debug_level > 0:
179 so = os.popen("tee \"%s\"" % logfile, "w")
180 else:
181 so = file(logfile, 'w')
182 except OSError, e:
183 bb.error("opening log file: %s" % e)
184 pass
185
186 se = so
187
188 # dup the existing fds so we dont lose them
189 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
190 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
191 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
192
193 # replace those fds with our own
194 os.dup2(si.fileno(), osi[1])
195 os.dup2(so.fileno(), oso[1])
196 os.dup2(se.fileno(), ose[1])
197
198 # execute function
199 prevdir = os.getcwd()
200 if data.getVarFlag(func, "fakeroot", d):
201 maybe_fakeroot = "PATH=\"%s\" fakeroot " % bb.data.getVar("PATH", d, 1)
202 else:
203 maybe_fakeroot = ''
204 ret = os.system('%ssh -e %s' % (maybe_fakeroot, runfile))
205 os.chdir(prevdir)
206
207 # restore the backups
208 os.dup2(osi[0], osi[1])
209 os.dup2(oso[0], oso[1])
210 os.dup2(ose[0], ose[1])
211
212 # close our logs
213 si.close()
214 so.close()
215 se.close()
216
217 # close the backup fds
218 os.close(osi[0])
219 os.close(oso[0])
220 os.close(ose[0])
221
222 if ret==0:
223 if bb.debug_level > 0:
224 os.remove(runfile)
225# os.remove(logfile)
226 return
227 else:
228 error("function %s failed" % func)
229 if data.getVar("BBINCLUDELOGS", d):
230 error("log data follows (%s)" % logfile)
231 f = open(logfile, "r")
232 while True:
233 l = f.readline()
234 if l == '':
235 break
236 l = l.rstrip()
237 print '| %s' % l
238 f.close()
239 else:
240 error("see log in %s" % logfile)
241 raise FuncFailed( logfile )
242
243
244def exec_task(task, d):
245 """Execute an BB 'task'
246
247 The primary difference between executing a task versus executing
248 a function is that a task exists in the task digraph, and therefore
249 has dependencies amongst other tasks."""
250
251 # check if the task is in the graph..
252 task_graph = data.getVar('_task_graph', d)
253 if not task_graph:
254 task_graph = bb.digraph()
255 data.setVar('_task_graph', task_graph, d)
256 task_cache = data.getVar('_task_cache', d)
257 if not task_cache:
258 task_cache = []
259 data.setVar('_task_cache', task_cache, d)
260 if not task_graph.hasnode(task):
261 raise EventException("Missing node in task graph", InvalidTask(task, d))
262
263 # check whether this task needs executing..
264 if not data.getVarFlag(task, 'force', d):
265 if stamp_is_current(task, d):
266 return 1
267
268 # follow digraph path up, then execute our way back down
269 def execute(graph, item):
270 if data.getVarFlag(item, 'task', d):
271 if item in task_cache:
272 return 1
273
274 if task != item:
275 # deeper than toplevel, exec w/ deps
276 exec_task(item, d)
277 return 1
278
279 try:
280 debug(1, "Executing task %s" % item)
281 old_overrides = data.getVar('OVERRIDES', d, 0)
282 localdata = data.createCopy(d)
283 data.setVar('OVERRIDES', 'task_%s:%s' % (item, old_overrides), localdata)
284 data.update_data(localdata)
285 event.fire(TaskStarted(item, localdata))
286 exec_func(item, localdata)
287 event.fire(TaskSucceeded(item, localdata))
288 task_cache.append(item)
289 data.setVar('_task_cache', task_cache, d)
290 except FuncFailed, reason:
291 note( "Task failed: %s" % reason )
292 failedevent = TaskFailed(item, d)
293 event.fire(failedevent)
294 raise EventException("Function failed in task: %s" % reason, failedevent)
295
296 # execute
297 task_graph.walkdown(task, execute)
298
299 # make stamp, or cause event and raise exception
300 if not data.getVarFlag(task, 'nostamp', d):
301 mkstamp(task, d)
302
303
304def stamp_is_current(task, d, checkdeps = 1):
305 """Check status of a given task's stamp. returns 0 if it is not current and needs updating."""
306 task_graph = data.getVar('_task_graph', d)
307 if not task_graph:
308 task_graph = bb.digraph()
309 data.setVar('_task_graph', task_graph, d)
310 stamp = data.getVar('STAMP', d)
311 if not stamp:
312 return 0
313 stampfile = "%s.%s" % (data.expand(stamp, d), task)
314 if not os.access(stampfile, os.F_OK):
315 return 0
316
317 if checkdeps == 0:
318 return 1
319
320 import stat
321 tasktime = os.stat(stampfile)[stat.ST_MTIME]
322
323 _deps = []
324 def checkStamp(graph, task):
325 # check for existance
326 if data.getVarFlag(task, 'nostamp', d):
327 return 1
328
329 if not stamp_is_current(task, d, 0):
330 return 0
331
332 depfile = "%s.%s" % (data.expand(stamp, d), task)
333 deptime = os.stat(depfile)[stat.ST_MTIME]
334 if deptime > tasktime:
335 return 0
336 return 1
337
338 return task_graph.walkdown(task, checkStamp)
339
340
341def md5_is_current(task):
342 """Check if a md5 file for a given task is current"""
343
344
345def mkstamp(task, d):
346 """Creates/updates a stamp for a given task"""
347 stamp = data.getVar('STAMP', d)
348 if not stamp:
349 return
350 stamp = "%s.%s" % (data.expand(stamp, d), task)
351 mkdirhier(os.path.dirname(stamp))
352 open(stamp, "w+")
353
354
355def add_task(task, deps, d):
356 task_graph = data.getVar('_task_graph', d)
357 if not task_graph:
358 task_graph = bb.digraph()
359 data.setVarFlag(task, 'task', 1, d)
360 task_graph.addnode(task, None)
361 for dep in deps:
362 if not task_graph.hasnode(dep):
363 task_graph.addnode(dep, None)
364 task_graph.addnode(task, dep)
365 # don't assume holding a reference
366 data.setVar('_task_graph', task_graph, d)
367
368
369def remove_task(task, kill, d):
370 """Remove an BB 'task'.
371
372 If kill is 1, also remove tasks that depend on this task."""
373
374 task_graph = data.getVar('_task_graph', d)
375 if not task_graph:
376 task_graph = bb.digraph()
377 if not task_graph.hasnode(task):
378 return
379
380 data.delVarFlag(task, 'task', d)
381 ref = 1
382 if kill == 1:
383 ref = 2
384 task_graph.delnode(task, ref)
385 data.setVar('_task_graph', task_graph, d)
386
387def task_exists(task, d):
388 task_graph = data.getVar('_task_graph', d)
389 if not task_graph:
390 task_graph = bb.digraph()
391 data.setVar('_task_graph', task_graph, d)
392 return task_graph.hasnode(task)
393
394def get_task_data():
395 return _task_data