cooker.py 27 KB

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