build.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  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. import bb.progress
  38. from bb import data, 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")
  83. self.taskfile = d.getVar("FILE")
  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"), 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 TaskProgress(event.Event):
  118. """
  119. Task made some progress that could be reported to the user, usually in
  120. the form of a progress bar or similar.
  121. NOTE: this class does not inherit from TaskBase since it doesn't need
  122. to - it's fired within the task context itself, so we don't have any of
  123. the context information that you do in the case of the other events.
  124. The event PID can be used to determine which task it came from.
  125. The progress value is normally 0-100, but can also be negative
  126. indicating that progress has been made but we aren't able to determine
  127. how much.
  128. The rate is optional, this is simply an extra string to display to the
  129. user if specified.
  130. """
  131. def __init__(self, progress, rate=None):
  132. self.progress = progress
  133. self.rate = rate
  134. event.Event.__init__(self)
  135. class LogTee(object):
  136. def __init__(self, logger, outfile):
  137. self.outfile = outfile
  138. self.logger = logger
  139. self.name = self.outfile.name
  140. def write(self, string):
  141. self.logger.plain(string)
  142. self.outfile.write(string)
  143. def __enter__(self):
  144. self.outfile.__enter__()
  145. return self
  146. def __exit__(self, *excinfo):
  147. self.outfile.__exit__(*excinfo)
  148. def __repr__(self):
  149. return '<LogTee {0}>'.format(self.name)
  150. def flush(self):
  151. self.outfile.flush()
  152. #
  153. # pythonexception allows the python exceptions generated to be raised
  154. # as the real exceptions (not FuncFailed) and without a backtrace at the
  155. # origin of the failure.
  156. #
  157. def exec_func(func, d, dirs = None, pythonexception=False):
  158. """Execute a BB 'function'"""
  159. try:
  160. oldcwd = os.getcwd()
  161. except:
  162. oldcwd = None
  163. flags = d.getVarFlags(func)
  164. cleandirs = flags.get('cleandirs')
  165. if cleandirs:
  166. for cdir in d.expand(cleandirs).split():
  167. bb.utils.remove(cdir, True)
  168. bb.utils.mkdirhier(cdir)
  169. if dirs is None:
  170. dirs = flags.get('dirs')
  171. if dirs:
  172. dirs = d.expand(dirs).split()
  173. if dirs:
  174. for adir in dirs:
  175. bb.utils.mkdirhier(adir)
  176. adir = dirs[-1]
  177. else:
  178. adir = None
  179. body = d.getVar(func, False)
  180. if not body:
  181. if body is None:
  182. logger.warning("Function %s doesn't exist", func)
  183. return
  184. ispython = flags.get('python')
  185. lockflag = flags.get('lockfiles')
  186. if lockflag:
  187. lockfiles = [f for f in d.expand(lockflag).split()]
  188. else:
  189. lockfiles = None
  190. tempdir = d.getVar('T')
  191. # or func allows items to be executed outside of the normal
  192. # task set, such as buildhistory
  193. task = d.getVar('BB_RUNTASK') or func
  194. if task == func:
  195. taskfunc = task
  196. else:
  197. taskfunc = "%s.%s" % (task, func)
  198. runfmt = d.getVar('BB_RUNFMT') or "run.{func}.{pid}"
  199. runfn = runfmt.format(taskfunc=taskfunc, task=task, func=func, pid=os.getpid())
  200. runfile = os.path.join(tempdir, runfn)
  201. bb.utils.mkdirhier(os.path.dirname(runfile))
  202. # Setup the courtesy link to the runfn, only for tasks
  203. # we create the link 'just' before the run script is created
  204. # if we create it after, and if the run script fails, then the
  205. # link won't be created as an exception would be fired.
  206. if task == func:
  207. runlink = os.path.join(tempdir, 'run.{0}'.format(task))
  208. if runlink:
  209. bb.utils.remove(runlink)
  210. try:
  211. os.symlink(runfn, runlink)
  212. except OSError:
  213. pass
  214. with bb.utils.fileslocked(lockfiles):
  215. if ispython:
  216. exec_func_python(func, d, runfile, cwd=adir, pythonexception=pythonexception)
  217. else:
  218. exec_func_shell(func, d, runfile, cwd=adir)
  219. try:
  220. curcwd = os.getcwd()
  221. except:
  222. curcwd = None
  223. if oldcwd and curcwd != oldcwd:
  224. try:
  225. bb.warn("Task %s changed cwd to %s" % (func, curcwd))
  226. os.chdir(oldcwd)
  227. except:
  228. pass
  229. _functionfmt = """
  230. {function}(d)
  231. """
  232. logformatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
  233. def exec_func_python(func, d, runfile, cwd=None, pythonexception=False):
  234. """Execute a python BB 'function'"""
  235. code = _functionfmt.format(function=func)
  236. bb.utils.mkdirhier(os.path.dirname(runfile))
  237. with open(runfile, 'w') as script:
  238. bb.data.emit_func_python(func, script, d)
  239. if cwd:
  240. try:
  241. olddir = os.getcwd()
  242. except OSError as e:
  243. bb.warn("%s: Cannot get cwd: %s" % (func, e))
  244. olddir = None
  245. os.chdir(cwd)
  246. bb.debug(2, "Executing python function %s" % func)
  247. try:
  248. text = "def %s(d):\n%s" % (func, d.getVar(func, False))
  249. fn = d.getVarFlag(func, "filename", False)
  250. lineno = int(d.getVarFlag(func, "lineno", False))
  251. bb.methodpool.insert_method(func, text, fn, lineno - 1)
  252. comp = utils.better_compile(code, func, "exec_python_func() autogenerated")
  253. utils.better_exec(comp, {"d": d}, code, "exec_python_func() autogenerated", pythonexception=pythonexception)
  254. except (bb.parse.SkipRecipe, bb.build.FuncFailed):
  255. raise
  256. except:
  257. if pythonexception:
  258. raise
  259. raise FuncFailed(func, None)
  260. finally:
  261. bb.debug(2, "Python function %s finished" % func)
  262. if cwd and olddir:
  263. try:
  264. os.chdir(olddir)
  265. except OSError as e:
  266. bb.warn("%s: Cannot restore cwd %s: %s" % (func, olddir, e))
  267. def shell_trap_code():
  268. return '''#!/bin/sh\n
  269. # Emit a useful diagnostic if something fails:
  270. bb_exit_handler() {
  271. ret=$?
  272. case $ret in
  273. 0) ;;
  274. *) case $BASH_VERSION in
  275. "") echo "WARNING: exit code $ret from a shell command.";;
  276. *) echo "WARNING: ${BASH_SOURCE[0]}:${BASH_LINENO[0]} exit $ret from '$BASH_COMMAND'";;
  277. esac
  278. exit $ret
  279. esac
  280. }
  281. trap 'bb_exit_handler' 0
  282. set -e
  283. '''
  284. def exec_func_shell(func, d, runfile, cwd=None):
  285. """Execute a shell function from the metadata
  286. Note on directory behavior. The 'dirs' varflag should contain a list
  287. of the directories you need created prior to execution. The last
  288. item in the list is where we will chdir/cd to.
  289. """
  290. # Don't let the emitted shell script override PWD
  291. d.delVarFlag('PWD', 'export')
  292. with open(runfile, 'w') as script:
  293. script.write(shell_trap_code())
  294. bb.data.emit_func(func, script, d)
  295. if bb.msg.loggerVerboseLogs:
  296. script.write("set -x\n")
  297. if cwd:
  298. script.write("cd '%s'\n" % cwd)
  299. script.write("%s\n" % func)
  300. script.write('''
  301. # cleanup
  302. ret=$?
  303. trap '' 0
  304. exit $ret
  305. ''')
  306. os.chmod(runfile, 0o775)
  307. cmd = runfile
  308. if d.getVarFlag(func, 'fakeroot', False):
  309. fakerootcmd = d.getVar('FAKEROOT')
  310. if fakerootcmd:
  311. cmd = [fakerootcmd, runfile]
  312. if bb.msg.loggerDefaultVerbose:
  313. logfile = LogTee(logger, sys.stdout)
  314. else:
  315. logfile = sys.stdout
  316. progress = d.getVarFlag(func, 'progress')
  317. if progress:
  318. if progress == 'percent':
  319. # Use default regex
  320. logfile = bb.progress.BasicProgressHandler(d, outfile=logfile)
  321. elif progress.startswith('percent:'):
  322. # Use specified regex
  323. logfile = bb.progress.BasicProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
  324. elif progress.startswith('outof:'):
  325. # Use specified regex
  326. logfile = bb.progress.OutOfProgressHandler(d, regex=progress.split(':', 1)[1], outfile=logfile)
  327. else:
  328. bb.warn('%s: invalid task progress varflag value "%s", ignoring' % (func, progress))
  329. fifobuffer = bytearray()
  330. def readfifo(data):
  331. nonlocal fifobuffer
  332. fifobuffer.extend(data)
  333. while fifobuffer:
  334. message, token, nextmsg = fifobuffer.partition(b"\00")
  335. if token:
  336. splitval = message.split(b' ', 1)
  337. cmd = splitval[0].decode("utf-8")
  338. if len(splitval) > 1:
  339. value = splitval[1].decode("utf-8")
  340. else:
  341. value = ''
  342. if cmd == 'bbplain':
  343. bb.plain(value)
  344. elif cmd == 'bbnote':
  345. bb.note(value)
  346. elif cmd == 'bbwarn':
  347. bb.warn(value)
  348. elif cmd == 'bberror':
  349. bb.error(value)
  350. elif cmd == 'bbfatal':
  351. # The caller will call exit themselves, so bb.error() is
  352. # what we want here rather than bb.fatal()
  353. bb.error(value)
  354. elif cmd == 'bbfatal_log':
  355. bb.error(value, forcelog=True)
  356. elif cmd == 'bbdebug':
  357. splitval = value.split(' ', 1)
  358. level = int(splitval[0])
  359. value = splitval[1]
  360. bb.debug(level, value)
  361. else:
  362. bb.warn("Unrecognised command '%s' on FIFO" % cmd)
  363. fifobuffer = nextmsg
  364. else:
  365. break
  366. tempdir = d.getVar('T')
  367. fifopath = os.path.join(tempdir, 'fifo.%s' % os.getpid())
  368. if os.path.exists(fifopath):
  369. os.unlink(fifopath)
  370. os.mkfifo(fifopath)
  371. with open(fifopath, 'r+b', buffering=0) as fifo:
  372. try:
  373. bb.debug(2, "Executing shell function %s" % func)
  374. try:
  375. with open(os.devnull, 'r+') as stdin:
  376. bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
  377. except bb.process.CmdError:
  378. logfn = d.getVar('BB_LOGFILE')
  379. raise FuncFailed(func, logfn)
  380. finally:
  381. os.unlink(fifopath)
  382. bb.debug(2, "Shell function %s finished" % func)
  383. def _task_data(fn, task, d):
  384. localdata = bb.data.createCopy(d)
  385. localdata.setVar('BB_FILENAME', fn)
  386. localdata.setVar('BB_CURRENTTASK', task[3:])
  387. localdata.setVar('OVERRIDES', 'task-%s:%s' %
  388. (task[3:].replace('_', '-'), d.getVar('OVERRIDES', False)))
  389. localdata.finalize()
  390. bb.data.expandKeys(localdata)
  391. return localdata
  392. def _exec_task(fn, task, d, quieterr):
  393. """Execute a BB 'task'
  394. Execution of a task involves a bit more setup than executing a function,
  395. running it with its own local metadata, and with some useful variables set.
  396. """
  397. if not d.getVarFlag(task, 'task', False):
  398. event.fire(TaskInvalid(task, d), d)
  399. logger.error("No such task: %s" % task)
  400. return 1
  401. logger.debug(1, "Executing task %s", task)
  402. localdata = _task_data(fn, task, d)
  403. tempdir = localdata.getVar('T')
  404. if not tempdir:
  405. bb.fatal("T variable not set, unable to build")
  406. # Change nice level if we're asked to
  407. nice = localdata.getVar("BB_TASK_NICE_LEVEL")
  408. if nice:
  409. curnice = os.nice(0)
  410. nice = int(nice) - curnice
  411. newnice = os.nice(nice)
  412. logger.debug(1, "Renice to %s " % newnice)
  413. ionice = localdata.getVar("BB_TASK_IONICE_LEVEL")
  414. if ionice:
  415. try:
  416. cls, prio = ionice.split(".", 1)
  417. bb.utils.ioprio_set(os.getpid(), int(cls), int(prio))
  418. except:
  419. bb.warn("Invalid ionice level %s" % ionice)
  420. bb.utils.mkdirhier(tempdir)
  421. # Determine the logfile to generate
  422. logfmt = localdata.getVar('BB_LOGFMT') or 'log.{task}.{pid}'
  423. logbase = logfmt.format(task=task, pid=os.getpid())
  424. # Document the order of the tasks...
  425. logorder = os.path.join(tempdir, 'log.task_order')
  426. try:
  427. with open(logorder, 'a') as logorderfile:
  428. logorderfile.write('{0} ({1}): {2}\n'.format(task, os.getpid(), logbase))
  429. except OSError:
  430. logger.exception("Opening log file '%s'", logorder)
  431. pass
  432. # Setup the courtesy link to the logfn
  433. loglink = os.path.join(tempdir, 'log.{0}'.format(task))
  434. logfn = os.path.join(tempdir, logbase)
  435. if loglink:
  436. bb.utils.remove(loglink)
  437. try:
  438. os.symlink(logbase, loglink)
  439. except OSError:
  440. pass
  441. prefuncs = localdata.getVarFlag(task, 'prefuncs', expand=True)
  442. postfuncs = localdata.getVarFlag(task, 'postfuncs', expand=True)
  443. class ErrorCheckHandler(logging.Handler):
  444. def __init__(self):
  445. self.triggered = False
  446. logging.Handler.__init__(self, logging.ERROR)
  447. def emit(self, record):
  448. if getattr(record, 'forcelog', False):
  449. self.triggered = False
  450. else:
  451. self.triggered = True
  452. # Handle logfiles
  453. si = open('/dev/null', 'r')
  454. try:
  455. bb.utils.mkdirhier(os.path.dirname(logfn))
  456. logfile = open(logfn, 'w')
  457. except OSError:
  458. logger.exception("Opening log file '%s'", logfn)
  459. pass
  460. # Dup the existing fds so we dont lose them
  461. osi = [os.dup(sys.stdin.fileno()), sys.stdin.fileno()]
  462. oso = [os.dup(sys.stdout.fileno()), sys.stdout.fileno()]
  463. ose = [os.dup(sys.stderr.fileno()), sys.stderr.fileno()]
  464. # Replace those fds with our own
  465. os.dup2(si.fileno(), osi[1])
  466. os.dup2(logfile.fileno(), oso[1])
  467. os.dup2(logfile.fileno(), ose[1])
  468. # Ensure Python logging goes to the logfile
  469. handler = logging.StreamHandler(logfile)
  470. handler.setFormatter(logformatter)
  471. # Always enable full debug output into task logfiles
  472. handler.setLevel(logging.DEBUG - 2)
  473. bblogger.addHandler(handler)
  474. errchk = ErrorCheckHandler()
  475. bblogger.addHandler(errchk)
  476. localdata.setVar('BB_LOGFILE', logfn)
  477. localdata.setVar('BB_RUNTASK', task)
  478. flags = localdata.getVarFlags(task)
  479. try:
  480. try:
  481. event.fire(TaskStarted(task, logfn, flags, localdata), localdata)
  482. except (bb.BBHandledException, SystemExit):
  483. return 1
  484. except FuncFailed as exc:
  485. logger.error(str(exc))
  486. return 1
  487. try:
  488. for func in (prefuncs or '').split():
  489. exec_func(func, localdata)
  490. exec_func(task, localdata)
  491. for func in (postfuncs or '').split():
  492. exec_func(func, localdata)
  493. except FuncFailed as exc:
  494. if quieterr:
  495. event.fire(TaskFailedSilent(task, logfn, localdata), localdata)
  496. else:
  497. errprinted = errchk.triggered
  498. logger.error(str(exc))
  499. event.fire(TaskFailed(task, logfn, localdata, errprinted), localdata)
  500. return 1
  501. except bb.BBHandledException:
  502. event.fire(TaskFailed(task, logfn, localdata, True), localdata)
  503. return 1
  504. finally:
  505. sys.stdout.flush()
  506. sys.stderr.flush()
  507. bblogger.removeHandler(handler)
  508. # Restore the backup fds
  509. os.dup2(osi[0], osi[1])
  510. os.dup2(oso[0], oso[1])
  511. os.dup2(ose[0], ose[1])
  512. # Close the backup fds
  513. os.close(osi[0])
  514. os.close(oso[0])
  515. os.close(ose[0])
  516. si.close()
  517. logfile.close()
  518. if os.path.exists(logfn) and os.path.getsize(logfn) == 0:
  519. logger.debug(2, "Zero size logfn %s, removing", logfn)
  520. bb.utils.remove(logfn)
  521. bb.utils.remove(loglink)
  522. event.fire(TaskSucceeded(task, logfn, localdata), localdata)
  523. if not localdata.getVarFlag(task, 'nostamp', False) and not localdata.getVarFlag(task, 'selfstamp', False):
  524. make_stamp(task, localdata)
  525. return 0
  526. def exec_task(fn, task, d, profile = False):
  527. try:
  528. quieterr = False
  529. if d.getVarFlag(task, "quieterrors", False) is not None:
  530. quieterr = True
  531. if profile:
  532. profname = "profile-%s.log" % (d.getVar("PN") + "-" + task)
  533. try:
  534. import cProfile as profile
  535. except:
  536. import profile
  537. prof = profile.Profile()
  538. ret = profile.Profile.runcall(prof, _exec_task, fn, task, d, quieterr)
  539. prof.dump_stats(profname)
  540. bb.utils.process_profilelog(profname)
  541. return ret
  542. else:
  543. return _exec_task(fn, task, d, quieterr)
  544. except Exception:
  545. from traceback import format_exc
  546. if not quieterr:
  547. logger.error("Build of %s failed" % (task))
  548. logger.error(format_exc())
  549. failedevent = TaskFailed(task, None, d, True)
  550. event.fire(failedevent, d)
  551. return 1
  552. def stamp_internal(taskname, d, file_name, baseonly=False, noextra=False):
  553. """
  554. Internal stamp helper function
  555. Makes sure the stamp directory exists
  556. Returns the stamp path+filename
  557. In the bitbake core, d can be a CacheData and file_name will be set.
  558. When called in task context, d will be a data store, file_name will not be set
  559. """
  560. taskflagname = taskname
  561. if taskname.endswith("_setscene") and taskname != "do_setscene":
  562. taskflagname = taskname.replace("_setscene", "")
  563. if file_name:
  564. stamp = d.stamp[file_name]
  565. extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
  566. else:
  567. stamp = d.getVar('STAMP')
  568. file_name = d.getVar('BB_FILENAME')
  569. extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
  570. if baseonly:
  571. return stamp
  572. if noextra:
  573. extrainfo = ""
  574. if not stamp:
  575. return
  576. stamp = bb.parse.siggen.stampfile(stamp, file_name, taskname, extrainfo)
  577. stampdir = os.path.dirname(stamp)
  578. if cached_mtime_noerror(stampdir) == 0:
  579. bb.utils.mkdirhier(stampdir)
  580. return stamp
  581. def stamp_cleanmask_internal(taskname, d, file_name):
  582. """
  583. Internal stamp helper function to generate stamp cleaning mask
  584. Returns the stamp path+filename
  585. In the bitbake core, d can be a CacheData and file_name will be set.
  586. When called in task context, d will be a data store, file_name will not be set
  587. """
  588. taskflagname = taskname
  589. if taskname.endswith("_setscene") and taskname != "do_setscene":
  590. taskflagname = taskname.replace("_setscene", "")
  591. if file_name:
  592. stamp = d.stampclean[file_name]
  593. extrainfo = d.stamp_extrainfo[file_name].get(taskflagname) or ""
  594. else:
  595. stamp = d.getVar('STAMPCLEAN')
  596. file_name = d.getVar('BB_FILENAME')
  597. extrainfo = d.getVarFlag(taskflagname, 'stamp-extra-info') or ""
  598. if not stamp:
  599. return []
  600. cleanmask = bb.parse.siggen.stampcleanmask(stamp, file_name, taskname, extrainfo)
  601. return [cleanmask, cleanmask.replace(taskflagname, taskflagname + "_setscene")]
  602. def make_stamp(task, d, file_name = None):
  603. """
  604. Creates/updates a stamp for a given task
  605. (d can be a data dict or dataCache)
  606. """
  607. cleanmask = stamp_cleanmask_internal(task, d, file_name)
  608. for mask in cleanmask:
  609. for name in glob.glob(mask):
  610. # Preserve sigdata files in the stamps directory
  611. if "sigdata" in name or "sigbasedata" in name:
  612. continue
  613. # Preserve taint files in the stamps directory
  614. if name.endswith('.taint'):
  615. continue
  616. os.unlink(name)
  617. stamp = stamp_internal(task, d, file_name)
  618. # Remove the file and recreate to force timestamp
  619. # change on broken NFS filesystems
  620. if stamp:
  621. bb.utils.remove(stamp)
  622. open(stamp, "w").close()
  623. # If we're in task context, write out a signature file for each task
  624. # as it completes
  625. if not task.endswith("_setscene") and task != "do_setscene" and not file_name:
  626. stampbase = stamp_internal(task, d, None, True)
  627. file_name = d.getVar('BB_FILENAME')
  628. bb.parse.siggen.dump_sigtask(file_name, task, stampbase, True)
  629. def del_stamp(task, d, file_name = None):
  630. """
  631. Removes a stamp for a given task
  632. (d can be a data dict or dataCache)
  633. """
  634. stamp = stamp_internal(task, d, file_name)
  635. bb.utils.remove(stamp)
  636. def write_taint(task, d, file_name = None):
  637. """
  638. Creates a "taint" file which will force the specified task and its
  639. dependents to be re-run the next time by influencing the value of its
  640. taskhash.
  641. (d can be a data dict or dataCache)
  642. """
  643. import uuid
  644. if file_name:
  645. taintfn = d.stamp[file_name] + '.' + task + '.taint'
  646. else:
  647. taintfn = d.getVar('STAMP') + '.' + task + '.taint'
  648. bb.utils.mkdirhier(os.path.dirname(taintfn))
  649. # The specific content of the taint file is not really important,
  650. # we just need it to be random, so a random UUID is used
  651. with open(taintfn, 'w') as taintf:
  652. taintf.write(str(uuid.uuid4()))
  653. def stampfile(taskname, d, file_name = None, noextra=False):
  654. """
  655. Return the stamp for a given task
  656. (d can be a data dict or dataCache)
  657. """
  658. return stamp_internal(taskname, d, file_name, noextra=noextra)
  659. def add_tasks(tasklist, d):
  660. task_deps = d.getVar('_task_deps', False)
  661. if not task_deps:
  662. task_deps = {}
  663. if not 'tasks' in task_deps:
  664. task_deps['tasks'] = []
  665. if not 'parents' in task_deps:
  666. task_deps['parents'] = {}
  667. for task in tasklist:
  668. task = d.expand(task)
  669. d.setVarFlag(task, 'task', 1)
  670. if not task in task_deps['tasks']:
  671. task_deps['tasks'].append(task)
  672. flags = d.getVarFlags(task)
  673. def getTask(name):
  674. if not name in task_deps:
  675. task_deps[name] = {}
  676. if name in flags:
  677. deptask = d.expand(flags[name])
  678. task_deps[name][task] = deptask
  679. getTask('depends')
  680. getTask('rdepends')
  681. getTask('deptask')
  682. getTask('rdeptask')
  683. getTask('recrdeptask')
  684. getTask('recideptask')
  685. getTask('nostamp')
  686. getTask('fakeroot')
  687. getTask('noexec')
  688. getTask('umask')
  689. task_deps['parents'][task] = []
  690. if 'deps' in flags:
  691. for dep in flags['deps']:
  692. dep = d.expand(dep)
  693. task_deps['parents'][task].append(dep)
  694. # don't assume holding a reference
  695. d.setVar('_task_deps', task_deps)
  696. def addtask(task, before, after, d):
  697. if task[:3] != "do_":
  698. task = "do_" + task
  699. d.setVarFlag(task, "task", 1)
  700. bbtasks = d.getVar('__BBTASKS', False) or []
  701. if task not in bbtasks:
  702. bbtasks.append(task)
  703. d.setVar('__BBTASKS', bbtasks)
  704. existing = d.getVarFlag(task, "deps", False) or []
  705. if after is not None:
  706. # set up deps for function
  707. for entry in after.split():
  708. if entry not in existing:
  709. existing.append(entry)
  710. d.setVarFlag(task, "deps", existing)
  711. if before is not None:
  712. # set up things that depend on this func
  713. for entry in before.split():
  714. existing = d.getVarFlag(entry, "deps", False) or []
  715. if task not in existing:
  716. d.setVarFlag(entry, "deps", [task] + existing)
  717. def deltask(task, d):
  718. if task[:3] != "do_":
  719. task = "do_" + task
  720. bbtasks = d.getVar('__BBTASKS', False) or []
  721. if task in bbtasks:
  722. bbtasks.remove(task)
  723. d.delVarFlag(task, 'task')
  724. d.setVar('__BBTASKS', bbtasks)
  725. d.delVarFlag(task, 'deps')
  726. for bbtask in d.getVar('__BBTASKS', False) or []:
  727. deps = d.getVarFlag(bbtask, 'deps', False) or []
  728. if task in deps:
  729. deps.remove(task)
  730. d.setVarFlag(bbtask, 'deps', deps)