build.py 24 KB

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