summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/cooker.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/cooker.py')
-rw-r--r--bitbake/lib/bb/cooker.py761
1 files changed, 482 insertions, 279 deletions
diff --git a/bitbake/lib/bb/cooker.py b/bitbake/lib/bb/cooker.py
index 14ccfb59aa..8036d7e9d5 100644
--- a/bitbake/lib/bb/cooker.py
+++ b/bitbake/lib/bb/cooker.py
@@ -7,7 +7,7 @@
7# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer 7# Copyright (C) 2003 - 2005 Michael 'Mickey' Lauer
8# Copyright (C) 2005 Holger Hans Peter Freyther 8# Copyright (C) 2005 Holger Hans Peter Freyther
9# Copyright (C) 2005 ROAD GmbH 9# Copyright (C) 2005 ROAD GmbH
10# Copyright (C) 2006 Richard Purdie 10# Copyright (C) 2006 - 2007 Richard Purdie
11# 11#
12# This program is free software; you can redistribute it and/or modify 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 13# it under the terms of the GNU General Public License version 2 as
@@ -25,9 +25,35 @@
25import sys, os, getopt, glob, copy, os.path, re, time 25import sys, os, getopt, glob, copy, os.path, re, time
26import bb 26import bb
27from bb import utils, data, parse, event, cache, providers, taskdata, runqueue 27from bb import utils, data, parse, event, cache, providers, taskdata, runqueue
28from bb import command
29import bb.server.xmlrpc
28import itertools, sre_constants 30import itertools, sre_constants
29 31
30parsespin = itertools.cycle( r'|/-\\' ) 32class MultipleMatches(Exception):
33 """
34 Exception raised when multiple file matches are found
35 """
36
37class ParsingErrorsFound(Exception):
38 """
39 Exception raised when parsing errors are found
40 """
41
42class NothingToBuild(Exception):
43 """
44 Exception raised when there is nothing to build
45 """
46
47
48# Different states cooker can be in
49cookerClean = 1
50cookerParsing = 2
51cookerParsed = 3
52
53# Different action states the cooker can be in
54cookerRun = 1 # Cooker is running normally
55cookerShutdown = 2 # Active tasks should be brought to a controlled stop
56cookerStop = 3 # Stop, now!
31 57
32#============================================================================# 58#============================================================================#
33# BBCooker 59# BBCooker
@@ -37,12 +63,14 @@ class BBCooker:
37 Manages one bitbake build run 63 Manages one bitbake build run
38 """ 64 """
39 65
40 def __init__(self, configuration): 66 def __init__(self, configuration, server):
41 self.status = None 67 self.status = None
42 68
43 self.cache = None 69 self.cache = None
44 self.bb_cache = None 70 self.bb_cache = None
45 71
72 self.server = server.BitBakeServer(self)
73
46 self.configuration = configuration 74 self.configuration = configuration
47 75
48 if self.configuration.verbose: 76 if self.configuration.verbose:
@@ -58,17 +86,15 @@ class BBCooker:
58 86
59 self.configuration.data = bb.data.init() 87 self.configuration.data = bb.data.init()
60 88
61 def parseConfiguration(self):
62
63 bb.data.inheritFromOS(self.configuration.data) 89 bb.data.inheritFromOS(self.configuration.data)
64 90
65 # Add conf/bitbake.conf to the list of configuration files to read 91 for f in self.configuration.file:
66 self.configuration.file.append( os.path.join( "conf", "bitbake.conf" ) ) 92 self.parseConfigurationFile( f )
67 93
68 self.parseConfigurationFile(self.configuration.file) 94 self.parseConfigurationFile( os.path.join( "conf", "bitbake.conf" ) )
69 95
70 if not self.configuration.cmd: 96 if not self.configuration.cmd:
71 self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data) or "build" 97 self.configuration.cmd = bb.data.getVar("BB_DEFAULT_TASK", self.configuration.data, True) or "build"
72 98
73 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, True) 99 bbpkgs = bb.data.getVar('BBPKGS', self.configuration.data, True)
74 if bbpkgs and len(self.configuration.pkgs_to_build) == 0: 100 if bbpkgs and len(self.configuration.pkgs_to_build) == 0:
@@ -80,9 +106,7 @@ class BBCooker:
80 self.configuration.event_data = bb.data.createCopy(self.configuration.data) 106 self.configuration.event_data = bb.data.createCopy(self.configuration.data)
81 bb.data.update_data(self.configuration.event_data) 107 bb.data.update_data(self.configuration.event_data)
82 108
83 #
84 # TOSTOP must not be set or our children will hang when they output 109 # TOSTOP must not be set or our children will hang when they output
85 #
86 fd = sys.stdout.fileno() 110 fd = sys.stdout.fileno()
87 if os.isatty(fd): 111 if os.isatty(fd):
88 import termios 112 import termios
@@ -92,40 +116,91 @@ class BBCooker:
92 tcattr[3] = tcattr[3] & ~termios.TOSTOP 116 tcattr[3] = tcattr[3] & ~termios.TOSTOP
93 termios.tcsetattr(fd, termios.TCSANOW, tcattr) 117 termios.tcsetattr(fd, termios.TCSANOW, tcattr)
94 118
119 self.command = bb.command.Command(self)
120 self.cookerState = cookerClean
121 self.cookerAction = cookerRun
122
123 def parseConfiguration(self):
124
125
95 # Change nice level if we're asked to 126 # Change nice level if we're asked to
96 nice = bb.data.getVar("BB_NICE_LEVEL", self.configuration.data, True) 127 nice = bb.data.getVar("BB_NICE_LEVEL", self.configuration.data, True)
97 if nice: 128 if nice:
98 curnice = os.nice(0) 129 curnice = os.nice(0)
99 nice = int(nice) - curnice 130 nice = int(nice) - curnice
100 bb.msg.note(2, bb.msg.domain.Build, "Renice to %s " % os.nice(nice)) 131 bb.msg.note(2, bb.msg.domain.Build, "Renice to %s " % os.nice(nice))
101 132
133 def parseCommandLine(self):
134 # Parse any commandline into actions
135 if self.configuration.show_environment:
136 self.commandlineAction = None
137
138 if 'world' in self.configuration.pkgs_to_build:
139 bb.error("'world' is not a valid target for --environment.")
140 elif len(self.configuration.pkgs_to_build) > 1:
141 bb.error("Only one target can be used with the --environment option.")
142 elif self.configuration.buildfile and len(self.configuration.pkgs_to_build) > 0:
143 bb.error("No target should be used with the --environment and --buildfile options.")
144 elif len(self.configuration.pkgs_to_build) > 0:
145 self.commandlineAction = ["showEnvironmentTarget", self.configuration.pkgs_to_build]
146 else:
147 self.commandlineAction = ["showEnvironment", self.configuration.buildfile]
148 elif self.configuration.buildfile is not None:
149 self.commandlineAction = ["buildFile", self.configuration.buildfile, self.configuration.cmd]
150 elif self.configuration.revisions_changed:
151 self.commandlineAction = ["compareRevisions"]
152 elif self.configuration.show_versions:
153 self.commandlineAction = ["showVersions"]
154 elif self.configuration.parse_only:
155 self.commandlineAction = ["parseFiles"]
156 # FIXME - implement
157 #elif self.configuration.interactive:
158 # self.interactiveMode()
159 elif self.configuration.dot_graph:
160 if self.configuration.pkgs_to_build:
161 self.commandlineAction = ["generateDotGraph", self.configuration.pkgs_to_build, self.configuration.cmd]
162 else:
163 self.commandlineAction = None
164 bb.error("Please specify a package name for dependency graph generation.")
165 else:
166 if self.configuration.pkgs_to_build:
167 self.commandlineAction = ["buildTargets", self.configuration.pkgs_to_build, self.configuration.cmd]
168 else:
169 self.commandlineAction = None
170 bb.error("Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help' for usage information.")
171
172 def runCommands(self, server, data, abort):
173 """
174 Run any queued asynchronous command
175 This is done by the idle handler so it runs in true context rather than
176 tied to any UI.
177 """
178
179 return self.command.runAsyncCommand()
102 180
103 def tryBuildPackage(self, fn, item, task, the_data): 181 def tryBuildPackage(self, fn, item, task, the_data):
104 """ 182 """
105 Build one task of a package, optionally build following task depends 183 Build one task of a package, optionally build following task depends
106 """ 184 """
107 bb.event.fire(bb.event.PkgStarted(item, the_data))
108 try: 185 try:
109 if not self.configuration.dry_run: 186 if not self.configuration.dry_run:
110 bb.build.exec_task('do_%s' % task, the_data) 187 bb.build.exec_task('do_%s' % task, the_data)
111 bb.event.fire(bb.event.PkgSucceeded(item, the_data))
112 return True 188 return True
113 except bb.build.FuncFailed: 189 except bb.build.FuncFailed:
114 bb.msg.error(bb.msg.domain.Build, "task stack execution failed") 190 bb.msg.error(bb.msg.domain.Build, "task stack execution failed")
115 bb.event.fire(bb.event.PkgFailed(item, the_data))
116 raise 191 raise
117 except bb.build.EventException, e: 192 except bb.build.EventException, e:
118 event = e.args[1] 193 event = e.args[1]
119 bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event)) 194 bb.msg.error(bb.msg.domain.Build, "%s event exception, aborting" % bb.event.getName(event))
120 bb.event.fire(bb.event.PkgFailed(item, the_data))
121 raise 195 raise
122 196
123 def tryBuild(self, fn): 197 def tryBuild(self, fn, task):
124 """ 198 """
125 Build a provider and its dependencies. 199 Build a provider and its dependencies.
126 build_depends is a list of previous build dependencies (not runtime) 200 build_depends is a list of previous build dependencies (not runtime)
127 If build_depends is empty, we're dealing with a runtime depends 201 If build_depends is empty, we're dealing with a runtime depends
128 """ 202 """
203
129 the_data = self.bb_cache.loadDataFull(fn, self.configuration.data) 204 the_data = self.bb_cache.loadDataFull(fn, self.configuration.data)
130 205
131 item = self.status.pkg_fn[fn] 206 item = self.status.pkg_fn[fn]
@@ -133,9 +208,13 @@ class BBCooker:
133 #if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data): 208 #if bb.build.stamp_is_current('do_%s' % self.configuration.cmd, the_data):
134 # return True 209 # return True
135 210
136 return self.tryBuildPackage(fn, item, self.configuration.cmd, the_data) 211 return self.tryBuildPackage(fn, item, task, the_data)
137 212
138 def showVersions(self): 213 def showVersions(self):
214
215 # Need files parsed
216 self.updateCache()
217
139 pkg_pn = self.status.pkg_pn 218 pkg_pn = self.status.pkg_pn
140 preferred_versions = {} 219 preferred_versions = {}
141 latest_versions = {} 220 latest_versions = {}
@@ -149,43 +228,36 @@ class BBCooker:
149 pkg_list = pkg_pn.keys() 228 pkg_list = pkg_pn.keys()
150 pkg_list.sort() 229 pkg_list.sort()
151 230
231 bb.msg.plain("%-35s %25s %25s" % ("Package Name", "Latest Version", "Preferred Version"))
232 bb.msg.plain("%-35s %25s %25s\n" % ("============", "==============", "================="))
233
152 for p in pkg_list: 234 for p in pkg_list:
153 pref = preferred_versions[p] 235 pref = preferred_versions[p]
154 latest = latest_versions[p] 236 latest = latest_versions[p]
155 237
156 if pref != latest: 238 prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2]
157 prefstr = pref[0][0] + ":" + pref[0][1] + '-' + pref[0][2] 239 lateststr = latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2]
158 else: 240
241 if pref == latest:
159 prefstr = "" 242 prefstr = ""
160 243
161 print "%-30s %20s %20s" % (p, latest[0][0] + ":" + latest[0][1] + "-" + latest[0][2], 244 bb.msg.plain("%-35s %25s %25s" % (p, lateststr, prefstr))
162 prefstr)
163 245
246 def compareRevisions(self):
247 ret = bb.fetch.fetcher_compare_revisons(self.configuration.data)
248 bb.event.fire(bb.command.CookerCommandSetExitCode(ret), self.configuration.event_data)
164 249
165 def showEnvironment(self , buildfile = None, pkgs_to_build = []): 250 def showEnvironment(self, buildfile = None, pkgs_to_build = []):
166 """ 251 """
167 Show the outer or per-package environment 252 Show the outer or per-package environment
168 """ 253 """
169 fn = None 254 fn = None
170 envdata = None 255 envdata = None
171 256
172 if 'world' in pkgs_to_build:
173 print "'world' is not a valid target for --environment."
174 sys.exit(1)
175
176 if len(pkgs_to_build) > 1:
177 print "Only one target can be used with the --environment option."
178 sys.exit(1)
179
180 if buildfile: 257 if buildfile:
181 if len(pkgs_to_build) > 0:
182 print "No target should be used with the --environment and --buildfile options."
183 sys.exit(1)
184 self.cb = None 258 self.cb = None
185 self.bb_cache = bb.cache.init(self) 259 self.bb_cache = bb.cache.init(self)
186 fn = self.matchFile(buildfile) 260 fn = self.matchFile(buildfile)
187 if not fn:
188 sys.exit(1)
189 elif len(pkgs_to_build) == 1: 261 elif len(pkgs_to_build) == 1:
190 self.updateCache() 262 self.updateCache()
191 263
@@ -193,13 +265,9 @@ class BBCooker:
193 bb.data.update_data(localdata) 265 bb.data.update_data(localdata)
194 bb.data.expandKeys(localdata) 266 bb.data.expandKeys(localdata)
195 267
196 taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) 268 taskdata = bb.taskdata.TaskData(self.configuration.abort)
197 269 taskdata.add_provider(localdata, self.status, pkgs_to_build[0])
198 try: 270 taskdata.add_unresolved(localdata, self.status)
199 taskdata.add_provider(localdata, self.status, pkgs_to_build[0])
200 taskdata.add_unresolved(localdata, self.status)
201 except bb.providers.NoProvider:
202 sys.exit(1)
203 271
204 targetid = taskdata.getbuild_id(pkgs_to_build[0]) 272 targetid = taskdata.getbuild_id(pkgs_to_build[0])
205 fnid = taskdata.build_targets[targetid][0] 273 fnid = taskdata.build_targets[targetid][0]
@@ -211,55 +279,69 @@ class BBCooker:
211 try: 279 try:
212 envdata = self.bb_cache.loadDataFull(fn, self.configuration.data) 280 envdata = self.bb_cache.loadDataFull(fn, self.configuration.data)
213 except IOError, e: 281 except IOError, e:
214 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to read %s: %s" % (fn, e)) 282 bb.msg.error(bb.msg.domain.Parsing, "Unable to read %s: %s" % (fn, e))
283 raise
215 except Exception, e: 284 except Exception, e:
216 bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e) 285 bb.msg.error(bb.msg.domain.Parsing, "%s" % e)
286 raise
287
288 class dummywrite:
289 def __init__(self):
290 self.writebuf = ""
291 def write(self, output):
292 self.writebuf = self.writebuf + output
217 293
218 # emit variables and shell functions 294 # emit variables and shell functions
219 try: 295 try:
220 data.update_data( envdata ) 296 data.update_data(envdata)
221 data.emit_env(sys.__stdout__, envdata, True) 297 wb = dummywrite()
298 data.emit_env(wb, envdata, True)
299 bb.msg.plain(wb.writebuf)
222 except Exception, e: 300 except Exception, e:
223 bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e) 301 bb.msg.fatal(bb.msg.domain.Parsing, "%s" % e)
224 # emit the metadata which isnt valid shell 302 # emit the metadata which isnt valid shell
225 data.expandKeys( envdata ) 303 data.expandKeys(envdata)
226 for e in envdata.keys(): 304 for e in envdata.keys():
227 if data.getVarFlag( e, 'python', envdata ): 305 if data.getVarFlag( e, 'python', envdata ):
228 sys.__stdout__.write("\npython %s () {\n%s}\n" % (e, data.getVar(e, envdata, 1))) 306 bb.msg.plain("\npython %s () {\n%s}\n" % (e, data.getVar(e, envdata, 1)))
229 307
230 def generateDotGraph( self, pkgs_to_build, ignore_deps ): 308 def generateDepTreeData(self, pkgs_to_build, task):
231 """ 309 """
232 Generate a task dependency graph. 310 Create a dependency tree of pkgs_to_build, returning the data.
233
234 pkgs_to_build A list of packages that needs to be built
235 ignore_deps A list of names where processing of dependencies
236 should be stopped. e.g. dependencies that get
237 """ 311 """
238 312
239 for dep in ignore_deps: 313 # Need files parsed
240 self.status.ignored_dependencies.add(dep) 314 self.updateCache()
315
316 # If we are told to do the None task then query the default task
317 if (task == None):
318 task = self.configuration.cmd
319
320 pkgs_to_build = self.checkPackages(pkgs_to_build)
241 321
242 localdata = data.createCopy(self.configuration.data) 322 localdata = data.createCopy(self.configuration.data)
243 bb.data.update_data(localdata) 323 bb.data.update_data(localdata)
244 bb.data.expandKeys(localdata) 324 bb.data.expandKeys(localdata)
245 taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) 325 taskdata = bb.taskdata.TaskData(self.configuration.abort)
246 326
247 runlist = [] 327 runlist = []
248 try: 328 for k in pkgs_to_build:
249 for k in pkgs_to_build: 329 taskdata.add_provider(localdata, self.status, k)
250 taskdata.add_provider(localdata, self.status, k) 330 runlist.append([k, "do_%s" % task])
251 runlist.append([k, "do_%s" % self.configuration.cmd]) 331 taskdata.add_unresolved(localdata, self.status)
252 taskdata.add_unresolved(localdata, self.status) 332
253 except bb.providers.NoProvider:
254 sys.exit(1)
255 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) 333 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist)
256 rq.prepare_runqueue() 334 rq.prepare_runqueue()
257 335
258 seen_fnids = [] 336 seen_fnids = []
259 depends_file = file('depends.dot', 'w' ) 337 depend_tree = {}
260 tdepends_file = file('task-depends.dot', 'w' ) 338 depend_tree["depends"] = {}
261 print >> depends_file, "digraph depends {" 339 depend_tree["tdepends"] = {}
262 print >> tdepends_file, "digraph depends {" 340 depend_tree["pn"] = {}
341 depend_tree["rdepends-pn"] = {}
342 depend_tree["packages"] = {}
343 depend_tree["rdepends-pkg"] = {}
344 depend_tree["rrecs-pkg"] = {}
263 345
264 for task in range(len(rq.runq_fnid)): 346 for task in range(len(rq.runq_fnid)):
265 taskname = rq.runq_task[task] 347 taskname = rq.runq_task[task]
@@ -267,43 +349,118 @@ class BBCooker:
267 fn = taskdata.fn_index[fnid] 349 fn = taskdata.fn_index[fnid]
268 pn = self.status.pkg_fn[fn] 350 pn = self.status.pkg_fn[fn]
269 version = "%s:%s-%s" % self.status.pkg_pepvpr[fn] 351 version = "%s:%s-%s" % self.status.pkg_pepvpr[fn]
270 print >> tdepends_file, '"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn) 352 if pn not in depend_tree["pn"]:
353 depend_tree["pn"][pn] = {}
354 depend_tree["pn"][pn]["filename"] = fn
355 depend_tree["pn"][pn]["version"] = version
271 for dep in rq.runq_depends[task]: 356 for dep in rq.runq_depends[task]:
272 depfn = taskdata.fn_index[rq.runq_fnid[dep]] 357 depfn = taskdata.fn_index[rq.runq_fnid[dep]]
273 deppn = self.status.pkg_fn[depfn] 358 deppn = self.status.pkg_fn[depfn]
274 print >> tdepends_file, '"%s.%s" -> "%s.%s"' % (pn, rq.runq_task[task], deppn, rq.runq_task[dep]) 359 dotname = "%s.%s" % (pn, rq.runq_task[task])
360 if not dotname in depend_tree["tdepends"]:
361 depend_tree["tdepends"][dotname] = []
362 depend_tree["tdepends"][dotname].append("%s.%s" % (deppn, rq.runq_task[dep]))
275 if fnid not in seen_fnids: 363 if fnid not in seen_fnids:
276 seen_fnids.append(fnid) 364 seen_fnids.append(fnid)
277 packages = [] 365 packages = []
278 print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn) 366
279 for depend in self.status.deps[fn]: 367 depend_tree["depends"][pn] = []
280 print >> depends_file, '"%s" -> "%s"' % (pn, depend) 368 for dep in taskdata.depids[fnid]:
369 depend_tree["depends"][pn].append(taskdata.build_names_index[dep])
370
371 depend_tree["rdepends-pn"][pn] = []
372 for rdep in taskdata.rdepids[fnid]:
373 depend_tree["rdepends-pn"][pn].append(taskdata.run_names_index[rdep])
374
281 rdepends = self.status.rundeps[fn] 375 rdepends = self.status.rundeps[fn]
282 for package in rdepends: 376 for package in rdepends:
283 for rdepend in re.findall("([\w.-]+)(\ \(.+\))?", rdepends[package]): 377 depend_tree["rdepends-pkg"][package] = []
284 print >> depends_file, '"%s" -> "%s%s" [style=dashed]' % (package, rdepend[0], rdepend[1]) 378 for rdepend in rdepends[package]:
379 depend_tree["rdepends-pkg"][package].append(rdepend)
285 packages.append(package) 380 packages.append(package)
381
286 rrecs = self.status.runrecs[fn] 382 rrecs = self.status.runrecs[fn]
287 for package in rrecs: 383 for package in rrecs:
288 for rdepend in re.findall("([\w.-]+)(\ \(.+\))?", rrecs[package]): 384 depend_tree["rrecs-pkg"][package] = []
289 print >> depends_file, '"%s" -> "%s%s" [style=dashed]' % (package, rdepend[0], rdepend[1]) 385 for rdepend in rrecs[package]:
386 depend_tree["rrecs-pkg"][package].append(rdepend)
290 if not package in packages: 387 if not package in packages:
291 packages.append(package) 388 packages.append(package)
389
292 for package in packages: 390 for package in packages:
293 if package != pn: 391 if package not in depend_tree["packages"]:
294 print >> depends_file, '"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn) 392 depend_tree["packages"][package] = {}
295 for depend in self.status.deps[fn]: 393 depend_tree["packages"][package]["pn"] = pn
296 print >> depends_file, '"%s" -> "%s"' % (package, depend) 394 depend_tree["packages"][package]["filename"] = fn
297 # Prints a flattened form of the above where subpackages of a package are merged into the main pn 395 depend_tree["packages"][package]["version"] = version
298 #print >> depends_file, '"%s" [label="%s %s\\n%s\\n%s"]' % (pn, pn, taskname, version, fn) 396
299 #for rdep in taskdata.rdepids[fnid]: 397 return depend_tree
300 # print >> depends_file, '"%s" -> "%s" [style=dashed]' % (pn, taskdata.run_names_index[rdep]) 398
301 #for dep in taskdata.depids[fnid]: 399
302 # print >> depends_file, '"%s" -> "%s"' % (pn, taskdata.build_names_index[dep]) 400 def generateDepTreeEvent(self, pkgs_to_build, task):
401 """
402 Create a task dependency graph of pkgs_to_build.
403 Generate an event with the result
404 """
405 depgraph = self.generateDepTreeData(pkgs_to_build, task)
406 bb.event.fire(bb.event.DepTreeGenerated(depgraph), self.configuration.data)
407
408 def generateDotGraphFiles(self, pkgs_to_build, task):
409 """
410 Create a task dependency graph of pkgs_to_build.
411 Save the result to a set of .dot files.
412 """
413
414 depgraph = self.generateDepTreeData(pkgs_to_build, task)
415
416 # Prints a flattened form of package-depends below where subpackages of a package are merged into the main pn
417 depends_file = file('pn-depends.dot', 'w' )
418 print >> depends_file, "digraph depends {"
419 for pn in depgraph["pn"]:
420 fn = depgraph["pn"][pn]["filename"]
421 version = depgraph["pn"][pn]["version"]
422 print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn)
423 for pn in depgraph["depends"]:
424 for depend in depgraph["depends"][pn]:
425 print >> depends_file, '"%s" -> "%s"' % (pn, depend)
426 for pn in depgraph["rdepends-pn"]:
427 for rdepend in depgraph["rdepends-pn"][pn]:
428 print >> depends_file, '"%s" -> "%s" [style=dashed]' % (pn, rdepend)
429 print >> depends_file, "}"
430 bb.msg.plain("PN dependencies saved to 'pn-depends.dot'")
431
432 depends_file = file('package-depends.dot', 'w' )
433 print >> depends_file, "digraph depends {"
434 for package in depgraph["packages"]:
435 pn = depgraph["packages"][package]["pn"]
436 fn = depgraph["packages"][package]["filename"]
437 version = depgraph["packages"][package]["version"]
438 if package == pn:
439 print >> depends_file, '"%s" [label="%s %s\\n%s"]' % (pn, pn, version, fn)
440 else:
441 print >> depends_file, '"%s" [label="%s(%s) %s\\n%s"]' % (package, package, pn, version, fn)
442 for depend in depgraph["depends"][pn]:
443 print >> depends_file, '"%s" -> "%s"' % (package, depend)
444 for package in depgraph["rdepends-pkg"]:
445 for rdepend in depgraph["rdepends-pkg"][package]:
446 print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend)
447 for package in depgraph["rrecs-pkg"]:
448 for rdepend in depgraph["rrecs-pkg"][package]:
449 print >> depends_file, '"%s" -> "%s" [style=dashed]' % (package, rdepend)
303 print >> depends_file, "}" 450 print >> depends_file, "}"
451 bb.msg.plain("Package dependencies saved to 'package-depends.dot'")
452
453 tdepends_file = file('task-depends.dot', 'w' )
454 print >> tdepends_file, "digraph depends {"
455 for task in depgraph["tdepends"]:
456 (pn, taskname) = task.rsplit(".", 1)
457 fn = depgraph["pn"][pn]["filename"]
458 version = depgraph["pn"][pn]["version"]
459 print >> tdepends_file, '"%s.%s" [label="%s %s\\n%s\\n%s"]' % (pn, taskname, pn, taskname, version, fn)
460 for dep in depgraph["tdepends"][task]:
461 print >> tdepends_file, '"%s" -> "%s"' % (task, dep)
304 print >> tdepends_file, "}" 462 print >> tdepends_file, "}"
305 bb.msg.note(1, bb.msg.domain.Collection, "Dependencies saved to 'depends.dot'") 463 bb.msg.plain("Task dependencies saved to 'task-depends.dot'")
306 bb.msg.note(1, bb.msg.domain.Collection, "Task dependencies saved to 'task-depends.dot'")
307 464
308 def buildDepgraph( self ): 465 def buildDepgraph( self ):
309 all_depends = self.status.all_depends 466 all_depends = self.status.all_depends
@@ -324,7 +481,7 @@ class BBCooker:
324 try: 481 try:
325 (providee, provider) = p.split(':') 482 (providee, provider) = p.split(':')
326 except: 483 except:
327 bb.msg.error(bb.msg.domain.Provider, "Malformed option in PREFERRED_PROVIDERS variable: %s" % p) 484 bb.msg.fatal(bb.msg.domain.Provider, "Malformed option in PREFERRED_PROVIDERS variable: %s" % p)
328 continue 485 continue
329 if providee in self.status.preferred and self.status.preferred[providee] != provider: 486 if providee in self.status.preferred and self.status.preferred[providee] != provider:
330 bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee])) 487 bb.msg.error(bb.msg.domain.Provider, "conflicting preferences for %s: both %s and %s specified" % (providee, provider, self.status.preferred[providee]))
@@ -362,19 +519,6 @@ class BBCooker:
362 self.status.possible_world = None 519 self.status.possible_world = None
363 self.status.all_depends = None 520 self.status.all_depends = None
364 521
365 def myProgressCallback( self, x, y, f, from_cache ):
366 """Update any tty with the progress change"""
367 if os.isatty(sys.stdout.fileno()):
368 sys.stdout.write("\rNOTE: Handling BitBake files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
369 sys.stdout.flush()
370 else:
371 if x == 1:
372 sys.stdout.write("Parsing .bb files, please wait...")
373 sys.stdout.flush()
374 if x == y:
375 sys.stdout.write("done.")
376 sys.stdout.flush()
377
378 def interactiveMode( self ): 522 def interactiveMode( self ):
379 """Drop off into a shell""" 523 """Drop off into a shell"""
380 try: 524 try:
@@ -383,12 +527,10 @@ class BBCooker:
383 bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details ) 527 bb.msg.fatal(bb.msg.domain.Parsing, "Sorry, shell not available (%s)" % details )
384 else: 528 else:
385 shell.start( self ) 529 shell.start( self )
386 sys.exit( 0 )
387 530
388 def parseConfigurationFile( self, afiles ): 531 def parseConfigurationFile( self, afile ):
389 try: 532 try:
390 for afile in afiles: 533 self.configuration.data = bb.parse.handle( afile, self.configuration.data )
391 self.configuration.data = bb.parse.handle( afile, self.configuration.data )
392 534
393 # Handle any INHERITs and inherit the base class 535 # Handle any INHERITs and inherit the base class
394 inherits = ["base"] + (bb.data.getVar('INHERIT', self.configuration.data, True ) or "").split() 536 inherits = ["base"] + (bb.data.getVar('INHERIT', self.configuration.data, True ) or "").split()
@@ -402,10 +544,10 @@ class BBCooker:
402 544
403 bb.fetch.fetcher_init(self.configuration.data) 545 bb.fetch.fetcher_init(self.configuration.data)
404 546
405 bb.event.fire(bb.event.ConfigParsed(self.configuration.data)) 547 bb.event.fire(bb.event.ConfigParsed(), self.configuration.data)
406 548
407 except IOError, e: 549 except IOError, e:
408 bb.msg.fatal(bb.msg.domain.Parsing, "IO Error: %s" % str(e) ) 550 bb.msg.fatal(bb.msg.domain.Parsing, "Error when parsing %s: %s" % (afile, str(e)))
409 except bb.parse.ParseError, details: 551 except bb.parse.ParseError, details:
410 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) ) 552 bb.msg.fatal(bb.msg.domain.Parsing, "Unable to parse %s (%s)" % (afile, details) )
411 553
@@ -439,17 +581,17 @@ class BBCooker:
439 """ 581 """
440 if not bb.data.getVar("BUILDNAME", self.configuration.data): 582 if not bb.data.getVar("BUILDNAME", self.configuration.data):
441 bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data) 583 bb.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), self.configuration.data)
442 bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()),self.configuration.data) 584 bb.data.setVar("BUILDSTART", time.strftime('%m/%d/%Y %H:%M:%S',time.gmtime()), self.configuration.data)
443 585
444 def matchFile(self, buildfile): 586 def matchFiles(self, buildfile):
445 """ 587 """
446 Convert the fragment buildfile into a real file 588 Find the .bb files which match the expression in 'buildfile'.
447 Error if there are too many matches
448 """ 589 """
590
449 bf = os.path.abspath(buildfile) 591 bf = os.path.abspath(buildfile)
450 try: 592 try:
451 os.stat(bf) 593 os.stat(bf)
452 return bf 594 return [bf]
453 except OSError: 595 except OSError:
454 (filelist, masked) = self.collect_bbfiles() 596 (filelist, masked) = self.collect_bbfiles()
455 regexp = re.compile(buildfile) 597 regexp = re.compile(buildfile)
@@ -458,27 +600,41 @@ class BBCooker:
458 if regexp.search(f) and os.path.isfile(f): 600 if regexp.search(f) and os.path.isfile(f):
459 bf = f 601 bf = f
460 matches.append(f) 602 matches.append(f)
461 if len(matches) != 1: 603 return matches
462 bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (buildfile, len(matches)))
463 for f in matches:
464 bb.msg.error(bb.msg.domain.Parsing, " %s" % f)
465 return False
466 return matches[0]
467 604
468 def buildFile(self, buildfile): 605 def matchFile(self, buildfile):
606 """
607 Find the .bb file which matches the expression in 'buildfile'.
608 Raise an error if multiple files
609 """
610 matches = self.matchFiles(buildfile)
611 if len(matches) != 1:
612 bb.msg.error(bb.msg.domain.Parsing, "Unable to match %s (%s matches found):" % (buildfile, len(matches)))
613 for f in matches:
614 bb.msg.error(bb.msg.domain.Parsing, " %s" % f)
615 raise MultipleMatches
616 return matches[0]
617
618 def buildFile(self, buildfile, task):
469 """ 619 """
470 Build the file matching regexp buildfile 620 Build the file matching regexp buildfile
471 """ 621 """
472 622
473 # Make sure our target is a fully qualified filename 623 # Parse the configuration here. We need to do it explicitly here since
624 # buildFile() doesn't use the cache
625 self.parseConfiguration()
626
627 # If we are told to do the None task then query the default task
628 if (task == None):
629 task = self.configuration.cmd
630
474 fn = self.matchFile(buildfile) 631 fn = self.matchFile(buildfile)
475 if not fn: 632 self.buildSetVars()
476 return False
477 633
478 # Load data into the cache for fn and parse the loaded cache data 634 # Load data into the cache for fn and parse the loaded cache data
479 self.bb_cache = bb.cache.init(self) 635 self.bb_cache = bb.cache.init(self)
480 self.status = bb.cache.CacheData() 636 self.status = bb.cache.CacheData()
481 self.bb_cache.loadData(fn, self.configuration.data, self.status) 637 self.bb_cache.loadData(fn, self.configuration.data, self.status)
482 638
483 # Tweak some variables 639 # Tweak some variables
484 item = self.bb_cache.getVar('PN', fn, True) 640 item = self.bb_cache.getVar('PN', fn, True)
@@ -493,159 +649,157 @@ class BBCooker:
493 649
494 # Remove stamp for target if force mode active 650 # Remove stamp for target if force mode active
495 if self.configuration.force: 651 if self.configuration.force:
496 bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (self.configuration.cmd, fn)) 652 bb.msg.note(2, bb.msg.domain.RunQueue, "Remove stamp %s, %s" % (task, fn))
497 bb.build.del_stamp('do_%s' % self.configuration.cmd, self.configuration.data) 653 bb.build.del_stamp('do_%s' % task, self.status, fn)
498 654
499 # Setup taskdata structure 655 # Setup taskdata structure
500 taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) 656 taskdata = bb.taskdata.TaskData(self.configuration.abort)
501 taskdata.add_provider(self.configuration.data, self.status, item) 657 taskdata.add_provider(self.configuration.data, self.status, item)
502 658
503 buildname = bb.data.getVar("BUILDNAME", self.configuration.data) 659 buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
504 bb.event.fire(bb.event.BuildStarted(buildname, [item], self.configuration.event_data)) 660 bb.event.fire(bb.event.BuildStarted(buildname, [item]), self.configuration.event_data)
505 661
506 # Execute the runqueue 662 # Execute the runqueue
507 runlist = [[item, "do_%s" % self.configuration.cmd]] 663 runlist = [[item, "do_%s" % task]]
664
508 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) 665 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist)
509 rq.prepare_runqueue() 666
510 try: 667 def buildFileIdle(server, rq, abort):
511 failures = rq.execute_runqueue() 668
512 except runqueue.TaskFailure, fnids: 669 if abort or self.cookerAction == cookerStop:
670 rq.finish_runqueue(True)
671 elif self.cookerAction == cookerShutdown:
672 rq.finish_runqueue(False)
513 failures = 0 673 failures = 0
514 for fnid in fnids: 674 try:
515 bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) 675 retval = rq.execute_runqueue()
516 failures = failures + 1 676 except runqueue.TaskFailure, fnids:
517 bb.event.fire(bb.event.BuildCompleted(buildname, [item], self.configuration.event_data, failures)) 677 for fnid in fnids:
518 return False 678 bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid])
519 bb.event.fire(bb.event.BuildCompleted(buildname, [item], self.configuration.event_data, failures)) 679 failures = failures + 1
520 return True 680 retval = False
681 if not retval:
682 self.command.finishAsyncCommand()
683 bb.event.fire(bb.event.BuildCompleted(buildname, item, failures), self.configuration.event_data)
684 return False
685 return 0.5
686
687 self.server.register_idle_function(buildFileIdle, rq)
521 688
522 def buildTargets(self, targets): 689 def buildTargets(self, targets, task):
523 """ 690 """
524 Attempt to build the targets specified 691 Attempt to build the targets specified
525 """ 692 """
526 693
527 buildname = bb.data.getVar("BUILDNAME", self.configuration.data) 694 # Need files parsed
528 bb.event.fire(bb.event.BuildStarted(buildname, targets, self.configuration.event_data)) 695 self.updateCache()
529 696
530 localdata = data.createCopy(self.configuration.data) 697 # If we are told to do the NULL task then query the default task
531 bb.data.update_data(localdata) 698 if (task == None):
532 bb.data.expandKeys(localdata) 699 task = self.configuration.cmd
533 700
534 taskdata = bb.taskdata.TaskData(self.configuration.abort, self.configuration.tryaltconfigs) 701 targets = self.checkPackages(targets)
535 702
536 runlist = [] 703 def buildTargetsIdle(server, rq, abort):
537 try:
538 for k in targets:
539 taskdata.add_provider(localdata, self.status, k)
540 runlist.append([k, "do_%s" % self.configuration.cmd])
541 taskdata.add_unresolved(localdata, self.status)
542 except bb.providers.NoProvider:
543 sys.exit(1)
544 704
545 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist) 705 if abort or self.cookerAction == cookerStop:
546 rq.prepare_runqueue() 706 rq.finish_runqueue(True)
547 try: 707 elif self.cookerAction == cookerShutdown:
548 failures = rq.execute_runqueue() 708 rq.finish_runqueue(False)
549 except runqueue.TaskFailure, fnids:
550 failures = 0 709 failures = 0
551 for fnid in fnids: 710 try:
552 bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid]) 711 retval = rq.execute_runqueue()
553 failures = failures + 1 712 except runqueue.TaskFailure, fnids:
554 bb.event.fire(bb.event.BuildCompleted(buildname, targets, self.configuration.event_data, failures)) 713 for fnid in fnids:
555 sys.exit(1) 714 bb.msg.error(bb.msg.domain.Build, "'%s' failed" % taskdata.fn_index[fnid])
556 bb.event.fire(bb.event.BuildCompleted(buildname, targets, self.configuration.event_data, failures)) 715 failures = failures + 1
716 retval = False
717 if not retval:
718 self.command.finishAsyncCommand()
719 bb.event.fire(bb.event.BuildCompleted(buildname, targets, failures), self.configuration.event_data)
720 return None
721 return 0.5
557 722
558 sys.exit(0) 723 self.buildSetVars()
559 724
560 def updateCache(self): 725 buildname = bb.data.getVar("BUILDNAME", self.configuration.data)
561 # Import Psyco if available and not disabled 726 bb.event.fire(bb.event.BuildStarted(buildname, targets), self.configuration.event_data)
562 import platform
563 if platform.machine() in ['i386', 'i486', 'i586', 'i686']:
564 if not self.configuration.disable_psyco:
565 try:
566 import psyco
567 except ImportError:
568 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
569 else:
570 psyco.bind( self.parse_bbfiles )
571 else:
572 bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
573 727
574 self.status = bb.cache.CacheData() 728 localdata = data.createCopy(self.configuration.data)
729 bb.data.update_data(localdata)
730 bb.data.expandKeys(localdata)
575 731
576 ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or "" 732 taskdata = bb.taskdata.TaskData(self.configuration.abort)
577 self.status.ignored_dependencies = set( ignore.split() )
578 733
579 self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) ) 734 runlist = []
735 for k in targets:
736 taskdata.add_provider(localdata, self.status, k)
737 runlist.append([k, "do_%s" % task])
738 taskdata.add_unresolved(localdata, self.status)
580 739
581 bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files") 740 rq = bb.runqueue.RunQueue(self, self.configuration.data, self.status, taskdata, runlist)
582 (filelist, masked) = self.collect_bbfiles()
583 bb.data.renameVar("__depends", "__base_depends", self.configuration.data)
584 self.parse_bbfiles(filelist, masked, self.myProgressCallback)
585 bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
586 741
587 self.buildDepgraph() 742 self.server.register_idle_function(buildTargetsIdle, rq)
588 743
589 def cook(self): 744 def updateCache(self):
590 """
591 We are building stuff here. We do the building
592 from here. By default we try to execute task
593 build.
594 """
595 745
596 # Wipe the OS environment 746 if self.cookerState == cookerParsed:
597 bb.utils.empty_environment() 747 return
598 748
599 if self.configuration.show_environment: 749 if self.cookerState != cookerParsing:
600 self.showEnvironment(self.configuration.buildfile, self.configuration.pkgs_to_build)
601 sys.exit( 0 )
602 750
603 self.buildSetVars() 751 self.parseConfiguration ()
604 752
605 if self.configuration.interactive: 753 # Import Psyco if available and not disabled
606 self.interactiveMode() 754 import platform
755 if platform.machine() in ['i386', 'i486', 'i586', 'i686']:
756 if not self.configuration.disable_psyco:
757 try:
758 import psyco
759 except ImportError:
760 bb.msg.note(1, bb.msg.domain.Collection, "Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
761 else:
762 psyco.bind( CookerParser.parse_next )
763 else:
764 bb.msg.note(1, bb.msg.domain.Collection, "You have disabled Psyco. This decreases performance.")
607 765
608 if self.configuration.buildfile is not None: 766 self.status = bb.cache.CacheData()
609 if not self.buildFile(self.configuration.buildfile):
610 sys.exit(1)
611 sys.exit(0)
612 767
613 # initialise the parsing status now we know we will need deps 768 ignore = bb.data.getVar("ASSUME_PROVIDED", self.configuration.data, 1) or ""
614 self.updateCache() 769 self.status.ignored_dependencies = set(ignore.split())
770
771 for dep in self.configuration.extra_assume_provided:
772 self.status.ignored_dependencies.add(dep)
773
774 self.handleCollections( bb.data.getVar("BBFILE_COLLECTIONS", self.configuration.data, 1) )
615 775
616 if self.configuration.revisions_changed: 776 bb.msg.debug(1, bb.msg.domain.Collection, "collecting .bb files")
617 sys.exit(bb.fetch.fetcher_compare_revisons(self.configuration.data)) 777 (filelist, masked) = self.collect_bbfiles()
778 bb.data.renameVar("__depends", "__base_depends", self.configuration.data)
618 779
619 if self.configuration.parse_only: 780 self.parser = CookerParser(self, filelist, masked)
620 bb.msg.note(1, bb.msg.domain.Collection, "Requested parsing .bb files only. Exiting.") 781 self.cookerState = cookerParsing
621 return 0
622 782
623 pkgs_to_build = self.configuration.pkgs_to_build 783 if not self.parser.parse_next():
784 bb.msg.debug(1, bb.msg.domain.Collection, "parsing complete")
785 self.buildDepgraph()
786 self.cookerState = cookerParsed
787 return None
624 788
625 if len(pkgs_to_build) == 0 and not self.configuration.show_versions: 789 return True
626 print "Nothing to do. Use 'bitbake world' to build everything, or run 'bitbake --help'"
627 print "for usage information."
628 sys.exit(0)
629 790
630 try: 791 def checkPackages(self, pkgs_to_build):
631 if self.configuration.show_versions:
632 self.showVersions()
633 sys.exit( 0 )
634 if 'world' in pkgs_to_build:
635 self.buildWorldTargetList()
636 pkgs_to_build.remove('world')
637 for t in self.status.world_target:
638 pkgs_to_build.append(t)
639 792
640 if self.configuration.dot_graph: 793 if len(pkgs_to_build) == 0:
641 self.generateDotGraph( pkgs_to_build, self.configuration.ignored_dot_deps ) 794 raise NothingToBuild
642 sys.exit( 0 )
643 795
644 return self.buildTargets(pkgs_to_build) 796 if 'world' in pkgs_to_build:
797 self.buildWorldTargetList()
798 pkgs_to_build.remove('world')
799 for t in self.status.world_target:
800 pkgs_to_build.append(t)
645 801
646 except KeyboardInterrupt: 802 return pkgs_to_build
647 bb.msg.note(1, bb.msg.domain.Collection, "KeyboardInterrupt - Build not completed.")
648 sys.exit(1)
649 803
650 def get_bbfiles( self, path = os.getcwd() ): 804 def get_bbfiles( self, path = os.getcwd() ):
651 """Get list of default .bb files by reading out the current directory""" 805 """Get list of default .bb files by reading out the current directory"""
@@ -717,59 +871,108 @@ class BBCooker:
717 871
718 return (finalfiles, masked) 872 return (finalfiles, masked)
719 873
720 def parse_bbfiles(self, filelist, masked, progressCallback = None): 874 def serve(self):
721 parsed, cached, skipped, error = 0, 0, 0, 0
722 for i in xrange( len( filelist ) ):
723 f = filelist[i]
724 875
725 #bb.msg.debug(1, bb.msg.domain.Collection, "parsing %s" % f) 876 # Empty the environment. The environment will be populated as
877 # necessary from the data store.
878 bb.utils.empty_environment()
726 879
727 # read a file's metadata 880 if self.configuration.profile:
728 try: 881 try:
729 fromCache, skip = self.bb_cache.loadData(f, self.configuration.data, self.status) 882 import cProfile as profile
730 if skip: 883 except:
731 skipped += 1 884 import profile
732 bb.msg.debug(2, bb.msg.domain.Collection, "skipping %s" % f) 885
733 self.bb_cache.skip(f) 886 profile.runctx("self.server.serve_forever()", globals(), locals(), "profile.log")
734 continue 887
735 elif fromCache: cached += 1 888 # Redirect stdout to capture profile information
736 else: parsed += 1 889 pout = open('profile.log.processed', 'w')
737 890 so = sys.stdout.fileno()
738 # Disabled by RP as was no longer functional 891 os.dup2(pout.fileno(), so)
739 # allow metadata files to add items to BBFILES 892
740 #data.update_data(self.pkgdata[f]) 893 import pstats
741 #addbbfiles = self.bb_cache.getVar('BBFILES', f, False) or None 894 p = pstats.Stats('profile.log')
742 #if addbbfiles: 895 p.sort_stats('time')
743 # for aof in addbbfiles.split(): 896 p.print_stats()
744 # if not files.count(aof): 897 p.print_callers()
745 # if not os.path.isabs(aof): 898 p.sort_stats('cumulative')
746 # aof = os.path.join(os.path.dirname(f),aof) 899 p.print_stats()
747 # files.append(aof) 900
748 901 os.dup2(so, pout.fileno())
749 # now inform the caller 902 pout.flush()
750 if progressCallback is not None: 903 pout.close()
751 progressCallback( i + 1, len( filelist ), f, fromCache ) 904 else:
905 self.server.serve_forever()
906
907 bb.event.fire(CookerExit(), self.configuration.event_data)
908
909class CookerExit(bb.event.Event):
910 """
911 Notify clients of the Cooker shutdown
912 """
913
914 def __init__(self):
915 bb.event.Event.__init__(self)
916
917class CookerParser:
918 def __init__(self, cooker, filelist, masked):
919 # Internal data
920 self.filelist = filelist
921 self.cooker = cooker
922
923 # Accounting statistics
924 self.parsed = 0
925 self.cached = 0
926 self.error = 0
927 self.masked = masked
928 self.total = len(filelist)
929
930 self.skipped = 0
931 self.virtuals = 0
932
933 # Pointer to the next file to parse
934 self.pointer = 0
935
936 def parse_next(self):
937 if self.pointer < len(self.filelist):
938 f = self.filelist[self.pointer]
939 cooker = self.cooker
940
941 try:
942 fromCache, skipped, virtuals = cooker.bb_cache.loadData(f, cooker.configuration.data, cooker.status)
943 if fromCache:
944 self.cached += 1
945 else:
946 self.parsed += 1
947
948 self.skipped += skipped
949 self.virtuals += virtuals
752 950
753 except IOError, e: 951 except IOError, e:
754 self.bb_cache.remove(f) 952 self.error += 1
953 cooker.bb_cache.remove(f)
755 bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e)) 954 bb.msg.error(bb.msg.domain.Collection, "opening %s: %s" % (f, e))
756 pass 955 pass
757 except KeyboardInterrupt: 956 except KeyboardInterrupt:
758 self.bb_cache.sync() 957 cooker.bb_cache.remove(f)
958 cooker.bb_cache.sync()
759 raise 959 raise
760 except Exception, e: 960 except Exception, e:
761 error += 1 961 self.error += 1
762 self.bb_cache.remove(f) 962 cooker.bb_cache.remove(f)
763 bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f)) 963 bb.msg.error(bb.msg.domain.Collection, "%s while parsing %s" % (e, f))
764 except: 964 except:
765 self.bb_cache.remove(f) 965 cooker.bb_cache.remove(f)
766 raise 966 raise
967 finally:
968 bb.event.fire(bb.event.ParseProgress(self.cached, self.parsed, self.skipped, self.masked, self.virtuals, self.error, self.total), cooker.configuration.event_data)
767 969
768 if progressCallback is not None: 970 self.pointer += 1
769 print "\r" # need newline after Handling Bitbake files message
770 bb.msg.note(1, bb.msg.domain.Collection, "Parsing finished. %d cached, %d parsed, %d skipped, %d masked." % ( cached, parsed, skipped, masked ))
771 971
772 self.bb_cache.sync() 972 if self.pointer >= self.total:
973 cooker.bb_cache.sync()
974 if self.error > 0:
975 raise ParsingErrorsFound
976 return False
977 return True
773 978
774 if error > 0:
775 bb.msg.fatal(bb.msg.domain.Collection, "Parsing errors found, exiting...")