summaryrefslogtreecommitdiffstats
path: root/bitbake-dev/lib/bb/build.py
diff options
context:
space:
mode:
authorRichard Purdie <richard@openedhand.com>2008-09-30 15:08:33 +0000
committerRichard Purdie <richard@openedhand.com>2008-09-30 15:08:33 +0000
commitc30eddb243e7e65f67f656e62848a033cf6f2e5c (patch)
tree110dd95788b76f55d31cb8d30aac2de8400b6f4a /bitbake-dev/lib/bb/build.py
parent5ef0510474004eeb2ae8a99b64e2febb1920e077 (diff)
downloadpoky-c30eddb243e7e65f67f656e62848a033cf6f2e5c.tar.gz
Add bitbake-dev to allow ease of testing and development of bitbake trunk
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@5337 311d38ba-8fff-0310-9ca6-ca027cbcb966
Diffstat (limited to 'bitbake-dev/lib/bb/build.py')
-rw-r--r--bitbake-dev/lib/bb/build.py377
1 files changed, 377 insertions, 0 deletions
diff --git a/bitbake-dev/lib/bb/build.py b/bitbake-dev/lib/bb/build.py
new file mode 100644
index 0000000000..ca7cfbc6bb
--- /dev/null
+++ b/bitbake-dev/lib/bb/build.py
@@ -0,0 +1,377 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# BitBake 'Build' implementation
5#
6# Core code for function execution and task handling in the
7# BitBake build tools.
8#
9# Copyright (C) 2003, 2004 Chris Larson
10#
11# Based on Gentoo's portage.py.
12#
13# This program is free software; you can redistribute it and/or modify
14# it under the terms of the GNU General Public License version 2 as
15# published by the Free Software Foundation.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License along
23# with this program; if not, write to the Free Software Foundation, Inc.,
24# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25#
26#Based on functions from the base bb module, Copyright 2003 Holger Schurig
27
28from bb import data, event, mkdirhier, utils
29import bb, os, sys
30
31# events
32class FuncFailed(Exception):
33 """
34 Executed function failed
35 First parameter a message
36 Second paramter is a logfile (optional)
37 """
38
39class EventException(Exception):
40 """Exception which is associated with an Event."""
41
42 def __init__(self, msg, event):
43 self.args = msg, event
44
45class TaskBase(event.Event):
46 """Base class for task events"""
47
48 def __init__(self, t, d ):
49 self._task = t
50 self._package = bb.data.getVar("PF", d, 1)
51 event.Event.__init__(self, d)
52 self._message = "package %s: task %s: %s" % (bb.data.getVar("PF", d, 1), t, bb.event.getName(self)[4:])
53
54 def getTask(self):
55 return self._task
56
57 def setTask(self, task):
58 self._task = task
59
60 task = property(getTask, setTask, None, "task property")
61
62class TaskStarted(TaskBase):
63 """Task execution started"""
64
65class TaskSucceeded(TaskBase):
66 """Task execution completed"""
67
68class TaskFailed(TaskBase):
69 """Task execution failed"""
70 def __init__(self, msg, logfile, t, d ):
71 self.logfile = logfile
72 self.msg = msg
73 TaskBase.__init__(self, t, d)
74
75class InvalidTask(TaskBase):
76 """Invalid Task"""
77
78# functions
79
80def exec_func(func, d, dirs = None):
81 """Execute an BB 'function'"""
82
83 body = data.getVar(func, d)
84 if not body:
85 return
86
87 flags = data.getVarFlags(func, d)
88 for item in ['deps', 'check', 'interactive', 'python', 'cleandirs', 'dirs', 'lockfiles', 'fakeroot']:
89 if not item in flags:
90 flags[item] = None
91
92 ispython = flags['python']
93
94 cleandirs = (data.expand(flags['cleandirs'], d) or "").split()
95 for cdir in cleandirs:
96 os.system("rm -rf %s" % cdir)
97
98 if dirs:
99 dirs = data.expand(dirs, d)
100 else:
101 dirs = (data.expand(flags['dirs'], d) or "").split()
102 for adir in dirs:
103 mkdirhier(adir)
104
105 if len(dirs) > 0:
106 adir = dirs[-1]
107 else:
108 adir = data.getVar('B', d, 1)
109
110 # Save current directory
111 try:
112 prevdir = os.getcwd()
113 except OSError:
114 prevdir = data.getVar('TOPDIR', d, True)
115
116 # Setup logfiles
117 t = data.getVar('T', d, 1)
118 if not t:
119 bb.msg.fatal(bb.msg.domain.Build, "T not set")
120 mkdirhier(t)
121 # Gross hack, FIXME
122 import random
123 logfile = "%s/log.%s.%s.%s" % (t, func, str(os.getpid()),random.random())
124 runfile = "%s/run.%s.%s" % (t, func, str(os.getpid()))
125
126 # Change to correct directory (if specified)
127 if adir and os.access(adir, os.F_OK):
128 os.chdir(adir)
129
130 # Handle logfiles
131 si = file('/dev/null', 'r')
132 try:
133 if bb.msg.debug_level['default'] > 0 or ispython:
134 so = os.popen("tee \"%s\"" % logfile, "w")
135 else:
136 so = file(logfile, 'w')
137 except OSError, e:
138 bb.msg.error(bb.msg.domain.Build, "opening log file: %s" % e)
139 pass
140
141 se = so
142
143 # Dup the existing fds so we dont lose them
144 osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
145 oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
146 ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
147
148 # Replace those fds with our own
149 os.dup2(si.fileno(), osi[1])
150 os.dup2(so.fileno(), oso[1])
151 os.dup2(se.fileno(), ose[1])
152
153 locks = []
154 lockfiles = (data.expand(flags['lockfiles'], d) or "").split()
155 for lock in lockfiles:
156 locks.append(bb.utils.lockfile(lock))
157
158 try:
159 # Run the function
160 if ispython:
161 exec_func_python(func, d, runfile, logfile)
162 else:
163 exec_func_shell(func, d, runfile, logfile, flags)
164
165 # Restore original directory
166 try:
167 os.chdir(prevdir)
168 except:
169 pass
170
171 finally:
172
173 # Unlock any lockfiles
174 for lock in locks:
175 bb.utils.unlockfile(lock)
176
177 # Restore the backup fds
178 os.dup2(osi[0], osi[1])
179 os.dup2(oso[0], oso[1])
180 os.dup2(ose[0], ose[1])
181
182 # Close our logs
183 si.close()
184 so.close()
185 se.close()
186
187 # Close the backup fds
188 os.close(osi[0])
189 os.close(oso[0])
190 os.close(ose[0])
191
192def exec_func_python(func, d, runfile, logfile):
193 """Execute a python BB 'function'"""
194 import re, os
195
196 bbfile = bb.data.getVar('FILE', d, 1)
197 tmp = "def " + func + "():\n%s" % data.getVar(func, d)
198 tmp += '\n' + func + '()'
199
200 f = open(runfile, "w")
201 f.write(tmp)
202 comp = utils.better_compile(tmp, func, bbfile)
203 g = {} # globals
204 g['bb'] = bb
205 g['os'] = os
206 g['d'] = d
207 utils.better_exec(comp, g, tmp, bbfile)
208
209
210def exec_func_shell(func, d, runfile, logfile, flags):
211 """Execute a shell BB 'function' Returns true if execution was successful.
212
213 For this, it creates a bash shell script in the tmp dectory, writes the local
214 data into it and finally executes. The output of the shell will end in a log file and stdout.
215
216 Note on directory behavior. The 'dirs' varflag should contain a list
217 of the directories you need created prior to execution. The last
218 item in the list is where we will chdir/cd to.
219 """
220
221 deps = flags['deps']
222 check = flags['check']
223 if check in globals():
224 if globals()[check](func, deps):
225 return
226
227 f = open(runfile, "w")
228 f.write("#!/bin/sh -e\n")
229 if bb.msg.debug_level['default'] > 0: f.write("set -x\n")
230 data.emit_env(f, d)
231
232 f.write("cd %s\n" % os.getcwd())
233 if func: f.write("%s\n" % func)
234 f.close()
235 os.chmod(runfile, 0775)
236 if not func:
237 bb.msg.error(bb.msg.domain.Build, "Function not specified")
238 raise FuncFailed("Function not specified for exec_func_shell")
239
240 # execute function
241 if flags['fakeroot']:
242 maybe_fakeroot = "PATH=\"%s\" fakeroot " % bb.data.getVar("PATH", d, 1)
243 else:
244 maybe_fakeroot = ''
245 lang_environment = "LC_ALL=C "
246 ret = os.system('%s%ssh -e %s' % (lang_environment, maybe_fakeroot, runfile))
247
248 if ret == 0:
249 return
250
251 bb.msg.error(bb.msg.domain.Build, "Function %s failed" % func)
252 raise FuncFailed("function %s failed" % func, logfile)
253
254
255def exec_task(task, d):
256 """Execute an BB 'task'
257
258 The primary difference between executing a task versus executing
259 a function is that a task exists in the task digraph, and therefore
260 has dependencies amongst other tasks."""
261
262 # Check whther this is a valid task
263 if not data.getVarFlag(task, 'task', d):
264 raise EventException("No such task", InvalidTask(task, d))
265
266 try:
267 bb.msg.debug(1, bb.msg.domain.Build, "Executing task %s" % task)
268 old_overrides = data.getVar('OVERRIDES', d, 0)
269 localdata = data.createCopy(d)
270 data.setVar('OVERRIDES', 'task_%s:%s' % (task, old_overrides), localdata)
271 data.update_data(localdata)
272 event.fire(TaskStarted(task, localdata))
273 exec_func(task, localdata)
274 event.fire(TaskSucceeded(task, localdata))
275 except FuncFailed, message:
276 # Try to extract the optional logfile
277 try:
278 (msg, logfile) = message
279 except:
280 logfile = None
281 msg = message
282 bb.msg.note(1, bb.msg.domain.Build, "Task failed: %s" % message )
283 failedevent = TaskFailed(msg, logfile, task, d)
284 event.fire(failedevent)
285 raise EventException("Function failed in task: %s" % message, failedevent)
286
287 # make stamp, or cause event and raise exception
288 if not data.getVarFlag(task, 'nostamp', d) and not data.getVarFlag(task, 'selfstamp', d):
289 make_stamp(task, d)
290
291def extract_stamp(d, fn):
292 """
293 Extracts stamp format which is either a data dictonary (fn unset)
294 or a dataCache entry (fn set).
295 """
296 if fn:
297 return d.stamp[fn]
298 return data.getVar('STAMP', d, 1)
299
300def stamp_internal(task, d, file_name):
301 """
302 Internal stamp helper function
303 Removes any stamp for the given task
304 Makes sure the stamp directory exists
305 Returns the stamp path+filename
306 """
307 stamp = extract_stamp(d, file_name)
308 if not stamp:
309 return
310 stamp = "%s.%s" % (stamp, task)
311 mkdirhier(os.path.dirname(stamp))
312 # Remove the file and recreate to force timestamp
313 # change on broken NFS filesystems
314 if os.access(stamp, os.F_OK):
315 os.remove(stamp)
316 return stamp
317
318def make_stamp(task, d, file_name = None):
319 """
320 Creates/updates a stamp for a given task
321 (d can be a data dict or dataCache)
322 """
323 stamp = stamp_internal(task, d, file_name)
324 if stamp:
325 f = open(stamp, "w")
326 f.close()
327
328def del_stamp(task, d, file_name = None):
329 """
330 Removes a stamp for a given task
331 (d can be a data dict or dataCache)
332 """
333 stamp_internal(task, d, file_name)
334
335def add_tasks(tasklist, d):
336 task_deps = data.getVar('_task_deps', d)
337 if not task_deps:
338 task_deps = {}
339 if not 'tasks' in task_deps:
340 task_deps['tasks'] = []
341 if not 'parents' in task_deps:
342 task_deps['parents'] = {}
343
344 for task in tasklist:
345 task = data.expand(task, d)
346 data.setVarFlag(task, 'task', 1, d)
347
348 if not task in task_deps['tasks']:
349 task_deps['tasks'].append(task)
350
351 flags = data.getVarFlags(task, d)
352 def getTask(name):
353 if not name in task_deps:
354 task_deps[name] = {}
355 if name in flags:
356 deptask = data.expand(flags[name], d)
357 task_deps[name][task] = deptask
358 getTask('depends')
359 getTask('deptask')
360 getTask('rdeptask')
361 getTask('recrdeptask')
362 getTask('nostamp')
363 task_deps['parents'][task] = []
364 for dep in flags['deps']:
365 dep = data.expand(dep, d)
366 task_deps['parents'][task].append(dep)
367
368 # don't assume holding a reference
369 data.setVar('_task_deps', task_deps, d)
370
371def remove_task(task, kill, d):
372 """Remove an BB 'task'.
373
374 If kill is 1, also remove tasks that depend on this task."""
375
376 data.delVarFlag(task, 'task', d)
377