build.py 24 KB

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