diff options
Diffstat (limited to 'bitbake/lib/bb/build.py')
-rw-r--r-- | bitbake/lib/bb/build.py | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/bitbake/lib/bb/build.py b/bitbake/lib/bb/build.py new file mode 100644 index 0000000000..65cc851df4 --- /dev/null +++ b/bitbake/lib/bb/build.py | |||
@@ -0,0 +1,711 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # BitBake 'Build' implementation | ||
5 | # | ||
6 | # Core code for function execution and task handling in the | ||
7 | # BitBake build tools. | ||
8 | # | ||
9 | # Copyright (C) 2003, 2004 Chris Larson | ||
10 | # | ||
11 | # Based on Gentoo's portage.py. | ||
12 | # | ||
13 | # This program is free software; you can redistribute it and/or modify | ||
14 | # it under the terms of the GNU General Public License version 2 as | ||
15 | # published by the Free Software Foundation. | ||
16 | # | ||
17 | # This program is distributed in the hope that it will be useful, | ||
18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | # GNU General Public License for more details. | ||
21 | # | ||
22 | # You should have received a copy of the GNU General Public License along | ||
23 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
24 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
25 | # | ||
26 | # Based on functions from the base bb module, Copyright 2003 Holger Schurig | ||
27 | |||
28 | import os | ||
29 | import sys | ||
30 | import logging | ||
31 | import shlex | ||
32 | import glob | ||
33 | import time | ||
34 | import bb | ||
35 | import bb.msg | ||
36 | import bb.process | ||
37 | from contextlib import nested | ||
38 | from bb import event, utils | ||
39 | |||
40 | bblogger = logging.getLogger('BitBake') | ||
41 | logger = logging.getLogger('BitBake.Build') | ||
42 | |||
43 | NULL = open(os.devnull, 'r+') | ||
44 | |||
45 | # When we execute a Python function, we'd like certain things | ||
46 | # in all namespaces, hence we add them to __builtins__. | ||
47 | # If we do not do this and use the exec globals, they will | ||
48 | # not be available to subfunctions. | ||
49 | __builtins__['bb'] = bb | ||
50 | __builtins__['os'] = os | ||
51 | |||
52 | class FuncFailed(Exception): | ||
53 | def __init__(self, name = None, logfile = None): | ||
54 | self.logfile = logfile | ||
55 | self.name = name | ||
56 | if name: | ||
57 | self.msg = 'Function failed: %s' % name | ||
58 | else: | ||
59 | self.msg = "Function failed" | ||
60 | |||
61 | def __str__(self): | ||
62 | if self.logfile and os.path.exists(self.logfile): | ||
63 | msg = ("%s (log file is located at %s)" % | ||
64 | (self.msg, self.logfile)) | ||
65 | else: | ||
66 | msg = self.msg | ||
67 | return msg | ||
68 | |||
69 | class TaskBase(event.Event): | ||
70 | """Base class for task events""" | ||
71 | |||
72 | def __init__(self, t, logfile, d): | ||
73 | self._task = t | ||
74 | self._package = d.getVar("PF", True) | ||
75 | self.taskfile = d.getVar("FILE", True) | ||
76 | self.taskname = self._task | ||
77 | self.logfile = logfile | ||
78 | self.time = time.time() | ||
79 | event.Event.__init__(self) | ||
80 | self._message = "recipe %s: task %s: %s" % (d.getVar("PF", True), t, self.getDisplayName()) | ||
81 | |||
82 | def getTask(self): | ||
83 | return self._task | ||
84 | |||
85 | def setTask(self, task): | ||
86 | self._task = task | ||
87 | |||
88 | def getDisplayName(self): | ||
89 | return bb.event.getName(self)[4:] | ||
90 | |||
91 | task = property(getTask, setTask, None, "task property") | ||
92 | |||
93 | class TaskStarted(TaskBase): | ||
94 | """Task execution started""" | ||
95 | def __init__(self, t, logfile, taskflags, d): | ||
96 | super(TaskStarted, self).__init__(t, logfile, d) | ||
97 | self.taskflags = taskflags | ||
98 | |||
99 | class TaskSucceeded(TaskBase): | ||
100 | """Task execution completed""" | ||
101 | |||
102 | class TaskFailed(TaskBase): | ||
103 | """Task execution failed""" | ||
104 | |||
105 | def __init__(self, task, logfile, metadata, errprinted = False): | ||
106 | self.errprinted = errprinted | ||
107 | super(TaskFailed, self).__init__(task, logfile, metadata) | ||
108 | |||
109 | class TaskFailedSilent(TaskBase): | ||
110 | """Task execution failed (silently)""" | ||
111 | def getDisplayName(self): | ||
112 | # Don't need to tell the user it was silent | ||
113 | return "Failed" | ||
114 | |||
115 | class TaskInvalid(TaskBase): | ||
116 | |||
117 | def __init__(self, task, metadata): | ||
118 | super(TaskInvalid, self).__init__(task, None, metadata) | ||
119 | self._message = "No such task '%s'" % task | ||
120 | |||
121 | |||
122 | class LogTee(object): | ||
123 | def __init__(self, logger, outfile): | ||
124 | self.outfile = outfile | ||
125 | self.logger = logger | ||
126 | self.name = self.outfile.name | ||
127 | |||
128 | def write(self, string): | ||
129 | self.logger.plain(string) | ||
130 | self.outfile.write(string) | ||
131 | |||
132 | def __enter__(self): | ||
133 | self.outfile.__enter__() | ||
134 | return self | ||
135 | |||
136 | def __exit__(self, *excinfo): | ||
137 | self.outfile.__exit__(*excinfo) | ||
138 | |||
139 | def __repr__(self): | ||
140 | return '<LogTee {0}>'.format(self.name) | ||
141 | def flush(self): | ||
142 | self.outfile.flush() | ||
143 | |||
144 | def exec_func(func, d, dirs = None): | ||
145 | """Execute a BB 'function'""" | ||
146 | |||
147 | body = d.getVar(func) | ||
148 | if not body: | ||
149 | if body is None: | ||
150 | logger.warn("Function %s doesn't exist", func) | ||
151 | return | ||
152 | |||
153 | flags = d.getVarFlags(func) | ||
154 | cleandirs = flags.get('cleandirs') | ||
155 | if cleandirs: | ||
156 | for cdir in d.expand(cleandirs).split(): | ||
157 | bb.utils.remove(cdir, True) | ||
158 | bb.utils.mkdirhier(cdir) | ||
159 | |||
160 | if dirs is None: | ||
161 | dirs = flags.get('dirs') | ||
162 | if dirs: | ||
163 | dirs = d.expand(dirs).split() | ||
164 | |||
165 | if dirs: | ||
166 | for adir in dirs: | ||
167 | bb.utils.mkdirhier(adir) | ||
168 | adir = dirs[-1] | ||
169 | else: | ||
170 | adir = d.getVar('B', True) | ||
171 | bb.utils.mkdirhier(adir) | ||
172 | |||
173 | ispython = flags.get('python') | ||
174 | |||
175 | lockflag = flags.get('lockfiles') | ||
176 | if lockflag: | ||
177 | lockfiles = [f for f in d.expand(lockflag).split()] | ||
178 | else: | ||
179 | lockfiles = None | ||
180 | |||
181 | tempdir = d.getVar('T', True) | ||
182 | |||
183 | # or func allows items to be executed outside of the normal | ||
184 | # task set, such as buildhistory | ||
185 | task = d.getVar('BB_RUNTASK', True) or func | ||
186 | if task == func: | ||
187 | taskfunc = task | ||
188 | else: | ||
189 | taskfunc = "%s.%s" % (task, func) | ||
190 | |||
191 | runfmt = d.getVar('BB_RUNFMT', True) or "run.{func}.{pid}" | ||
192 | runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid()) | ||
193 | runfile = os.path.join(tempdir, runfn) | ||
194 | bb.utils.mkdirhier(os.path.dirname(runfile)) | ||
195 | |||
196 | # Setup the courtesy link to the runfn, only for tasks | ||
197 | # we create the link 'just' before the run script is created | ||
198 | # if we create it after, and if the run script fails, then the | ||
199 | # link won't be created as an exception would be fired. | ||
200 | if task == func: | ||
201 | runlink = os.path.join(tempdir, 'run.{0}'.format(task)) | ||
202 | if runlink: | ||
203 | bb.utils.remove(runlink) | ||
204 | |||
205 | try: | ||
206 | os.symlink(runfn, runlink) | ||
207 | except OSError: | ||
208 | pass | ||
209 | |||
210 | with bb.utils.fileslocked(lockfiles): | ||
211 | if ispython: | ||
212 | exec_func_python(func, d, runfile, cwd=adir) | ||
213 | else: | ||
214 | exec_func_shell(func, d, runfile, cwd=adir) | ||
215 | |||
216 | _functionfmt = """ | ||
217 | def {function}(d): | ||
218 | {body} | ||
219 | |||
220 | {function}(d) | ||
221 | """ | ||
222 | logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s") | ||
223 | def exec_func_python(func, d, runfile, cwd=None): | ||
224 | """Execute a python BB 'function'""" | ||
225 | |||
226 | bbfile = d.getVar('FILE', True) | ||
227 | code = _functionfmt.format(function=func, body=d.getVar(func, True)) | ||
228 | bb.utils.mkdirhier(os.path.dirname(runfile)) | ||
229 | with open(runfile, 'w') as script: | ||
230 | bb.data.emit_func_python(func, script, d) | ||
231 | |||
232 | if cwd: | ||
233 | try: | ||
234 | olddir = os.getcwd() | ||
235 | except OSError: | ||
236 | olddir = None | ||
237 | os.chdir(cwd) | ||
238 | |||
239 | bb.debug(2, "Executing python function %s" % func) | ||
240 | |||
241 | try: | ||
242 | comp = utils.better_compile(code, func, bbfile) | ||
243 | utils.better_exec(comp, {"d": d}, code, bbfile) | ||
244 | except (bb.parse.SkipRecipe, bb.build.FuncFailed): | ||
245 | raise | ||
246 | except: | ||
247 | raise FuncFailed(func, None) | ||
248 | finally: | ||
249 | bb.debug(2, "Python function %s finished" % func) | ||
250 | |||
251 | if cwd and olddir: | ||
252 | try: | ||
253 | os.chdir(olddir) | ||
254 | except OSError: | ||
255 | pass | ||
256 | |||
257 | def shell_trap_code(): | ||
258 | return '''#!/bin/sh\n | ||
259 | # Emit a useful diagnostic if something fails: | ||
260 | bb_exit_handler() { | ||
261 | ret=$? | ||
262 | case $ret in | ||
263 | 0) ;; | ||
264 | *) case $BASH_VERSION in | ||
265 | "") echo "WARNING: exit code $ret from a shell command.";; | ||
266 | *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from | ||
267 | \"$BASH_COMMAND\"";; | ||
268 | esac | ||
269 | exit $ret | ||
270 | esac | ||
271 | } | ||
272 | trap 'bb_exit_handler' 0 | ||
273 | set -e | ||
274 | ''' | ||
275 | |||
276 | def exec_func_shell(func, d, runfile, cwd=None): | ||
277 | """Execute a shell function from the metadata | ||
278 | |||
279 | Note on directory behavior. The 'dirs' varflag should contain a list | ||
280 | of the directories you need created prior to execution. The last | ||
281 | item in the list is where we will chdir/cd to. | ||
282 | """ | ||
283 | |||
284 | # Don't let the emitted shell script override PWD | ||
285 | d.delVarFlag('PWD', 'export') | ||
286 | |||
287 | with open(runfile, 'w') as script: | ||
288 | script.write(shell_trap_code()) | ||
289 | |||
290 | bb.data.emit_func(func, script, d) | ||
291 | |||
292 | if bb.msg.loggerVerboseLogs: | ||
293 | script.write("set -x\n") | ||
294 | if cwd: | ||
295 | script.write("cd '%s'\n" % cwd) | ||
296 | script.write("%s\n" % func) | ||
297 | script.write(''' | ||
298 | # cleanup | ||
299 | ret=$? | ||
300 | trap '' 0 | ||
301 | exit $? | ||
302 | ''') | ||
303 | |||
304 | os.chmod(runfile, 0775) | ||
305 | |||
306 | cmd = runfile | ||
307 | if d.getVarFlag(func, 'fakeroot'): | ||
308 | fakerootcmd = d.getVar('FAKEROOT', True) | ||
309 | if fakerootcmd: | ||
310 | cmd = [fakerootcmd, runfile] | ||
311 | |||
312 | if bb.msg.loggerDefaultVerbose: | ||
313 | logfile = LogTee(logger, sys.stdout) | ||
314 | else: | ||
315 | logfile = sys.stdout | ||
316 | |||
317 | bb.debug(2, "Executing shell function %s" % func) | ||
318 | |||
319 | try: | ||
320 | with open(os.devnull, 'r+') as stdin: | ||
321 | bb.process.run(cmd, shell=False, stdin=stdin, log=logfile) | ||
322 | except bb.process.CmdError: | ||
323 | logfn = d.getVar('BB_LOGFILE', True) | ||
324 | raise FuncFailed(func, logfn) | ||
325 | |||
326 | bb.debug(2, "Shell function %s finished" % func) | ||
327 | |||
328 | def _task_data(fn, task, d): | ||
329 | localdata = bb.data.createCopy(d) | ||
330 | localdata.setVar('BB_FILENAME', fn) | ||
331 | localdata.setVar('BB_CURRENTTASK', task[3:]) | ||
332 | localdata.setVar('OVERRIDES', 'task-%s:%s' % | ||
333 | (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False))) | ||
334 | localdata.finalize() | ||
335 | bb.data.expandKeys(localdata) | ||
336 | return localdata | ||
337 | |||
338 | def _exec_task(fn, task, d, quieterr): | ||
339 | """Execute a BB 'task' | ||
340 | |||
341 | Execution of a task involves a bit more setup than executing a function, | ||
342 | running it with its own local metadata, and with some useful variables set. | ||
343 | """ | ||
344 | if not d.getVarFlag(task, 'task'): | ||
345 | event.fire(TaskInvalid(task, d), d) | ||
346 | logger.error("No such task: %s" % task) | ||
347 | return 1 | ||
348 | |||
349 | logger.debug(1, "Executing task %s", task) | ||
350 | |||
351 | localdata = _task_data(fn, task, d) | ||
352 | tempdir = localdata.getVar('T', True) | ||
353 | if not tempdir: | ||
354 | bb.fatal("T variable not set, unable to build") | ||
355 | |||
356 | # Change nice level if we're asked to | ||
357 | nice = localdata.getVar("BB_TASK_NICE_LEVEL", True) | ||
358 | if nice: | ||
359 | curnice = os.nice(0) | ||
360 | nice = int(nice) - curnice | ||
361 | newnice = os.nice(nice) | ||
362 | logger.debug(1, "Renice to %s " % newnice) | ||
363 | |||
364 | bb.utils.mkdirhier(tempdir) | ||
365 | |||
366 | # Determine the logfile to generate | ||
367 | logfmt = localdata.getVar('BB_LOGFMT', True) or 'log.{task}.{pid}' | ||
368 | logbase = logfmt.format(task=task, pid=os.getpid()) | ||
369 | |||
370 | # Document the order of the tasks... | ||
371 | logorder = os.path.join(tempdir, 'log.task_order') | ||
372 | try: | ||
373 | with open(logorder, 'a') as logorderfile: | ||
374 | logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase)) | ||
375 | except OSError: | ||
376 | logger.exception("Opening log file '%s'", logorder) | ||
377 | pass | ||
378 | |||
379 | # Setup the courtesy link to the logfn | ||
380 | loglink = os.path.join(tempdir, 'log.{0}'.format(task)) | ||
381 | logfn = os.path.join(tempdir, logbase) | ||
382 | if loglink: | ||
383 | bb.utils.remove(loglink) | ||
384 | |||
385 | try: | ||
386 | os.symlink(logbase, loglink) | ||
387 | except OSError: | ||
388 | pass | ||
389 | |||
390 | prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True) | ||
391 | postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True) | ||
392 | |||
393 | class ErrorCheckHandler(logging.Handler): | ||
394 | def __init__(self): | ||
395 | self.triggered = False | ||
396 | logging.Handler.__init__(self, logging.ERROR) | ||
397 | def emit(self, record): | ||
398 | self.triggered = True | ||
399 | |||
400 | # Handle logfiles | ||
401 | si = open('/dev/null', 'r') | ||
402 | try: | ||
403 | bb.utils.mkdirhier(os.path.dirname(logfn)) | ||
404 | logfile = open(logfn, 'w') | ||
405 | except OSError: | ||
406 | logger.exception("Opening log file '%s'", logfn) | ||
407 | pass | ||
408 | |||
409 | # Dup the existing fds so we dont lose them | ||
410 | osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()] | ||
411 | oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()] | ||
412 | ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()] | ||
413 | |||
414 | # Replace those fds with our own | ||
415 | os.dup2(si.fileno(), osi[1]) | ||
416 | os.dup2(logfile.fileno(), oso[1]) | ||
417 | os.dup2(logfile.fileno(), ose[1]) | ||
418 | |||
419 | # Ensure Python logging goes to the logfile | ||
420 | handler = logging.StreamHandler(logfile) | ||
421 | handler.setFormatter(logformatter) | ||
422 | # Always enable full debug output into task logfiles | ||
423 | handler.setLevel(logging.DEBUG - 2) | ||
424 | bblogger.addHandler(handler) | ||
425 | |||
426 | errchk = ErrorCheckHandler() | ||
427 | bblogger.addHandler(errchk) | ||
428 | |||
429 | localdata.setVar('BB_LOGFILE', logfn) | ||
430 | localdata.setVar('BB_RUNTASK', task) | ||
431 | |||
432 | flags = localdata.getVarFlags(task) | ||
433 | |||
434 | event.fire(TaskStarted(task, logfn, flags, localdata), localdata) | ||
435 | try: | ||
436 | for func in (prefuncs or '').split(): | ||
437 | exec_func(func, localdata) | ||
438 | exec_func(task, localdata) | ||
439 | for func in (postfuncs or '').split(): | ||
440 | exec_func(func, localdata) | ||
441 | except FuncFailed as exc: | ||
442 | if quieterr: | ||
443 | event.fire(TaskFailedSilent(task, logfn, localdata), localdata) | ||
444 | else: | ||
445 | errprinted = errchk.triggered | ||
446 | logger.error(str(exc)) | ||
447 | event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata) | ||
448 | return 1 | ||
449 | finally: | ||
450 | sys.stdout.flush() | ||
451 | sys.stderr.flush() | ||
452 | |||
453 | bblogger.removeHandler(handler) | ||
454 | |||
455 | # Restore the backup fds | ||
456 | os.dup2(osi[0], osi[1]) | ||
457 | os.dup2(oso[0], oso[1]) | ||
458 | os.dup2(ose[0], ose[1]) | ||
459 | |||
460 | # Close the backup fds | ||
461 | os.close(osi[0]) | ||
462 | os.close(oso[0]) | ||
463 | os.close(ose[0]) | ||
464 | si.close() | ||
465 | |||
466 | logfile.close() | ||
467 | if os.path.exists(logfn) and os.path.getsize(logfn) == 0: | ||
468 | logger.debug(2, "Zero size logfn %s, removing", logfn) | ||
469 | bb.utils.remove(logfn) | ||
470 | bb.utils.remove(loglink) | ||
471 | event.fire(TaskSucceeded(task, logfn, localdata), localdata) | ||
472 | |||
473 | if not localdata.getVarFlag(task, 'nostamp') and not localdata.getVarFlag(task, 'selfstamp'): | ||
474 | make_stamp(task, localdata) | ||
475 | |||
476 | return 0 | ||
477 | |||
478 | def exec_task(fn, task, d, profile = False): | ||
479 | try: | ||
480 | quieterr = False | ||
481 | if d.getVarFlag(task, "quieterrors") is not None: | ||
482 | quieterr = True | ||
483 | |||
484 | if profile: | ||
485 | profname = "profile-%s.log" % (d.getVar("PN", True) + "-" + task) | ||
486 | try: | ||
487 | import cProfile as profile | ||
488 | except: | ||
489 | import profile | ||
490 | prof = profile.Profile() | ||
491 | ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr) | ||
492 | prof.dump_stats(profname) | ||
493 | bb.utils.process_profilelog(profname) | ||
494 | |||
495 | return ret | ||
496 | else: | ||
497 | return _exec_task(fn, task, d, quieterr) | ||
498 | |||
499 | except Exception: | ||
500 | from traceback import format_exc | ||
501 | if not quieterr: | ||
502 | logger.error("Build of %s failed" % (task)) | ||
503 | logger.error(format_exc()) | ||
504 | failedevent = TaskFailed(task, None, d, True) | ||
505 | event.fire(failedevent, d) | ||
506 | return 1 | ||
507 | |||
508 | def stamp_internal(taskname, d, file_name, baseonly=False): | ||
509 | """ | ||
510 | Internal stamp helper function | ||
511 | Makes sure the stamp directory exists | ||
512 | Returns the stamp path+filename | ||
513 | |||
514 | In the bitbake core, d can be a CacheData and file_name will be set. | ||
515 | When called in task context, d will be a data store, file_name will not be set | ||
516 | """ | ||
517 | taskflagname = taskname | ||
518 | if taskname.endswith("_setscene") and taskname != "do_setscene": | ||
519 | taskflagname = taskname.replace("_setscene", "") | ||
520 | |||
521 | if file_name: | ||
522 | stamp = d.stamp_base[file_name].get(taskflagname) or d.stamp[file_name] | ||
523 | extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or "" | ||
524 | else: | ||
525 | stamp = d.getVarFlag(taskflagname, 'stamp-base', True) or d.getVar('STAMP', True) | ||
526 | file_name = d.getVar('BB_FILENAME', True) | ||
527 | extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or "" | ||
528 | |||
529 | if baseonly: | ||
530 | return stamp | ||
531 | |||
532 | if not stamp: | ||
533 | return | ||
534 | |||
535 | stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo) | ||
536 | |||
537 | stampdir = os.path.dirname(stamp) | ||
538 | if bb.parse.cached_mtime_noerror(stampdir) == 0: | ||
539 | bb.utils.mkdirhier(stampdir) | ||
540 | |||
541 | return stamp | ||
542 | |||
543 | def stamp_cleanmask_internal(taskname, d, file_name): | ||
544 | """ | ||
545 | Internal stamp helper function to generate stamp cleaning mask | ||
546 | Returns the stamp path+filename | ||
547 | |||
548 | In the bitbake core, d can be a CacheData and file_name will be set. | ||
549 | When called in task context, d will be a data store, file_name will not be set | ||
550 | """ | ||
551 | taskflagname = taskname | ||
552 | if taskname.endswith("_setscene") and taskname != "do_setscene": | ||
553 | taskflagname = taskname.replace("_setscene", "") | ||
554 | |||
555 | if file_name: | ||
556 | stamp = d.stamp_base_clean[file_name].get(taskflagname) or d.stampclean[file_name] | ||
557 | extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or "" | ||
558 | else: | ||
559 | stamp = d.getVarFlag(taskflagname, 'stamp-base-clean', True) or d.getVar('STAMPCLEAN', True) | ||
560 | file_name = d.getVar('BB_FILENAME', True) | ||
561 | extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info', True) or "" | ||
562 | |||
563 | if not stamp: | ||
564 | return [] | ||
565 | |||
566 | cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo) | ||
567 | |||
568 | return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")] | ||
569 | |||
570 | def make_stamp(task, d, file_name = None): | ||
571 | """ | ||
572 | Creates/updates a stamp for a given task | ||
573 | (d can be a data dict or dataCache) | ||
574 | """ | ||
575 | cleanmask = stamp_cleanmask_internal(task, d, file_name) | ||
576 | for mask in cleanmask: | ||
577 | for name in glob.glob(mask): | ||
578 | # Preserve sigdata files in the stamps directory | ||
579 | if "sigdata" in name: | ||
580 | continue | ||
581 | # Preserve taint files in the stamps directory | ||
582 | if name.endswith('.taint'): | ||
583 | continue | ||
584 | os.unlink(name) | ||
585 | |||
586 | stamp = stamp_internal(task, d, file_name) | ||
587 | # Remove the file and recreate to force timestamp | ||
588 | # change on broken NFS filesystems | ||
589 | if stamp: | ||
590 | bb.utils.remove(stamp) | ||
591 | open(stamp, "w").close() | ||
592 | |||
593 | # If we're in task context, write out a signature file for each task | ||
594 | # as it completes | ||
595 | if not task.endswith("_setscene") and task != "do_setscene" and not file_name: | ||
596 | stampbase = stamp_internal(task, d, None, True) | ||
597 | file_name = d.getVar('BB_FILENAME', True) | ||
598 | bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True) | ||
599 | |||
600 | def del_stamp(task, d, file_name = None): | ||
601 | """ | ||
602 | Removes a stamp for a given task | ||
603 | (d can be a data dict or dataCache) | ||
604 | """ | ||
605 | stamp = stamp_internal(task, d, file_name) | ||
606 | bb.utils.remove(stamp) | ||
607 | |||
608 | def write_taint(task, d, file_name = None): | ||
609 | """ | ||
610 | Creates a "taint" file which will force the specified task and its | ||
611 | dependents to be re-run the next time by influencing the value of its | ||
612 | taskhash. | ||
613 | (d can be a data dict or dataCache) | ||
614 | """ | ||
615 | import uuid | ||
616 | if file_name: | ||
617 | taintfn = d.stamp[file_name] + '.' + task + '.taint' | ||
618 | else: | ||
619 | taintfn = d.getVar('STAMP', True) + '.' + task + '.taint' | ||
620 | bb.utils.mkdirhier(os.path.dirname(taintfn)) | ||
621 | # The specific content of the taint file is not really important, | ||
622 | # we just need it to be random, so a random UUID is used | ||
623 | with open(taintfn, 'w') as taintf: | ||
624 | taintf.write(str(uuid.uuid4())) | ||
625 | |||
626 | def stampfile(taskname, d, file_name = None): | ||
627 | """ | ||
628 | Return the stamp for a given task | ||
629 | (d can be a data dict or dataCache) | ||
630 | """ | ||
631 | return stamp_internal(taskname, d, file_name) | ||
632 | |||
633 | def add_tasks(tasklist, deltasklist, d): | ||
634 | task_deps = d.getVar('_task_deps') | ||
635 | if not task_deps: | ||
636 | task_deps = {} | ||
637 | if not 'tasks' in task_deps: | ||
638 | task_deps['tasks'] = [] | ||
639 | if not 'parents' in task_deps: | ||
640 | task_deps['parents'] = {} | ||
641 | |||
642 | for task in tasklist: | ||
643 | task = d.expand(task) | ||
644 | |||
645 | if task in deltasklist: | ||
646 | continue | ||
647 | |||
648 | d.setVarFlag(task, 'task', 1) | ||
649 | |||
650 | if not task in task_deps['tasks']: | ||
651 | task_deps['tasks'].append(task) | ||
652 | |||
653 | flags = d.getVarFlags(task) | ||
654 | def getTask(name): | ||
655 | if not name in task_deps: | ||
656 | task_deps[name] = {} | ||
657 | if name in flags: | ||
658 | deptask = d.expand(flags[name]) | ||
659 | task_deps[name][task] = deptask | ||
660 | getTask('depends') | ||
661 | getTask('rdepends') | ||
662 | getTask('deptask') | ||
663 | getTask('rdeptask') | ||
664 | getTask('recrdeptask') | ||
665 | getTask('recideptask') | ||
666 | getTask('nostamp') | ||
667 | getTask('fakeroot') | ||
668 | getTask('noexec') | ||
669 | getTask('umask') | ||
670 | task_deps['parents'][task] = [] | ||
671 | if 'deps' in flags: | ||
672 | for dep in flags['deps']: | ||
673 | dep = d.expand(dep) | ||
674 | task_deps['parents'][task].append(dep) | ||
675 | |||
676 | # don't assume holding a reference | ||
677 | d.setVar('_task_deps', task_deps) | ||
678 | |||
679 | def addtask(task, before, after, d): | ||
680 | if task[:3] != "do_": | ||
681 | task = "do_" + task | ||
682 | |||
683 | d.setVarFlag(task, "task", 1) | ||
684 | bbtasks = d.getVar('__BBTASKS') or [] | ||
685 | if not task in bbtasks: | ||
686 | bbtasks.append(task) | ||
687 | d.setVar('__BBTASKS', bbtasks) | ||
688 | |||
689 | existing = d.getVarFlag(task, "deps") or [] | ||
690 | if after is not None: | ||
691 | # set up deps for function | ||
692 | for entry in after.split(): | ||
693 | if entry not in existing: | ||
694 | existing.append(entry) | ||
695 | d.setVarFlag(task, "deps", existing) | ||
696 | if before is not None: | ||
697 | # set up things that depend on this func | ||
698 | for entry in before.split(): | ||
699 | existing = d.getVarFlag(entry, "deps") or [] | ||
700 | if task not in existing: | ||
701 | d.setVarFlag(entry, "deps", [task] + existing) | ||
702 | |||
703 | def deltask(task, d): | ||
704 | if task[:3] != "do_": | ||
705 | task = "do_" + task | ||
706 | |||
707 | bbtasks = d.getVar('__BBDELTASKS') or [] | ||
708 | if not task in bbtasks: | ||
709 | bbtasks.append(task) | ||
710 | d.setVar('__BBDELTASKS', bbtasks) | ||
711 | |||