diff options
author | Richard Purdie <richard@openedhand.com> | 2007-01-08 23:53:01 +0000 |
---|---|---|
committer | Richard Purdie <richard@openedhand.com> | 2007-01-08 23:53:01 +0000 |
commit | f5665d5bfcfb13d01da9e4c7d5046453e80f7baf (patch) | |
tree | b8908549afaf3006bf3763419711090ac999c2a4 /bitbake/lib/bb/cooker.py | |
parent | aec95de5f7dca2afa3a4a0bdb0d4d553c13f680d (diff) | |
download | poky-f5665d5bfcfb13d01da9e4c7d5046453e80f7baf.tar.gz |
bitbake: Sync with upstream.
* File licence headers were sanitised causing most of the diff.
* cooker.py was created from bin/bitbake.
* cvs fetcher port option was added
* The -f force option was fixed to work correctly
* Multiple entries in rrecrdeps are now handled correctly
(allows adding do_deploy to image depends)
git-svn-id: https://svn.o-hand.com/repos/poky/trunk@1129 311d38ba-8fff-0310-9ca6-ca027cbcb966
Diffstat (limited to 'bitbake/lib/bb/cooker.py')
-rw-r--r-- | bitbake/lib/bb/cooker.py | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py new file mode 100644 index 0000000000..8a9c588633 --- /dev/null +++ b/bitbake/lib/bb/cooker.py | |||
@@ -0,0 +1,665 @@ | |||
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 | # Copyright (C) 2003, 2004 Chris Larson | ||
6 | # Copyright (C) 2003, 2004 Phil Blundell | ||
7 | # Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer | ||
8 | # Copyright (C) 2005 Holger Hans Peter Freyther | ||
9 | # Copyright (C) 2005 ROAD GmbH | ||
10 | # Copyright (C) 2006 Richard Purdie | ||
11 | # | ||
12 | # This program is free software; you can redistribute it and/or modify | ||
13 | # it under the terms of the GNU General Public License version 2 as | ||
14 | # published by the Free Software Foundation. | ||
15 | # | ||
16 | # This program is distributed in the hope that it will be useful, | ||
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | # GNU General Public License for more details. | ||
20 | # | ||
21 | # You should have received a copy of the GNU General Public License along | ||
22 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
23 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
24 | |||
25 | import sys, os, getopt, glob, copy, os.path, re, time | ||
26 | import bb | ||
27 | from bb import utils, data, parse, event, cache, providers, taskdata, runqueue | ||
28 | from sets import Set | ||
29 | import itertools | ||
30 | |||
31 | parsespin = itertools.cycle( r'|/-\\' ) | ||
32 | |||
33 | #============================================================================# | ||
34 | # BBStatistics | ||
35 | #============================================================================# | ||
36 | class BBStatistics: | ||
37 | """ | ||
38 | Manage build statistics for one run | ||
39 | """ | ||
40 | def __init__(self ): | ||
41 | self.attempt = 0 | ||
42 | self.success = 0 | ||
43 | self.fail = 0 | ||
44 | self.deps = 0 | ||
45 | |||
46 | def show( self ): | ||
47 | print "Build statistics:" | ||
48 | print " Attempted builds: %d" % self.attempt | ||
49 | if self.fail: | ||
50 | print " Failed builds: %d" % self.fail | ||
51 | if self.deps: | ||
52 | print " Dependencies not satisfied: %d" % self.deps | ||
53 | if self.fail or self.deps: return 1 | ||
54 | else: return 0 | ||
55 | |||
56 | #============================================================================# | ||
57 | # BBCooker | ||
58 | #============================================================================# | ||
59 | class BBCooker: | ||
60 | """ | ||
61 | Manages one bitbake build run | ||
62 | """ | ||
63 | |||
64 | Statistics = BBStatistics # make it visible from the shell | ||
65 | |||
66 | def __init__( self ): | ||
67 | self.build_cache_fail = [] | ||
68 | self.build_cache = [] | ||
69 | self.stats = BBStatistics() | ||
70 | self.status = None | ||
71 | |||
72 | self.cache = None | ||
73 | self.bb_cache = None | ||
74 | |||
75 | def tryBuildPackage(self, fn, item, task, the_data, build_depends): | ||
76 | """ | ||
77 | Build one task of a package, optionally build following task depends | ||
78 | """ | ||
79 | bb.event.fire(bb.event.PkgStarted(item, the_data)) | ||
80 | try: | ||
81 | self.stats.attempt += 1 | ||
82 | if not build_depends: | ||
83 | bb.data.setVarFlag('do_%s' % task, 'dontrundeps', 1, the_data) | ||
84 | if not self.configuration.dry_run: | ||
85 | bb.build.exec_task('do_%s' % task, the_data) | ||
86 | bb.event.fire(bb.event.PkgSucceeded(item, the_data)) | ||
87 | self.build_cache.append(fn) | ||
88 | return True | ||
89 | except bb.build.FuncFailed: | ||
90 | self.stats.fail += 1 | ||
91 | bb.msg.error(bb.msg.domain.Build, "task stack execution failed") | ||
92 | bb.event.fire(bb.event.PkgFailed(item, the_data)) | ||
93 | self.build_cache_fail.append(fn) | ||
94 | raise | ||
95 | except bb.build.EventException, e: | ||
96 | self.stats.fail += 1 | ||
97 | event = e.args[1] | ||
98 | bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event)) | ||
99 | bb.event.fire(bb.event.PkgFailed(item, the_data)) | ||
100 | self.build_cache_fail.append(fn) | ||
101 | raise | ||
102 | |||
103 | def tryBuild( self, fn, build_depends): | ||
104 | """ | ||
105 | Build a provider and its dependencies. | ||
106 | build_depends is a list of previous build dependencies (not runtime) | ||
107 | If build_depends is empty, we're dealing with a runtime depends | ||
108 | """ | ||
109 | |||
110 | the_data = self.bb_cache.loadDataFull(fn, self.configuration.data) | ||
111 | |||
112 | item = self.status.pkg_fn[fn] | ||
113 | |||
114 | if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data): | ||
115 | self.build_cache.append(fn) | ||
116 | return True | ||
117 | |||
118 | return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data, build_depends) | ||
119 | |||
120 | def showVersions( self ): | ||
121 | pkg_pn = self.status.pkg_pn | ||
122 | preferred_versions = {} | ||
123 | latest_versions = {} | ||
124 | |||
125 | # Sort by priority | ||
126 | for pn in pkg_pn.keys(): | ||
127 | (last_ver,last_file,pref_ver,pref_file) = bb.providers.findBestProvider(pn, self.configuration.data, self.status) | ||
128 | preferred_versions[pn] = (pref_ver, pref_file) | ||
129 | latest_versions[pn] = (last_ver, last_file) | ||
130 | |||
131 | pkg_list = pkg_pn.keys() | ||
132 | pkg_list.sort() | ||
133 | |||
134 | for p in pkg_list: | ||
135 | pref = preferred_versions[p] | ||
136 | latest = latest_versions[p] | ||
137 | |||
138 | if pref != latest: | ||
139 | prefstr = pref[0][0] + "-" + pref[0][1] | ||
140 | else: | ||
141 | prefstr = "" | ||
142 | |||
143 | print "%-30s %20s %20s" % (p, latest[0][0] + "-" + latest[0][1], | ||
144 | prefstr) | ||
145 | |||
146 | |||
147 | def showEnvironment( self ): | ||
148 | """Show the outer or per-package environment""" | ||
149 | if self.configuration.buildfile: | ||
150 | self.cb = None | ||
151 | self.bb_cache = bb.cache.init(self) | ||
152 | try: | ||
153 | self.configuration.data = self.bb_cache.loadDataFull(self.configuration.buildfile, self.configuration.data) | ||
154 | except IOError, e: | ||
155 | bb.msg.fatal(bb.msg.domain.Parsing, "Unable to read %s: %s" % ( self.configuration.buildfile, e )) | ||
156 | except Exception, e: | ||
157 | bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e) | ||
158 | # emit variables and shell functions | ||
159 | try: | ||
160 | data.update_data( self.configuration.data ) | ||
161 | data.emit_env(sys.__stdout__, self.configuration.data, True) | ||
162 | except Exception, e: | ||
163 | bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e) | ||
164 | # emit the metadata which isnt valid shell | ||
165 | data.expandKeys( self.configuration.data ) | ||
166 | for e in self.configuration.data.keys(): | ||
167 | if data.getVarFlag( e, 'python', self.configuration.data ): | ||
168 | sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, self.configuration.data, 1))) | ||
169 | |||
170 | def generateDotGraph( self, pkgs_to_build, ignore_deps ): | ||
171 | """ | ||
172 | Generate a task dependency graph. | ||
173 | |||
174 | pkgs_to_build A list of packages that needs to be built | ||
175 | ignore_deps A list of names where processing of dependencies | ||
176 | should be stopped. e.g. dependencies that get | ||
177 | """ | ||
178 | |||
179 | for dep in ignore_deps: | ||
180 | self.status.ignored_dependencies.add(dep) | ||
181 | |||
182 | localdata = data.createCopy(self.configuration.data) | ||
183 | bb.data.update_data(localdata) | ||
184 | bb.data.expandKeys(localdata) | ||
185 | taskdata = bb.taskdata.TaskData(self.configuration.abort) | ||
186 | |||
187 | runlist = [] | ||
188 | try: | ||
189 | for k in pkgs_to_build: | ||
190 | taskdata.add_provider(localdata, self.status, k) | ||
191 | runlist.append([k, "do_%s" % self.configuration.cmd]) | ||
192 | taskdata.add_unresolved(localdata, self.status) | ||
193 | except bb.providers.NoProvider: | ||
194 | sys.exit(1) | ||
195 | rq = bb.runqueue.RunQueue() | ||
196 | rq.prepare_runqueue(self, self.configuration.data, self.status, taskdata, runlist) | ||
197 | |||
198 | seen_fnids = [] | ||
199 | depends_file = file('depends.dot', 'w' ) | ||
200 | tdepends_file = file('task-depends.dot', 'w' ) | ||
201 | print >> depends_file, "digraph depends {" | ||
202 | print >> tdepends_file, "digraph depends {" | ||
203 | rq.prio_map.reverse() | ||
204 | for task1 in range(len(rq.runq_fnid)): | ||
205 | task = rq.prio_map[task1] | ||
206 | taskname = rq.runq_task[task] | ||
207 | fnid = rq.runq_fnid[task] | ||
208 | fn = taskdata.fn_index[fnid] | ||
209 | pn = self.status.pkg_fn[fn] | ||
210 | version = self.bb_cache.getVar('PV', fn, True ) + '-' + self.bb_cache.getVar('PR', fn, True) | ||
211 | print >> tdepends_file, '"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn) | ||
212 | for dep in rq.runq_depends[task]: | ||
213 | depfn = taskdata.fn_index[rq.runq_fnid[dep]] | ||
214 | deppn = self.status.pkg_fn[depfn] | ||
215 | print >> tdepends_file, '"%s.%s" -> "%s.%s"' % (pn, rq.runq_task[task], deppn, rq.runq_task[dep]) | ||
216 | if fnid not in seen_fnids: | ||
217 | seen_fnids.append(fnid) | ||
218 | packages = [] | ||
219 | print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn) | ||
220 | for depend in self.status.deps[fn]: | ||
221 | print >> depends_file, '"%s" -> "%s"' % (pn, depend) | ||
222 | rdepends = self.status.rundeps[fn] | ||
223 | for package in rdepends: | ||
224 | for rdepend in rdepends[package]: | ||
225 | print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend) | ||
226 | packages.append(package) | ||
227 | rrecs = self.status.runrecs[fn] | ||
228 | for package in rrecs: | ||
229 | for rdepend in rrecs[package]: | ||
230 | print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend) | ||
231 | if not package in packages: | ||
232 | packages.append(package) | ||
233 | for package in packages: | ||
234 | if package != pn: | ||
235 | print >> depends_file, '"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn) | ||
236 | for depend in self.status.deps[fn]: | ||
237 | print >> depends_file, '"%s" -> "%s"' % (package, depend) | ||
238 | # Prints a flattened form of the above where subpackages of a package are merged into the main pn | ||
239 | #print >> depends_file, '"%s" [label="%s %s\\n%s\\n%s"]' % (pn, pn, taskname, version, fn) | ||
240 | #for rdep in taskdata.rdepids[fnid]: | ||
241 | # print >> depends_file, '"%s" -> "%s" [style=dashed]' % (pn, taskdata.run_names_index[rdep]) | ||
242 | #for dep in taskdata.depids[fnid]: | ||
243 | # print >> depends_file, '"%s" -> "%s"' % (pn, taskdata.build_names_index[dep]) | ||
244 | print >> depends_file, "}" | ||
245 | print >> tdepends_file, "}" | ||
246 | bb.msg.note(1, bb.msg.domain.Collection, "Dependencies saved to 'depends.dot'") | ||
247 | bb.msg.note(1, bb.msg.domain.Collection, "Task dependencies saved to 'task-depends.dot'") | ||
248 | |||
249 | def buildDepgraph( self ): | ||
250 | all_depends = self.status.all_depends | ||
251 | pn_provides = self.status.pn_provides | ||
252 | |||
253 | localdata = data.createCopy(self.configuration.data) | ||
254 | bb.data.update_data(localdata) | ||
255 | bb.data.expandKeys(localdata) | ||
256 | |||
257 | def calc_bbfile_priority(filename): | ||
258 | for (regex, pri) in self.status.bbfile_config_priorities: | ||
259 | if regex.match(filename): | ||
260 | return pri | ||
261 | return 0 | ||
262 | |||
263 | # Handle PREFERRED_PROVIDERS | ||
264 | for p in (bb.data.getVar('PREFERRED_PROVIDERS', localdata, 1) or "").split(): | ||
265 | (providee, provider) = p.split(':') | ||
266 | if providee in self.status.preferred and self.status.preferred[providee] != provider: | ||
267 | bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee])) | ||
268 | self.status.preferred[providee] = provider | ||
269 | |||
270 | # Calculate priorities for each file | ||
271 | for p in self.status.pkg_fn.keys(): | ||
272 | self.status.bbfile_priority[p] = calc_bbfile_priority(p) | ||
273 | |||
274 | def buildWorldTargetList(self): | ||
275 | """ | ||
276 | Build package list for "bitbake world" | ||
277 | """ | ||
278 | all_depends = self.status.all_depends | ||
279 | pn_provides = self.status.pn_provides | ||
280 | bb.msg.debug(1, bb.msg.domain.Parsing, "collating packages for \"world\"") | ||
281 | for f in self.status.possible_world: | ||
282 | terminal = True | ||
283 | pn = self.status.pkg_fn[f] | ||
284 | |||
285 | for p in pn_provides[pn]: | ||
286 | if p.startswith('virtual/'): | ||
287 | bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to %s provider starting with virtual/" % (f, p)) | ||
288 | terminal = False | ||
289 | break | ||
290 | for pf in self.status.providers[p]: | ||
291 | if self.status.pkg_fn[pf] != pn: | ||
292 | bb.msg.debug(2, bb.msg.domain.Parsing, "World build skipping %s due to both us and %s providing %s" % (f, pf, p)) | ||
293 | terminal = False | ||
294 | break | ||
295 | if terminal: | ||
296 | self.status.world_target.add(pn) | ||
297 | |||
298 | # drop reference count now | ||
299 | self.status.possible_world = None | ||
300 | self.status.all_depends = None | ||
301 | |||
302 | def myProgressCallback( self, x, y, f, from_cache ): | ||
303 | """Update any tty with the progress change""" | ||
304 | if os.isatty(sys.stdout.fileno()): | ||
305 | sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) ) | ||
306 | sys.stdout.flush() | ||
307 | else: | ||
308 | if x == 1: | ||
309 | sys.stdout.write("Parsing .bb files, please wait...") | ||
310 | sys.stdout.flush() | ||
311 | if x == y: | ||
312 | sys.stdout.write("done.") | ||
313 | sys.stdout.flush() | ||
314 | |||
315 | def interactiveMode( self ): | ||
316 | """Drop off into a shell""" | ||
317 | try: | ||
318 | from bb import shell | ||
319 | except ImportError, details: | ||
320 | bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details ) | ||
321 | else: | ||
322 | bb.data.update_data( self.configuration.data ) | ||
323 | bb.data.expandKeys( self.configuration.data ) | ||
324 | shell.start( self ) | ||
325 | sys.exit( 0 ) | ||
326 | |||
327 | def parseConfigurationFile( self, afile ): | ||
328 | try: | ||
329 | self.configuration.data = bb.parse.handle( afile, self.configuration.data ) | ||
330 | |||
331 | # Add the handlers we inherited by INHERIT | ||
332 | # we need to do this manually as it is not guranteed | ||
333 | # we will pick up these classes... as we only INHERIT | ||
334 | # on .inc and .bb files but not on .conf | ||
335 | data = bb.data.createCopy( self.configuration.data ) | ||
336 | inherits = ["base"] + (bb.data.getVar('INHERIT', data, True ) or "").split() | ||
337 | for inherit in inherits: | ||
338 | data = bb.parse.handle( os.path.join('classes', '%s.bbclass' % inherit ), data, True ) | ||
339 | |||
340 | # FIXME: This assumes that we included at least one .inc file | ||
341 | for var in bb.data.keys(data): | ||
342 | if bb.data.getVarFlag(var, 'handler', data): | ||
343 | bb.event.register(var,bb.data.getVar(var, data)) | ||
344 | |||
345 | except IOError: | ||
346 | bb.msg.fatal(bb.msg.domain.Parsing, "Unable to open %s" % afile ) | ||
347 | except bb.parse.ParseError, details: | ||
348 | bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) ) | ||
349 | |||
350 | def handleCollections( self, collections ): | ||
351 | """Handle collections""" | ||
352 | if collections: | ||
353 | collection_list = collections.split() | ||
354 | for c in collection_list: | ||
355 | regex = bb.data.getVar("BBFILE_PATTERN_%s" % c, self.configuration.data, 1) | ||
356 | if regex == None: | ||
357 | bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s not defined" % c) | ||
358 | continue | ||
359 | priority = bb.data.getVar("BBFILE_PRIORITY_%s" % c, self.configuration.data, 1) | ||
360 | if priority == None: | ||
361 | bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PRIORITY_%s not defined" % c) | ||
362 | continue | ||
363 | try: | ||
364 | cre = re.compile(regex) | ||
365 | except re.error: | ||
366 | bb.msg.error(bb.msg.domain.Parsing, "BBFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex)) | ||
367 | continue | ||
368 | try: | ||
369 | pri = int(priority) | ||
370 | self.status.bbfile_config_priorities.append((cre, pri)) | ||
371 | except ValueError: | ||
372 | bb.msg.error(bb.msg.domain.Parsing, "invalid value for BBFILE_PRIORITY_%s: \"%s\"" % (c, priority)) | ||
373 | |||
374 | |||
375 | def cook(self, configuration): | ||
376 | """ | ||
377 | We are building stuff here. We do the building | ||
378 | from here. By default we try to execute task | ||
379 | build. | ||
380 | """ | ||
381 | |||
382 | self.configuration = configuration | ||
383 | |||
384 | if self.configuration.verbose: | ||
385 | bb.msg.set_verbose(True) | ||
386 | |||
387 | if self.configuration.debug: | ||
388 | bb.msg.set_debug_level(self.configuration.debug) | ||
389 | else: | ||
390 | bb.msg.set_debug_level(0) | ||
391 | |||
392 | if self.configuration.debug_domains: | ||
393 | bb.msg.set_debug_domains(self.configuration.debug_domains) | ||
394 | |||
395 | self.configuration.data = bb.data.init() | ||
396 | |||
397 | for f in self.configuration.file: | ||
398 | self.parseConfigurationFile( f ) | ||
399 | |||
400 | self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) ) | ||
401 | |||
402 | if not self.configuration.cmd: | ||
403 | self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data) or "build" | ||
404 | |||
405 | # | ||
406 | # Special updated configuration we use for firing events | ||
407 | # | ||
408 | self.configuration.event_data = bb.data.createCopy(self.configuration.data) | ||
409 | bb.data.update_data(self.configuration.event_data) | ||
410 | |||
411 | if self.configuration.show_environment: | ||
412 | self.showEnvironment() | ||
413 | sys.exit( 0 ) | ||
414 | |||
415 | # inject custom variables | ||
416 | if not bb.data.getVar("BUILDNAME", self.configuration.data): | ||
417 | bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data) | ||
418 | bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data) | ||
419 | |||
420 | buildname = bb.data.getVar("BUILDNAME", self.configuration.data) | ||
421 | |||
422 | if self.configuration.interactive: | ||
423 | self.interactiveMode() | ||
424 | |||
425 | if self.configuration.buildfile is not None: | ||
426 | bf = os.path.abspath( self.configuration.buildfile ) | ||
427 | try: | ||
428 | os.stat(bf) | ||
429 | except OSError: | ||
430 | (filelist, masked) = self.collect_bbfiles() | ||
431 | regexp = re.compile(self.configuration.buildfile) | ||
432 | matches = [] | ||
433 | for f in filelist: | ||
434 | if regexp.search(f) and os.path.isfile(f): | ||
435 | bf = f | ||
436 | matches.append(f) | ||
437 | if len(matches) != 1: | ||
438 | bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (self.configuration.buildfile, len(matches))) | ||
439 | for f in matches: | ||
440 | bb.msg.error(bb.msg.domain.Parsing, " %s" % f) | ||
441 | sys.exit(1) | ||
442 | bf = matches[0] | ||
443 | |||
444 | bbfile_data = bb.parse.handle(bf, self.configuration.data) | ||
445 | |||
446 | # Remove stamp for target if force mode active | ||
447 | if self.configuration.force: | ||
448 | bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (self.configuration.cmd, bf)) | ||
449 | bb.build.del_stamp('do_%s' % self.configuration.cmd, bbfile_data) | ||
450 | |||
451 | item = bb.data.getVar('PN', bbfile_data, 1) | ||
452 | try: | ||
453 | self.tryBuildPackage(bf, item, self.configuration.cmd, bbfile_data, True) | ||
454 | except bb.build.EventException: | ||
455 | bb.msg.error(bb.msg.domain.Build, "Build of '%s' failed" % item ) | ||
456 | |||
457 | sys.exit( self.stats.show() ) | ||
458 | |||
459 | # initialise the parsing status now we know we will need deps | ||
460 | self.status = bb.cache.CacheData() | ||
461 | |||
462 | ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or "" | ||
463 | self.status.ignored_dependencies = Set( ignore.split() ) | ||
464 | |||
465 | self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) ) | ||
466 | |||
467 | pkgs_to_build = self.configuration.pkgs_to_build | ||
468 | |||
469 | bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, 1) | ||
470 | if bbpkgs: | ||
471 | pkgs_to_build.extend(bbpkgs.split()) | ||
472 | if len(pkgs_to_build) == 0 and not self.configuration.show_versions \ | ||
473 | and not self.configuration.show_environment: | ||
474 | print "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help'" | ||
475 | print "for usage information." | ||
476 | sys.exit(0) | ||
477 | |||
478 | # Import Psyco if available and not disabled | ||
479 | if not self.configuration.disable_psyco: | ||
480 | try: | ||
481 | import psyco | ||
482 | except ImportError: | ||
483 | bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.") | ||
484 | else: | ||
485 | psyco.bind( self.parse_bbfiles ) | ||
486 | else: | ||
487 | bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.") | ||
488 | |||
489 | try: | ||
490 | bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files") | ||
491 | (filelist, masked) = self.collect_bbfiles() | ||
492 | self.parse_bbfiles(filelist, masked, self.myProgressCallback) | ||
493 | bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete") | ||
494 | |||
495 | if self.configuration.parse_only: | ||
496 | bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only. Exiting.") | ||
497 | return | ||
498 | |||
499 | |||
500 | self.buildDepgraph() | ||
501 | |||
502 | if self.configuration.show_versions: | ||
503 | self.showVersions() | ||
504 | sys.exit( 0 ) | ||
505 | if 'world' in pkgs_to_build: | ||
506 | self.buildWorldTargetList() | ||
507 | pkgs_to_build.remove('world') | ||
508 | for t in self.status.world_target: | ||
509 | pkgs_to_build.append(t) | ||
510 | |||
511 | if self.configuration.dot_graph: | ||
512 | self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps ) | ||
513 | sys.exit( 0 ) | ||
514 | |||
515 | bb.event.fire(bb.event.BuildStarted(buildname, pkgs_to_build, self.configuration.event_data)) | ||
516 | |||
517 | localdata = data.createCopy(self.configuration.data) | ||
518 | bb.data.update_data(localdata) | ||
519 | bb.data.expandKeys(localdata) | ||
520 | |||
521 | taskdata = bb.taskdata.TaskData(self.configuration.abort) | ||
522 | |||
523 | runlist = [] | ||
524 | try: | ||
525 | for k in pkgs_to_build: | ||
526 | taskdata.add_provider(localdata, self.status, k) | ||
527 | runlist.append([k, "do_%s" % self.configuration.cmd]) | ||
528 | taskdata.add_unresolved(localdata, self.status) | ||
529 | except bb.providers.NoProvider: | ||
530 | sys.exit(1) | ||
531 | |||
532 | rq = bb.runqueue.RunQueue() | ||
533 | rq.prepare_runqueue(self, self.configuration.data, self.status, taskdata, runlist) | ||
534 | try: | ||
535 | failures = rq.execute_runqueue(self, self.configuration.data, self.status, taskdata, runlist) | ||
536 | except runqueue.TaskFailure, fnids: | ||
537 | for fnid in fnids: | ||
538 | bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) | ||
539 | sys.exit(1) | ||
540 | bb.event.fire(bb.event.BuildCompleted(buildname, pkgs_to_build, self.configuration.event_data, failures)) | ||
541 | |||
542 | sys.exit( self.stats.show() ) | ||
543 | |||
544 | except KeyboardInterrupt: | ||
545 | bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.") | ||
546 | sys.exit(1) | ||
547 | |||
548 | def get_bbfiles( self, path = os.getcwd() ): | ||
549 | """Get list of default .bb files by reading out the current directory""" | ||
550 | contents = os.listdir(path) | ||
551 | bbfiles = [] | ||
552 | for f in contents: | ||
553 | (root, ext) = os.path.splitext(f) | ||
554 | if ext == ".bb": | ||
555 | bbfiles.append(os.path.abspath(os.path.join(os.getcwd(),f))) | ||
556 | return bbfiles | ||
557 | |||
558 | def find_bbfiles( self, path ): | ||
559 | """Find all the .bb files in a directory (uses find)""" | ||
560 | findcmd = 'find ' + path + ' -name *.bb | grep -v SCCS/' | ||
561 | try: | ||
562 | finddata = os.popen(findcmd) | ||
563 | except OSError: | ||
564 | return [] | ||
565 | return finddata.readlines() | ||
566 | |||
567 | def collect_bbfiles( self ): | ||
568 | """Collect all available .bb build files""" | ||
569 | parsed, cached, skipped, masked = 0, 0, 0, 0 | ||
570 | self.bb_cache = bb.cache.init(self) | ||
571 | |||
572 | files = (data.getVar( "BBFILES", self.configuration.data, 1 ) or "").split() | ||
573 | data.setVar("BBFILES", " ".join(files), self.configuration.data) | ||
574 | |||
575 | if not len(files): | ||
576 | files = self.get_bbfiles() | ||
577 | |||
578 | if not len(files): | ||
579 | bb.msg.error(bb.msg.domain.Collection, "no files to build.") | ||
580 | |||
581 | newfiles = [] | ||
582 | for f in files: | ||
583 | if os.path.isdir(f): | ||
584 | dirfiles = self.find_bbfiles(f) | ||
585 | if dirfiles: | ||
586 | newfiles += dirfiles | ||
587 | continue | ||
588 | newfiles += glob.glob(f) or [ f ] | ||
589 | |||
590 | bbmask = bb.data.getVar('BBMASK', self.configuration.data, 1) | ||
591 | |||
592 | if not bbmask: | ||
593 | return (newfiles, 0) | ||
594 | |||
595 | try: | ||
596 | bbmask_compiled = re.compile(bbmask) | ||
597 | except sre_constants.error: | ||
598 | bb.msg.fatal(bb.msg.domain.Collection, "BBMASK is not a valid regular expression.") | ||
599 | |||
600 | finalfiles = [] | ||
601 | for i in xrange( len( newfiles ) ): | ||
602 | f = newfiles[i] | ||
603 | if bbmask and bbmask_compiled.search(f): | ||
604 | bb.msg.debug(1, bb.msg.domain.Collection, "skipping masked file %s" % f) | ||
605 | masked += 1 | ||
606 | continue | ||
607 | finalfiles.append(f) | ||
608 | |||
609 | return (finalfiles, masked) | ||
610 | |||
611 | def parse_bbfiles(self, filelist, masked, progressCallback = None): | ||
612 | parsed, cached, skipped = 0, 0, 0 | ||
613 | for i in xrange( len( filelist ) ): | ||
614 | f = filelist[i] | ||
615 | |||
616 | bb.msg.debug(1, bb.msg.domain.Collection, "parsing %s" % f) | ||
617 | |||
618 | # read a file's metadata | ||
619 | try: | ||
620 | fromCache, skip = self.bb_cache.loadData(f, self.configuration.data) | ||
621 | if skip: | ||
622 | skipped += 1 | ||
623 | bb.msg.debug(2, bb.msg.domain.Collection, "skipping %s" % f) | ||
624 | self.bb_cache.skip(f) | ||
625 | continue | ||
626 | elif fromCache: cached += 1 | ||
627 | else: parsed += 1 | ||
628 | deps = None | ||
629 | |||
630 | # Disabled by RP as was no longer functional | ||
631 | # allow metadata files to add items to BBFILES | ||
632 | #data.update_data(self.pkgdata[f]) | ||
633 | #addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None | ||
634 | #if addbbfiles: | ||
635 | # for aof in addbbfiles.split(): | ||
636 | # if not files.count(aof): | ||
637 | # if not os.path.isabs(aof): | ||
638 | # aof = os.path.join(os.path.dirname(f),aof) | ||
639 | # files.append(aof) | ||
640 | |||
641 | self.bb_cache.handle_data(f, self.status) | ||
642 | |||
643 | # now inform the caller | ||
644 | if progressCallback is not None: | ||
645 | progressCallback( i + 1, len( filelist ), f, fromCache ) | ||
646 | |||
647 | except IOError, e: | ||
648 | self.bb_cache.remove(f) | ||
649 | bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e)) | ||
650 | pass | ||
651 | except KeyboardInterrupt: | ||
652 | self.bb_cache.sync() | ||
653 | raise | ||
654 | except Exception, e: | ||
655 | self.bb_cache.remove(f) | ||
656 | bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f)) | ||
657 | except: | ||
658 | self.bb_cache.remove(f) | ||
659 | raise | ||
660 | |||
661 | if progressCallback is not None: | ||
662 | print "\r" # need newline after Handling Bitbake files message | ||
663 | bb.msg.note(1, bb.msg.domain.Collection, "Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked )) | ||
664 | |||
665 | self.bb_cache.sync() | ||