diff options
Diffstat (limited to 'bitbake/lib/bb/cooker.py')
| -rw-r--r-- | bitbake/lib/bb/cooker.py | 761 |
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 @@ | |||
| 25 | import sys, os, getopt, glob, copy, os.path, re, time | 25 | import sys, os, getopt, glob, copy, os.path, re, time |
| 26 | import bb | 26 | import bb |
| 27 | from bb import utils, data, parse, event, cache, providers, taskdata, runqueue | 27 | from bb import utils, data, parse, event, cache, providers, taskdata, runqueue |
| 28 | from bb import command | ||
| 29 | import bb.server.xmlrpc | ||
| 28 | import itertools, sre_constants | 30 | import itertools, sre_constants |
| 29 | 31 | ||
| 30 | parsespin = itertools.cycle( r'|/-\\' ) | 32 | class MultipleMatches(Exception): |
| 33 | """ | ||
| 34 | Exception raised when multiple file matches are found | ||
| 35 | """ | ||
| 36 | |||
| 37 | class ParsingErrorsFound(Exception): | ||
| 38 | """ | ||
| 39 | Exception raised when parsing errors are found | ||
| 40 | """ | ||
| 41 | |||
| 42 | class NothingToBuild(Exception): | ||
| 43 | """ | ||
| 44 | Exception raised when there is nothing to build | ||
| 45 | """ | ||
| 46 | |||
| 47 | |||
| 48 | # Different states cooker can be in | ||
| 49 | cookerClean = 1 | ||
| 50 | cookerParsing = 2 | ||
| 51 | cookerParsed = 3 | ||
| 52 | |||
| 53 | # Different action states the cooker can be in | ||
| 54 | cookerRun = 1 # Cooker is running normally | ||
| 55 | cookerShutdown = 2 # Active tasks should be brought to a controlled stop | ||
| 56 | cookerStop = 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 | |||
| 909 | class 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 | |||
| 917 | class 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...") | ||
