add documentation about common commands
[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             return False
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             return False
138     finally:
139         __building_list.remove(fn)
140         __build_path.remove(pathstr)
141
142
143 def buildPackage(item):
144     fn = None
145
146     if not providers.has_key(item):
147         oe.error("Nothing provides %s" % item)
148         return 0
149
150     all_p = providers[item]
151
152     for p in all_p:
153         if p in __build_cache:
154             return 1
155
156     versions = {}
157     for p in all_p:
158         the_data = make.pkgdata[p]
159         pn = oe.data.getVar('PN', the_data, 1)
160         pv = oe.data.getVar('PV', the_data, 1)
161         pr = oe.data.getVar('PR', the_data, 1)
162         if not versions.has_key(pn):
163             versions[pn] = []
164         versions[pn].append(((pv, pr), p))
165
166 #    # find the latest version of each provider
167     preferred_versions = {}
168     for p in versions.keys():
169         latest = None
170         latest_f = None
171         for (v, _fn) in versions[p]:
172             if (latest is None) or (make.vercmp(latest, v) < 0):
173                 latest = v
174                 latest_f = _fn
175         preferred_versions[p] = (latest, latest_f)
176
177 #    # build a new list with just the latest version of everything
178     eligible = []
179     for p in preferred_versions.keys():
180         (v, f) = preferred_versions[p]
181         eligible.append(f)
182
183     for p in eligible:
184         if p in __build_cache_fail:
185             oe.debug(1, "rejecting already-failed %s" % p)
186             eligible.remove(p)
187
188     if len(eligible) == 0:
189         oe.error("no eligible providers for %s" % item)
190         return 0
191
192 #    # look to see if one of them is already staged, or marked as preferred.
193 #    # if so, bump it to the head of the queue
194     for p in all_p:
195         the_data = make.pkgdata[p]
196         pn = oe.data.getVar('PN', the_data, 1)
197         pv = oe.data.getVar('PV', the_data, 1)
198         pr = oe.data.getVar('PR', the_data, 1)
199         tmpdir = oe.data.getVar('TMPDIR', the_data, 1)
200         stamp = '%s/stamps/%s-%s-%s.do_populate_staging' % (tmpdir, pn, pv, pr)
201         if os.path.exists(stamp):
202             (newvers, fn) = preferred_versions[pn]
203             if not fn in eligible:
204 #                # package was made ineligible by already-failed check
205                 continue
206             oldver = "%s-%s" % (pv, pr)
207             newver = '-'.join(newvers)
208             if (newver != oldver):
209                 extra_chat = "; upgrading from %s to %s" % (oldver, newver)
210             else:
211                 extra_chat = ""
212             if make.options.verbose:
213                 oe.note("selecting already-staged %s to satisfy %s%s" % (pn, item, extra_chat))
214             eligible.remove(fn)
215             eligible = [fn] + eligible
216             break
217
218     if __preferred.has_key(item):
219         for p in eligible:
220             the_data = make.pkgdata[p]
221             pn = oe.data.getVar('PN', the_data, 1)
222             if __preferred[item] == pn:
223                 if make.options.verbose:
224                     oe.note("selecting %s to satisfy %s due to PREFERRED_PROVIDERS" % (pn, item))
225                 eligible.remove(p)
226                 eligible = [p] + eligible
227                 break
228
229 #    # run through the list until we find one that we can build
230     for fn in eligible:
231         oe.debug(2, "selecting %s to satisfy %s" % (fn, item))
232         if try_build(fn, item):
233             return 1
234
235     oe.note("no buildable providers for %s" % item)
236     return 0
237
238
239 def build_depgraph():
240     all_depends = Set()
241     pn_provides = {}
242
243     for f in make.pkgdata.keys():
244         d = make.pkgdata[f]
245
246         pn = oe.data.getVar('PN', d, 1)
247
248         deps = (oe.data.getVar("DEPENDS", d, 1) or "").split()
249         provides = Set([pn] + (oe.data.getVar("PROVIDES", d, 1) or "").split())
250
251         for dep in deps:
252             all_depends.add(dep)
253
254         if not pn_provides.has_key(pn):
255             pn_provides[pn] = Set()
256         pn_provides[pn] |= provides
257
258         for provide in provides:
259             if not providers.has_key(provide):
260                 providers[provide] = []
261             providers[provide].append(f)
262
263         for p in (oe.data.getVar('PREFERRED_PROVIDERS', d, 1) or "").split():
264             (providee, provider) = p.split(':')
265             if __preferred.has_key(providee) and __preferred[providee] != provider:
266                 oe.error("conflicting preferences for %s: both %s and %s specified" % (providee, provider, __preferred[providee]))
267             __preferred[providee] = provider
268
269     for f in make.pkgdata.keys():
270         d = make.pkgdata[f]
271         if oe.data.getVar('BROKEN', d, 1):
272             continue
273         terminal = True
274         pn = oe.data.getVar('PN', d, 1)
275         for p in pn_provides[pn]:
276             if p in all_depends or p.startswith('virtual/'):
277                 terminal = False
278                 break
279         if terminal:
280             __world_target.add(pn)
281
282 def myProgressCallback( x, y, f ):
283     if os.isatty(sys.stdout.fileno()):
284         sys.stdout.write("\rNOTE: Parsing .oe files: %s (%04d/%04d) [%2d %%]" % ( parsespin.next(), x, y, x*100/y ) )
285         sys.stdout.flush()
286     else:
287         if x == 1:
288             sys.stdout.write("Parsing .oe files, please wait...")
289             sys.stdout.flush()
290         if x == y:
291             sys.stdout.write("done.\n")
292             sys.stdout.flush()
293
294
295 #
296 # main
297 #
298
299 if __name__ == "__main__":
300
301     make.options, args = handle_options( sys.argv )
302
303     if not make.options.cmd:
304         make.options.cmd = "build"
305
306     if make.options.cmd in __depcmds:
307         __depcmd=__depcmds[make.options.cmd]
308     else:
309         __depcmd=make.options.cmd
310
311     make.pkgdata = {}
312     make.cfg = {}
313     providers = {}
314
315     for f in make.options.file:
316         try:
317             make.cfg = oe.parse.handle(f, make.cfg)
318         except IOError:
319             oe.fatal("Unable to open %s" % f)
320
321     try:
322         make.cfg = oe.parse.handle("conf/oe.conf", make.cfg)
323     except IOError:
324         oe.fatal("Unable to open oe.conf")
325
326     if not oe.data.getVar("BUILDNAME", make.cfg):
327         oe.data.setVar("BUILDNAME", os.popen('date +%Y%m%d%H%M').readline().strip(), make.cfg)
328
329     buildname = oe.data.getVar("BUILDNAME", make.cfg)
330
331     ignore = oe.data.getVar("ASSUME_PROVIDED", make.cfg, 1) or ""
332     __ignored_dependencies = ignore.split()
333
334     pkgs_to_build = None
335     if args:
336         if not pkgs_to_build:
337             pkgs_to_build = []
338         pkgs_to_build.extend(args)
339     if not pkgs_to_build:
340             oepkgs = oe.data.getVar('OEPKGS', make.cfg, 1)
341             if oepkgs:
342                     pkgs_to_build = oepkgs.split()
343     if not pkgs_to_build:
344             print "Nothing to build. Use 'oemake world' to build everything."
345             sys.exit(0)
346
347     __stats["attempt"] = 0
348     __stats["success"] = 0
349     __stats["fail"] = 0
350     __stats["deps"] = 0
351
352     # Import Psyco if available and not disabled
353     if not make.options.disable_psyco:
354         try:
355             import psyco
356         except ImportError:
357             print "NOTE: Psyco JIT Compiler (http://psyco.sf.net) not available. Install it to increase performance."
358         else:
359             psyco.bind( make.collect_oefiles )
360     else:
361         print "NOTE: You have disabled Psyco. This decreases performance."
362
363     try:
364         make.collect_oefiles( myProgressCallback )
365         print
366         if make.options.parse_only:
367             print "Requested parsing .oe files only.  Exiting."
368             sys.exit(0)
369         build_depgraph()
370
371         if 'world' in pkgs_to_build:
372             pkgs_to_build.remove('world')
373             for t in __world_target:
374                 pkgs_to_build.append(t)
375
376         oe.event.fire(oe.event.BuildStarted(buildname, pkgs_to_build, make.cfg))
377
378         for k in pkgs_to_build:
379             if buildPackage(k) == 0:
380                 oe.error("Build of " + k + " failed")
381                 if make.options.abort:
382                     sys.exit(1)
383
384         oe.event.fire(oe.event.BuildCompleted(buildname, pkgs_to_build, make.cfg))
385
386         print "Build statistics:"
387         print "  Attempted builds: %d" % __stats["attempt"]
388         if __stats["fail"] != 0:
389             print "  Failed builds: %d" % __stats["fail"]
390         if __stats["deps"] != 0:
391             print "  Dependencies not satisfied: %d" % __stats["deps"]
392         if __stats["fail"] != 0 or __stats["deps"] != 0:
393             sys.exit(1)
394         sys.exit(0)
395
396     except KeyboardInterrupt:
397         print "\nNOTE: KeyboardInterrupt - Build not completed."