2 # ex:ts=4:sw=4:sts=4:et
3 # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
5 import sys, os, getopt, glob, copy, os.path, re
6 sys.path.append('/usr/share/oe')
10 import itertools, optparse
12 parsespin = itertools.cycle( r'|/-\\' )
15 __build_cache_fail = []
21 __world_target = Set()
22 __ignored_dependencies = Set()
23 __depcmds = { "clean": None,
28 oefile_config_priorities = []
32 def handle_options( args ):
33 parser = optparse.OptionParser( version = "OpenEmbedded Build Infrastructure Core version %s, %%prog version %s" % ( oe.__version__, __version__ ),
34 usage = """%prog [options] [package ...]
36 Builds specified packages, expecting that the .oe files
37 it has to work from are in OEFILES
38 Default packages are all packages in OEFILES.
39 Default OEFILES are the .oe files in the current directory.""" )
41 parser.add_option( "-k", "--continue", help = "continue as much as possible after an error. While the target that failed, and those that depend on it, cannot be remade, the other dependencies of these targets can be processed all the same.",
42 action = "store_false", dest = "abort", default = True )
44 parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
45 action = "store_true", dest = "force", default = False )
48 parser.add_option( "-c", "--cmd", help = "specify command to pass to oebuild. Valid commands are "
49 "'fetch' (fetch all sources), "
50 "'unpack' (unpack the sources), "
51 "'patch' (apply the patches), "
52 "'configure' (configure the source tree), "
53 "'compile' (compile the source tree), "
54 "'stage' (install libraries and headers needed for subsequent packages), "
55 "'install' (install libraries and executables), and"
56 "'package' (package files into the selected package format)",
57 action = "store", dest = "cmd", default = "build" )
59 parser.add_option( "-r", "--read", help = "read the specified file before oe.conf",
60 action = "append", dest = "file", default = [] )
62 parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
63 action = "store_true", dest = "verbose", default = False )
65 parser.add_option( "-n", "--dry-run", help = "don't call oebuild, just go through the motions",
66 action = "store_true", dest = "dry_run", default = False )
68 parser.add_option( "-p", "--parse-only", help = "quit after parsing the OE files (developers only)",
69 action = "store_true", dest = "parse_only", default = False )
71 parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
72 action = "store_true", dest = "disable_psyco", default = False )
74 options, args = parser.parse_args( args )
75 return options, args[1:]
77 def try_build(fn, virtual):
78 if fn in __building_list:
79 oe.error("%s depends on itself (eventually)" % fn)
80 oe.error("upwards chain is: %s" % (" -> ".join(__build_path)))
83 __building_list.append(fn)
85 the_data = make.pkgdata[fn]
86 item = oe.data.getVar('PN', the_data, 1)
87 pathstr = "%s (%s)" % (item, virtual)
88 __build_path.append(pathstr)
90 depends_list = (oe.data.getVar('DEPENDS', the_data, 1) or "").split()
91 if make.options.verbose:
92 oe.note("current path: %s" % (" -> ".join(__build_path)))
93 oe.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
99 oldcmd = make.options.cmd
100 make.options.cmd = __depcmd
102 for d in depends_list:
103 if d in __ignored_dependencies:
107 if buildPackage(d) == 0:
108 oe.error("dependency %s (for %s) not satisfied" % (d,item))
110 if make.options.abort:
114 make.options.cmd = oldcmd
120 oe.event.fire(oe.event.PkgStarted(item, make.pkgdata[fn]))
122 __stats["attempt"] += 1
123 if not make.options.dry_run:
124 oe.build.exec_task('do_%s' % make.options.cmd, make.pkgdata[fn])
125 oe.event.fire(oe.event.PkgSucceeded(item, make.pkgdata[fn]))
126 __build_cache.append(fn)
128 except oe.build.FuncFailed:
130 oe.error("task stack execution failed")
131 oe.event.fire(oe.event.PkgFailed(item, make.pkgdata[fn]))
132 __build_cache_fail.append(fn)
134 except oe.build.EventException:
136 (type, value, traceback) = sys.exc_info()
138 oe.error("%s event exception, aborting" % oe.event.getName(e))
139 oe.event.fire(oe.event.PkgFailed(item, make.pkgdata[fn]))
140 __build_cache_fail.append(fn)
143 __building_list.remove(fn)
144 __build_path.remove(pathstr)
146 def buildPackage(item):
149 discriminated = False
151 if not providers.has_key(item):
152 oe.error("Nothing provides %s" % item)
155 all_p = providers[item]
158 if p in __build_cache:
159 oe.debug(1, "already built %s in this run\n" % p)
163 preferred_versions = {}
165 # Collate providers by PN
168 the_data = make.pkgdata[p]
169 pn = oe.data.getVar('PN', the_data, 1)
170 if not pkg_pn.has_key(pn):
174 oe.debug(1, "providers for %s are: %s" % (item, pkg_pn.keys()))
177 for pn in pkg_pn.keys():
181 priority = oefile_priority[f]
182 if not priorities.has_key(priority):
183 priorities[priority] = []
184 priorities[priority].append(f)
185 p_list = priorities.keys()
186 p_list.sort(lambda a, b: a - b)
189 pkg_pn[pn] = [ priorities[p] ] + pkg_pn[pn]
191 # If there is a PREFERRED_VERSION, find the highest-priority oefile providing that
192 # version. If not, find the latest version provided by an oefile in the
193 # highest-priority set.
194 for pn in pkg_pn.keys():
195 preferred_file = None
197 preferred_v = oe.data.getVar('PREFERRED_VERSION_%s' % pn, make.cfg, 1)
200 m = re.match('(.*)_(.*)', preferred_v)
202 preferred_v = m.group(1)
203 preferred_r = m.group(2)
205 for file_set in pkg_pn[pn]:
207 the_data = make.pkgdata[f]
208 pv = oe.data.getVar('PV', the_data, 1)
209 pr = oe.data.getVar('PR', the_data, 1)
210 if preferred_v == pv and (preferred_r == pr or preferred_r == None):
212 preferred_ver = (pv, pr)
217 pv_str = '%s-%s' % (preferred_v, preferred_r)
220 if preferred_file is None:
221 oe.note("preferred version %s of %s not available" % (pv_str, pn))
223 oe.debug(1, "selecting %s as PREFERRED_VERSION %s of package %s" % (preferred_file, pv_str, pn))
225 if preferred_file is None:
226 # get highest priority file set
227 files = pkg_pn[pn][0]
232 the_data = make.pkgdata[f]
233 pv = oe.data.getVar('PV', the_data, 1)
234 pr = oe.data.getVar('PR', the_data, 1)
235 dp = int(oe.data.getVar('DEFAULT_PREFERENCE', the_data, 1) or "0")
237 if (latest is None) or ((latest_p == dp) and (make.vercmp(latest, (pv, pr)) < 0)) or (dp > latest_p):
241 preferred_file = latest_f
242 preferred_ver = latest
244 oe.debug(1, "selecting %s as latest version of provider %s" % (preferred_file, pn))
246 preferred_versions[pn] = (preferred_ver, preferred_file)
247 eligible.append(preferred_file)
250 if p in __build_cache_fail:
251 oe.debug(1, "rejecting already-failed %s" % p)
254 if len(eligible) == 0:
255 oe.error("no eligible providers for %s" % item)
258 # look to see if one of them is already staged, or marked as preferred.
259 # if so, bump it to the head of the queue
261 the_data = make.pkgdata[p]
262 pn = oe.data.getVar('PN', the_data, 1)
263 pv = oe.data.getVar('PV', the_data, 1)
264 pr = oe.data.getVar('PR', the_data, 1)
265 tmpdir = oe.data.getVar('TMPDIR', the_data, 1)
266 stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
267 if os.path.exists(stamp):
268 (newvers, fn) = preferred_versions[pn]
269 if not fn in eligible:
270 # package was made ineligible by already-failed check
272 oldver = "%s-%s" % (pv, pr)
273 newver = '-'.join(newvers)
274 if (newver != oldver):
275 extra_chat = "; upgrading from %s to %s" % (oldver, newver)
278 if make.options.verbose:
279 oe.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
281 eligible = [fn] + eligible
285 prefervar = oe.data.getVar('PREFERRED_PROVIDER_%s' % item, make.cfg, 1)
287 __preferred[item] = prefervar
289 if __preferred.has_key(item):
291 the_data = make.pkgdata[p]
292 pn = oe.data.getVar('PN', the_data, 1)
293 if __preferred[item] == pn:
294 if make.options.verbose:
295 oe.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
297 eligible = [p] + eligible
301 if len(eligible) > 1 and discriminated == False:
304 providers_list.append(oe.data.getVar('PN', make.pkgdata[fn], 1))
305 oe.note("multiple providers are available (%s);" % ", ".join(providers_list))
306 oe.note("consider defining PREFERRED_PROVIDER_%s" % item)
308 # run through the list until we find one that we can build
310 oe.debug(2, "selecting %s to satisfy %s" % (fn, item))
311 if try_build(fn, item):
314 oe.note("no buildable providers for %s" % item)
317 def build_depgraph():
322 if oedebug or progress.p == p: return
324 if os.isatty(sys.stdout.fileno()):
325 sys.stdout.write("\rNOTE: Building provider hash: [%s%s] (%02d%%)" % ( "#" * (p/5), " " * ( 20 - p/5 ), p ) )
329 sys.stdout.write("NOTE: Building provider hash, please wait...\n")
331 sys.stdout.write("done.\n")
334 def calc_oefile_priority(filename):
335 for (regex, pri) in oefile_config_priorities:
336 if regex.match(filename):
340 # Handle PREFERRED_PROVIDERS
341 for p in (oe.data.getVar('PREFERRED_PROVIDERS', make.cfg, 1) or "").split():
342 (providee, provider) = p.split(':')
343 if __preferred.has_key(providee) and __preferred[providee] != provider:
344 oe.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, __preferred[providee]))
345 __preferred[providee] = provider
347 # Calculate priorities for each file
348 for p in make.pkgdata.keys():
349 oefile_priority[p] = calc_oefile_priority(p)
351 n = len(make.pkgdata.keys())
356 oe.debug(1, "OEMAKE building providers hashes")
358 # Build forward and reverse provider hashes
359 # Forward: virtual -> [filenames]
360 # Reverse: PN -> [virtuals]
361 for f in make.pkgdata.keys():
364 pn = oe.data.getVar('PN', d, 1)
365 provides = Set([pn] + (oe.data.getVar("PROVIDES", d, 1) or "").split())
367 if not pn_provides.has_key(pn):
368 pn_provides[pn] = Set()
369 pn_provides[pn] |= provides
371 for provide in provides:
372 if not providers.has_key(provide):
373 providers[provide] = []
374 providers[provide].append(f)
376 deps = (oe.data.getVar("DEPENDS", d, 1) or "").split()
387 sys.stdout.write("\n")
389 # Build package list for "oemake world"
390 oe.debug(1, "OEMAKE collating packages for \"world\"")
391 for f in make.pkgdata.keys():
393 if oe.data.getVar('BROKEN', d, 1):
396 pn = oe.data.getVar('PN', d, 1)
397 for p in pn_provides[pn]:
398 if p in all_depends or p.startswith('virtual/'):
402 __world_target.add(pn)
404 def myProgressCallback( x, y, f ):
407 if os.isatty(sys.stdout.fileno()):
408 sys.stdout.write("\rNOTE: Parsing .oe files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
412 sys.stdout.write("Parsing .oe files, please wait...")
415 sys.stdout.write("done.")
423 if __name__ == "__main__":
425 if "OEDEBUG" in os.environ:
426 oedebug = int(os.environ["OEDEBUG"])
428 make.options, args = handle_options( sys.argv )
430 if not make.options.cmd:
431 make.options.cmd = "build"
433 if make.options.cmd in __depcmds:
434 __depcmd=__depcmds[make.options.cmd]
436 __depcmd=make.options.cmd
439 make.cfg = oe.data.init()
442 for f in make.options.file:
444 make.cfg = oe.parse.handle(f, make.cfg)
446 oe.fatal("Unable to open %s" % f)
449 make.cfg = oe.parse.handle("conf/oe.conf", make.cfg)
451 oe.fatal("Unable to open oe.conf")
453 if not oe.data.getVar("BUILDNAME", make.cfg):
454 oe.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), make.cfg)
456 buildname = oe.data.getVar("BUILDNAME", make.cfg)
458 ignore = oe.data.getVar("ASSUME_PROVIDED", make.cfg, 1) or ""
459 __ignored_dependencies = ignore.split()
461 collections = oe.data.getVar("OEFILE_COLLECTIONS", make.cfg, 1)
463 collection_list = collections.split()
464 for c in collection_list:
465 regex = oe.data.getVar("OEFILE_PATTERN_%s" % c, make.cfg, 1)
467 oe.error("OEFILE_PATTERN_%s not defined" % c)
469 priority = oe.data.getVar("OEFILE_PRIORITY_%s" % c, make.cfg, 1)
471 oe.error("OEFILE_PRIORITY_%s not defined" % c)
474 cre = re.compile(regex)
476 oe.error("OEFILE_PATTERN_%s \"%s\" is not a valid regular expression" % (c, regex))
480 oefile_config_priorities.append((cre, pri))
482 oe.error("invalid value for OEFILE_PRIORITY_%s: \"%s\"" % (c, priority))
486 if not pkgs_to_build:
488 pkgs_to_build.extend(args)
489 if not pkgs_to_build:
490 oepkgs = oe.data.getVar('OEPKGS', make.cfg, 1)
492 pkgs_to_build = oepkgs.split()
493 if not pkgs_to_build:
494 print "Nothing to build. Use 'oemake world' to build everything."
497 __stats["attempt"] = 0
498 __stats["success"] = 0
502 # Import Psyco if available and not disabled
503 if not make.options.disable_psyco:
508 oe.note("Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance.")
510 psyco.bind( make.collect_oefiles )
512 oe.note("You have disabled Psyco. This decreases performance.")
515 oe.debug(1, "OEMAKE collecting .oe files")
516 make.collect_oefiles( myProgressCallback )
517 oe.debug(1, "OEMAKE parsing complete")
520 if make.options.parse_only:
521 print "Requested parsing .oe files only. Exiting."
525 if 'world' in pkgs_to_build:
526 pkgs_to_build.remove('world')
527 for t in __world_target:
528 pkgs_to_build.append(t)
530 oe.event.fire(oe.event.BuildStarted(buildname, pkgs_to_build, make.cfg))
532 for k in pkgs_to_build:
535 if buildPackage(k) == 0:
538 except oe.build.EventException:
539 oe.error("Build of " + k + " failed")
543 if make.options.abort:
546 oe.event.fire(oe.event.BuildCompleted(buildname, pkgs_to_build, make.cfg))
548 print "Build statistics:"
549 print " Attempted builds: %d" % __stats["attempt"]
550 if __stats["fail"] != 0:
551 print " Failed builds: %d" % __stats["fail"]
552 if __stats["deps"] != 0:
553 print " Dependencies not satisfied: %d" % __stats["deps"]
554 if __stats["fail"] != 0 or __stats["deps"] != 0:
558 except KeyboardInterrupt:
559 print "\nNOTE: KeyboardInterrupt - Build not completed."