cooker.py 28 KB

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