OE Package
[bitbake.git] / bin / oebuild
1 #!/usr/bin/python
2
3 import sys,os,string
4 import oe.fetch
5 sys.path.append(os.path.join(sys.prefix,'share/oe'))
6 from oe import *
7
8
9 #######################################################################
10 class FuncFailed(Exception):
11         """Executed function failed"""
12
13 def exec_shell_func(func, dir=None, myfatal=fatal):
14         """This executes any shell function stored in env[func]. Returns
15         true if execution was successful.
16
17         For this, it creates a bash shell script in the tmp directory, writes the local
18         environment in env[] into it and finally executes this. The output of the
19         shell will end in a log file and stdout.
20         """
21
22         if tasks.has_key(func) and tasks[func].has_key('deps'):
23                 deps = tasks[func]['deps']
24         else:
25                 deps = None
26
27         if stamp_is_current(func, deps):
28                 return
29
30         global logfile
31         mkdirhier(getenv('T'))
32         logfile = getenv('T') + '/log.' + func + '.' + str(os.getpid())
33         runfile = getenv('T') + '/run.' + func + '.' + str(os.getpid())
34
35         f = open(runfile, "w")
36         f.write("#!/bin/bash\n")
37         if env.has_key('OEDEBUG'): f.write("set -x\n")
38         if env.has_key('OEPATH'):
39                 for s in expand(env['OEPATH']).split(":"):
40                         f.write("if test -f %s/build/oebuild.sh; then source %s/build/oebuild.sh; fi\n" % (s,s));
41         emit_env(f)
42
43         if dir:  f.write("cd %s\n" % env[dir])
44         if func: f.write(func +"\n")
45         f.close()
46         os.chmod(runfile, 0775)
47         if not func:
48                 error("Function not specified")
49                 raise FuncFailed()
50         ret = os.system("bash -c 'source %s' 2>&1 | tee %s; exit $PIPESTATUS" % (runfile, logfile))
51         if ret==0:
52                 if not env.has_key('OEDEBUG'): 
53                         os.remove(runfile)
54                         os.remove(logfile)
55                 mkstamp(func)
56                 return
57         else:
58                 error("'%s'() failed" % func);
59                 error("more info in: %s" % logfile);
60                 raise FuncFailed()
61
62
63 def exec_python_func(func, dirname = None, myfatal = fatal):
64         if tasks.has_key(func) and tasks[func].has_key('deps'):
65                 deps = tasks[func]['deps']
66         else:
67                 deps = None
68
69         if stamp_is_current(func, deps):
70                 return
71
72         if not env.has_key(dirname):
73                 warn("dir variable %s does not exist, cannot chdir" % dirname)
74                 return
75
76         dir = env[dirname]
77
78         if not os.path.isdir(dir):
79                 debug(1, "%s does not exist, creating it." % dir)
80                 mkdirhier(dir)
81
82         os.chdir(dir)
83         try:
84                 comp = compile(getenv(func),func,'exec')
85                 exec(comp)
86         except Exception, e:
87                 (type, value, traceback) = sys.exc_info()
88                 error("Python function %s failed: %s" % (func, value))
89                 raise FuncFailed(value)
90         mkstamp(func)
91
92
93 #######################################################################
94
95 def exec_task(task, dir, deptasks = []):
96         """Executes a given task, handling the stamps appropriately"""
97
98         if not deptasks:
99                 try:
100                         deptasks = tasks[task]["deps"]
101                 except KeyError:
102                         pass
103
104         for dep in deptasks:
105                 try:
106                         depfuncname = tasks[dep]["func"]
107                         depfunc = globals()[depfuncname]
108                         depfunc()
109                 except KeyError:
110                         debug(2, "%s: invalid 'func' for task %s, running exec_task directly." % (task, dep))
111                         if tasks.has_key(dep) and tasks[dep].has_key("deps"):
112                                 exec_task(dep, dir, tasks[dep]["deps"])
113                         else:
114                                 exec_task(dep, dir)
115
116         if not tasks.has_key(task):
117                 if not envflags.has_key(task) and not env.has_key(task):
118                         error("unsupported task: %s" % task)
119                         raise FuncFailed()
120
121         debug(1, "Executing task '%s'" % task)
122
123         if envflags.has_key(task) and envflags[task].has_key("python") and envflags[task]["python"] is not None:
124                 # execute this function as a block of python code, not shell
125                 exec_python_func(task, dir)
126         else:
127                 exec_shell_func(task, dir)
128
129 def mkstamp(task):
130         """Creates/updates a stamp for a given task"""
131         mkdirhier(expand('${TMPDIR}/stamps'));
132         open(getenv('STAMP')+"."+task, "w+")
133
134 def stamp_is_current(task, deptasks=""):
135         """Check status of a given task's stamp. returns False if it is not current and needs updating."""
136         stampfile = getenv('STAMP') + "." + task
137         if not os.access(stampfile, os.F_OK):
138                 return False
139
140         import stat
141         tasktime = os.stat(stampfile)[stat.ST_MTIME]
142         if deptasks is None:
143                 return True
144
145         for dep in deptasks:
146                 depfile = getenv('STAMP') + "." + dep
147                 if not os.access(depfile, os.F_OK):
148                         return False
149                 deptime = os.stat(depfile)[stat.ST_MTIME]
150                 if deptime > tasktime:
151                         return False
152
153         return True
154
155 #TODO: rmstamp
156
157
158 #######################################################################
159
160 def do_clean():
161         """clear the build and temp directories"""
162
163         note("Executing task 'clean'")
164
165         dir = expand("${TMPDIR}/${CATEGORY}/${PF}")
166         if dir == '//': fatal("wrong DATADIR")
167         note("removing " + dir)
168         os.system('rm -rf ' + dir)
169
170         dir = getenv('STAMP')+'.*'
171         note("removing " + dir)
172         os.system('rm -f '+ dir)
173
174
175 #######################################################################
176
177 def do_mrproper():
178         """clear downloaded sources, build and temp directories"""
179
180         note("Executing task 'mrproper'")
181
182         dir = getenv("DL_DIR")
183         if dir == '/': fatal("wrong DATADIR");
184         debug(2, "removing " + dir)
185         os.system('rm -rf ' + dir)
186         do_clean()
187
188
189 #######################################################################
190
191 def do_fetch():
192         """download needed sources"""
193
194         if stamp_is_current('do_fetch', tasks['do_fetch']['deps']):
195                 return
196
197         mkdirhier(getenv("DL_DIR"))     # create file download directory
198
199         if env.has_key('SRC_URI'):
200                 try:
201                         oe.fetch.init(getenv('SRC_URI').split())
202                 except oe.fetch.NoMethodError:
203                         (type, value, traceback) = sys.exc_info()
204                         fatal("No method: %s" % value)
205                         return
206
207                 try:
208                         oe.fetch.go()
209                 except oe.fetch.MissingParameterError:
210                         (type, value, traceback) = sys.exc_info()
211                         fatal("Missing parameters: %s" % value)
212                         return
213                 except oe.fetch.FetchError:
214                         (type, value, traceback) = sys.exc_info()
215                         fatal("Fetch failed: %s" % value)
216                         return
217                 mkstamp('do_fetch')
218         else:
219                 fatal("No SRC_URI variable, not downloading anything")
220
221
222 #######################################################################
223
224 def do_unpack():
225         """unpack downloaded sources, patch sources if needed"""
226
227         mkdirhier(env["WORKDIR"])       # create workdir
228         
229         exec_task('do_unpack', 'WORKDIR')
230
231
232 #######################################################################
233
234 def do_compile():
235         """compile extracted sources"""
236
237         mkdirhier(env["S"])             # create sourcedir if not yet existing
238         exec_task('do_compile', 'S')
239
240
241 #######################################################################
242
243 def do_stage():
244         """install needed files to compile other packages into staging directory"""
245
246         mkdirhier(getenv("S"))          # create sourcedir if not yet existing
247         mkdirhier(getenv("STAGING_LIBDIR"))
248         mkdirhier(getenv("STAGING_BINDIR"))
249         mkdirhier(getenv("STAGING_DIR") + "/build/include")
250         mkdirhier(getenv("STAGING_DIR") + "/target/include")
251         exec_task('do_stage', 'S')
252                 
253
254 #######################################################################
255
256 def do_install():
257         """install compiled files into image directory"""
258
259         mkdirhier(env["S"])             # create sourcedir if not yet existing
260         exec_task('do_install', 'S')
261                 
262
263 #######################################################################
264
265 def do_build():
266         """fetch, extract, compile, stage and install files"""
267         
268         do_fetch()
269         print_orphan_env()
270         print_missing_env()
271         #emit_env()
272         do_install()
273
274
275
276 #######################################################################
277
278 def do_test():
279         """internal"""
280
281         ret = exec_shell_func(None)
282
283
284
285 #######################################################################
286
287 def do_showenv():
288         """internal
289
290         Just for testing purposes. Displays all environment vars that
291         are NOT automatically generated by oebuild, but are in the config
292         file.
293
294         Might give 'false positives' if some environment variable has not
295         yet been documented in py's envflags{}.
296         """
297
298         keys = env.keys()
299         keys.sort()
300         for s in keys:
301                 #if envflags.has_key(s): continue
302                 
303                 var = env[s]
304                 var = expand(var)
305
306                 if s == s.lower():
307                         print s + '() {\n' + var + '}'
308                 else:
309                         print s+'=' + var
310
311         print_missing_env()
312         print_orphan_env()
313
314
315 #######################################################################
316
317 def usage(errorlevel=0, txt=''):
318         global tasks
319         if txt:
320                 print
321                 print txt
322
323         funcs = []
324         for i in tasks.keys():
325                 if tasks[i].has_key("func"):
326                         funcs.append(tasks[i]["func"])
327         funcs.sort()
328
329         print "\noebuild <command> <somebuildfile.oe>\n"
330         print "Valid commands are:"
331         for s in funcs:
332                 if not globals().has_key(s):
333                         continue
334                 doc = getattr(globals()[s], '__doc__')
335                 if doc.startswith('internal'): continue
336                 print "     %-15s %s" % (s[3:], doc)
337         
338         sys.exit(errorlevel)
339
340 tasks = {
341         "do_clean" : { "func" : "do_clean" },
342         "do_mrproper" : { "func" : "do_mrproper" },
343         "do_test" : { "func" : "do_test" },
344         "do_showenv" : { "func" : "do_showenv" },
345         "do_fetch" : { "func" : "do_fetch",
346                      "deps" : [ ] },
347         "do_unpack" : { "func" : "do_unpack",
348                      "deps" : [ 'do_fetch' ] },
349         "do_compile" : { "func" : "do_compile",
350                       "deps" : [ 'do_unpack' ] },
351         "do_stage" : { "func" : "do_stage",
352                     "deps" : [ 'do_compile' ] },
353         "do_install" : { "func" : "do_install",
354                       "deps" : [ 'do_stage' ] },
355         "do_package" : { "func" : "do_package",
356                       "deps" : [ 'do_install' ] },
357         "do_build" : { "func" : "do_build",
358                     "deps" : [ 'do_package' ] },
359 }
360
361 if len(sys.argv) < 2:
362         usage(1)
363 if sys.argv[1] in ('help', 'usage'):
364         usage(0);
365 if len(sys.argv) < 3:
366         usage(1)
367         fatal("try 'oebuild <command> <somebuildfile.oe>'")
368
369 inherit_os_env(1)
370
371 __oepath_found_it__ = 0
372
373 for s in getenv('OEPATH').split(":"):
374         if os.access(os.path.join(s,'conf/oe.conf'), os.R_OK):
375                 __oepath_found_it__ = 1
376                 try:
377                         read_config(os.path.join(s,'conf/oe.conf'))     # Read configuration
378                 except IOError:
379                         pass
380
381 if __oepath_found_it__ == 0:
382         fatal("error locating conf/oe.conf")
383
384 set_automatic_vars(sys.argv[2])                 # Deduce per-package environment variables
385 try:
386         read_oe(sys.argv[2])            # Read package configuration
387 except IOError:
388         fatal("error accessing build file")
389 set_additional_vars()                           # set rest of vars, e.g. ${A}
390 update_env()                                    # Environment modification, e.g. local overrides
391
392 # check for functions to insert into the task stack
393 for var in env.keys():
394         if not envflags.has_key(var):
395                 continue
396         if not envflags[var].has_key("task"):
397                 continue
398
399         taskvar = var
400         tasks[taskvar] = {}
401         if envflags[var].has_key("func"):
402                 tasks[taskvar]["func"] = envflags[var]["func"]
403         else:
404                 tasks[taskvar]["func"] = "INVALID"
405         if envflags[var].has_key("deps"):
406                 # other tasks that we depend on
407                 tasks[taskvar]["deps"] = envflags[var]["deps"] 
408         if envflags[var].has_key("postdeps"):
409                 # other tasks that depend on this one
410                 postdeps = envflags[var]["postdeps"]
411                 for t in tasks.keys():
412                         for i in postdeps:
413                                 if t == i:
414                                         if not tasks[t].has_key("deps"):
415                                                 tasks[t]["deps"] = []
416                                         tasks[t]["deps"].append(taskvar)
417                                         break
418                         
419         if envflags[var].has_key("python"):
420                 tasks[taskvar]["python"] = "1"
421
422 for s in sys.argv[1].split(','):
423         try:
424                 try:
425                         funcname = tasks["do_" + s]["func"]
426                         func = globals()[funcname]
427                         func()
428                 except KeyError:
429                         exec_task("do_" + s, "S")
430         except FuncFailed:
431                 fatal("task \"%s\" failed" % s)
432
433         note("task \"%s\" completed" % s)
434 #               INVALID()
435 #               usage(1, "'%s' is not valid command" % s)
436
437 sys.exit(0)