cooker.py 31 KB

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