if a build fails, re-throw the exception rather than trying other providers
[bitbake.git] / bin / oemake
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 import sys, os, getopt, glob, copy, os.path, re
6 sys.path.append('/usr/share/oe')
7 import oe
8 from oe import make
9 from sets import Set
10 import itertools, optparse
11
12 parsespin = itertools.cycle( r'|/-\-' )
13
14 __version__ = 1.2
15 __build_cache_fail = []
16 __build_cache = []
17 __building_list = []
18 __build_path = []
19
20 __preferred = {}
21 __world_target = Set()
22 __ignored_dependencies = Set()
23 __depcmds = { "clean": None,
24              "mrproper": None }
25
26 __stats = {}
27
28 def handle_options( args ):
29     parser = optparse.OptionParser( version = "OpenEmbedded Build Infrastructure Core version %s, %%prog version %s" % ( oe.__version__, __version__ ),
30     usage = """%prog [options] [package ...]
31
32 Builds specified packages, expecting that the .oe files
33 it has to work from are in OEFILES
34 Default packages are all packages in OEFILES.
35 Default OEFILES are the .oe files in the current directory.""" )
36
37     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.",
38                action = "store_false", dest = "abort", default = True )
39
40     parser.add_option( "-f", "--force", help = "force run of specified cmd, regardless of stamp status",
41                action = "store_true", dest = "force", default = False )
42
43
44     parser.add_option( "-c", "--cmd", help = "specify command to pass to oebuild. Valid commands are "
45                                              "'fetch' (fetch all sources), " 
46                                              "'unpack' (unpack the sources), "
47                                              "'patch' (apply the patches), "
48                                              "'configure' (configure the source tree), "
49                                              "'compile' (compile the source tree), "
50                                              "'stage' (install libraries and headers needed for subsequent packages), "
51                                              "'install' (install libraries and executables), and"
52                                              "'package' (package files into the selected package format)",
53                action = "store", dest = "cmd", default = "build" )
54
55     parser.add_option( "-r", "--read", help = "read the specified file before oe.conf",
56                action = "append", dest = "file", default = [] )
57
58     parser.add_option( "-v", "--verbose", help = "output more chit-chat to the terminal",
59                action = "store_true", dest = "verbose", default = False )
60
61     parser.add_option( "-n", "--dry-run", help = "don't call oebuild, just go through the motions",
62                action = "store_true", dest = "dry_run", default = False )
63
64     parser.add_option( "-p", "--parse-only", help = "quit after parsing the OE files (developers only)",
65                action = "store_true", dest = "parse_only", default = False )
66
67     parser.add_option( "-d", "--disable-psyco", help = "disable using the psyco just-in-time compiler (not recommended)",
68                action = "store_true", dest = "disable_psyco", default = False )
69
70     options, args = parser.parse_args( args )
71     return options, args[1:]
72
73 def try_build(fn, virtual):
74     if fn in __building_list:
75         oe.error("%s depends on itself (eventually)" % fn)
76         oe.error("upwards chain is: %s" % (" -> ".join(__build_path)))
77         return False
78
79     __building_list.append(fn)
80
81     the_data = make.pkgdata[fn]
82     item = oe.data.getVar('PN', the_data, 1)
83     pathstr = "%s (%s)" % (item, virtual)
84     __build_path.append(pathstr)
85
86     depends_list = (oe.data.getVar('DEPENDS', the_data, 1) or "").split()
87     if make.options.verbose:
88         oe.note("current path: %s" % (" -> ".join(__build_path)))
89         oe.note("dependencies for %s are: %s" % (item, " ".join(depends_list)))
90
91     try:
92         failed = False
93
94         if __depcmd:
95             oldcmd = make.options.cmd
96             make.options.cmd = __depcmd
97
98         for d in depends_list:
99             if d in __ignored_dependencies:
100                 continue
101             if not __depcmd:
102                 continue
103             if buildPackage(d) == 0:
104                 oe.error("dependency %s (for %s) not satisfied" % (d,item))
105                 failed = True
106                 if make.options.abort:
107                     break
108
109         if __depcmd:
110             make.options.cmd = oldcmd
111
112         if failed:
113             __stats["deps"] += 1
114             return False
115
116         oe.event.fire(oe.event.PkgStarted(item, make.pkgdata[fn]))
117         try:
118             __stats["attempt"] += 1
119             if not make.options.dry_run:
120                 oe.build.exec_task('do_%s' % make.options.cmd, make.pkgdata[fn])
121             oe.event.fire(oe.event.PkgSucceeded(item, make.pkgdata[fn]))
122             __build_cache.append(fn)
123             return True
124         except oe.build.FuncFailed:
125             __stats["fail"] += 1
126             oe.error("task stack execution failed")
127             oe.event.fire(oe.event.PkgFailed(item, make.pkgdata[fn]))
128             __build_cache_fail.append(fn)
129             raise
130         except oe.build.EventException:
131             __stats["fail"] += 1
132             (type, value, traceback) = sys.exc_info()
133             e = value.event
134             oe.error("%s event exception, aborting" % oe.event.getName(e))
135             oe.event.fire(oe.event.PkgFailed(item, make.pkgdata[fn]))
136             __build_cache_fail.append(fn)
137             raise
138     finally:
139         __building_list.remove(fn)
140         __build_path.remove(pathstr)
141
142 def buildPackage(item):
143     fn = None
144
145     if not providers.has_key(item):
146         oe.error("Nothing provides %s" % item)
147         return 0
148
149     all_p = providers[item]
150
151     for p in all_p:
152         if p in __build_cache:
153             return 1
154
155     versions = {}
156     for p in all_p:
157         the_data = make.pkgdata[p]
158         pn = oe.data.getVar('PN', the_data, 1)
159         pv = oe.data.getVar('PV', the_data, 1)
160         pr = oe.data.getVar('PR', the_data, 1)
161         if not versions.has_key(pn):
162             versions[pn] = []
163         versions[pn].append(((pv, pr), p))
164
165 #    # find the latest version of each provider
166     preferred_versions = {}
167     for p in versions.keys():
168         latest = None
169         latest_f = None
170         for (v, _fn) in versions[p]:
171             if (latest is None) or (make.vercmp(latest, v) < 0):
172                 latest = v
173                 latest_f = _fn
174         preferred_versions[p] = (latest, latest_f)
175
176 #    # build a new list with just the latest version of everything
177     eligible = []
178     for p in preferred_versions.keys():
179         (v, f) = preferred_versions[p]
180         eligible.append(f)
181
182     for p in eligible:
183         if p in __build_cache_fail:
184             oe.debug(1, "rejecting already-failed %s" % p)
185             eligible.remove(p)
186
187     if len(eligible) == 0:
188         oe.error("no eligible providers for %s" % item)
189         return 0
190
191 #    # look to see if one of them is already staged, or marked as preferred.
192 #    # if so, bump it to the head of the queue
193     for p in all_p:
194         the_data = make.pkgdata[p]
195         pn = oe.data.getVar('PN', the_data, 1)
196         pv = oe.data.getVar('PV', the_data, 1)
197         pr = oe.data.getVar('PR', the_data, 1)
198         tmpdir = oe.data.getVar('TMPDIR', the_data, 1)
199         stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
200         if os.path.exists(stamp):
201             (newvers, fn) = preferred_versions[pn]
202             if not fn in eligible:
203 #                # package was made ineligible by already-failed check
204                 continue
205             oldver = "%s-%s" % (pv, pr)
206             newver = '-'.join(newvers)
207             if (newver != oldver):
208                 extra_chat = "; upgrading from %s to %s" % (oldver, newver)
209             else:
210                 extra_chat = ""
211             if make.options.verbose:
212                 oe.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
213             eligible.remove(fn)
214             eligible = [fn] + eligible
215             break
216
217     prefervar = oe.data.getVar('PREFERRED_PROVIDER_%s' % item, make.cfg, 1)
218     if prefervar:
219         __preferred[item] = prefervar
220
221     if __preferred.has_key(item):
222         for p in eligible:
223             the_data = make.pkgdata[p]
224             pn = oe.data.getVar('PN', the_data, 1)
225             if __preferred[item] == pn:
226                 if make.options.verbose:
227                     oe.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
228                 eligible.remove(p)
229                 eligible = [p] + eligible
230                 break
231
232 #    # run through the list until we find one that we can build
233     for fn in eligible:
234         oe.debug(2, "selecting %s to satisfy %s" % (fn, item))
235         if try_build(fn, item):
236             return 1
237
238     oe.note("no buildable providers for %s" % item)
239     return 0
240
241 def build_depgraph():
242     all_depends = Set()
243     pn_provides = {}
244
245     def progress(p):
246         if os.isatty(sys.stdout.fileno()):
247             sys.stdout.write("\rNOTE: Building provider hash: [")
248             x = 0
249             for i in range(20):
250                 if (p >= x):
251                     sys.stdout.write("#")
252                 else:
253                     sys.stdout.write(" ")
254                 x += 5
255             sys.stdout.write("] (%02d%%)" % p)
256         else:
257             if p == 0:
258                 sys.stdout.write("\rNOTE: Building provider hash, please wait...")
259                 sys.stdout.flush()
260             if p == 100:
261                 sys.stdout.write("done.")
262                 sys.stdout.flush()
263
264     n = len(make.pkgdata.keys())
265     i = 0
266
267     op = -1
268
269     for p in (oe.data.getVar('PREFERRED_PROVIDERS', make.cfg, 1) or "").split():
270         (providee, provider) = p.split(':')
271         if __preferred.has_key(providee) and __preferred[providee] != provider:
272             oe.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, __preferred[providee]))
273         __preferred[providee] = provider
274
275     for f in make.pkgdata.keys():
276         d = make.pkgdata[f]
277
278         pn = oe.data.getVar('PN', d, 1)
279
280         deps = (oe.data.getVar("DEPENDS", d, 1) or "").split()
281         provides = Set([pn] + (oe.data.getVar("PROVIDES", d, 1) or "").split())
282
283         for dep in deps:
284             all_depends.add(dep)
285
286         if not pn_provides.has_key(pn):
287             pn_provides[pn] = Set()
288         pn_provides[pn] |= provides
289
290         for provide in provides:
291             if not providers.has_key(provide):
292                 providers[provide] = []
293             providers[provide].append(f)
294
295         i += 1
296         p = (100 * i) / n
297         if p != op:
298             op = p
299             progress(p)
300
301     sys.stdout.write("\n")
302
303     for f in make.pkgdata.keys():
304         d = make.pkgdata[f]
305         if oe.data.getVar('BROKEN', d, 1):
306             continue
307         terminal = True
308         pn = oe.data.getVar('PN', d, 1)
309         for p in pn_provides[pn]:
310             if p in all_depends or p.startswith('virtual/'):
311                 terminal = False
312                 break
313         if terminal:
314             __world_target.add(pn)
315
316 def myProgressCallback( x, y, f ):
317     if os.isatty(sys.stdout.fileno()):
318         sys.stdout.write("\rNOTE: Parsing .oe files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
319         sys.stdout.flush()
320     else:
321         if x == 1:
322             sys.stdout.write("Parsing .oe files, please wait...")
323             sys.stdout.flush()
324         if x == y:
325             sys.stdout.write("done.")
326             sys.stdout.flush()
327
328
329 #
330 # main
331 #
332
333 if __name__ == "__main__":
334
335     make.options, args = handle_options( sys.argv )
336
337     if not make.options.cmd:
338         make.options.cmd = "build"
339
340     if make.options.cmd in __depcmds:
341         __depcmd=__depcmds[make.options.cmd]
342     else:
343         __depcmd=make.options.cmd
344
345     make.pkgdata = {}
346     make.cfg = {}
347     providers = {}
348
349     for f in make.options.file:
350         try:
351             make.cfg = oe.parse.handle(f, make.cfg)
352         except IOError:
353             oe.fatal("Unable to open %s" % f)
354
355     try:
356         make.cfg = oe.parse.handle("conf/oe.conf", make.cfg)
357     except IOError:
358         oe.fatal("Unable to open oe.conf")
359
360     if not oe.data.getVar("BUILDNAME", make.cfg):
361         oe.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), make.cfg)
362
363     buildname = oe.data.getVar("BUILDNAME", make.cfg)
364
365     ignore = oe.data.getVar("ASSUME_PROVIDED", make.cfg, 1) or ""
366     __ignored_dependencies = ignore.split()
367
368     pkgs_to_build = None
369     if args:
370         if not pkgs_to_build:
371             pkgs_to_build = []
372         pkgs_to_build.extend(args)
373     if not pkgs_to_build:
374             oepkgs = oe.data.getVar('OEPKGS', make.cfg, 1)
375             if oepkgs:
376                     pkgs_to_build = oepkgs.split()
377     if not pkgs_to_build:
378             print "Nothing to build. Use 'oemake world' to build everything."
379             sys.exit(0)
380
381     __stats["attempt"] = 0
382     __stats["success"] = 0
383     __stats["fail"] = 0
384     __stats["deps"] = 0
385
386     # Import Psyco if available and not disabled
387     if not make.options.disable_psyco:
388         try:
389             import psyco
390         except ImportError:
391             print "NOTE: Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance."
392         else:
393             psyco.bind( make.collect_oefiles )
394     else:
395         print "NOTE: You have disabled Psyco. This decreases performance."
396
397     try:
398         make.collect_oefiles( myProgressCallback )
399         print
400         if make.options.parse_only:
401             print "Requested parsing .oe files only.  Exiting."
402             sys.exit(0)
403         build_depgraph()
404
405         if 'world' in pkgs_to_build:
406             pkgs_to_build.remove('world')
407             for t in __world_target:
408                 pkgs_to_build.append(t)
409
410         oe.event.fire(oe.event.BuildStarted(buildname, pkgs_to_build, make.cfg))
411
412         for k in pkgs_to_build:
413             if buildPackage(k) == 0:
414                 oe.error("Build of " + k + " failed")
415                 if make.options.abort:
416                     sys.exit(1)
417
418         oe.event.fire(oe.event.BuildCompleted(buildname, pkgs_to_build, make.cfg))
419
420         print "Build statistics:"
421         print "  Attempted builds: %d" % __stats["attempt"]
422         if __stats["fail"] != 0:
423             print "  Failed builds: %d" % __stats["fail"]
424         if __stats["deps"] != 0:
425             print "  Dependencies not satisfied: %d" % __stats["deps"]
426         if __stats["fail"] != 0 or __stats["deps"] != 0:
427             sys.exit(1)
428         sys.exit(0)
429
430     except KeyboardInterrupt:
431         print "\nNOTE: KeyboardInterrupt - Build not completed."