taskdata.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. #!/usr/bin/env python
  2. # ex:ts=4:sw=4:sts=4:et
  3. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  4. """
  5. BitBake 'TaskData' implementation
  6. Task data collection and handling
  7. """
  8. # Copyright (C) 2006 Richard Purdie
  9. #
  10. # This program is free software; you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License version 2 as
  12. # published by the Free Software Foundation.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License along
  20. # with this program; if not, write to the Free Software Foundation, Inc.,
  21. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  22. import logging
  23. import re
  24. import bb
  25. logger = logging.getLogger("BitBake.TaskData")
  26. def re_match_strings(target, strings):
  27. """
  28. Whether or not the string 'target' matches
  29. any one string of the strings which can be regular expression string
  30. """
  31. return any(name == target or re.match(name, target)
  32. for name in strings)
  33. class TaskData:
  34. """
  35. BitBake Task Data implementation
  36. """
  37. def __init__(self, abort = True, tryaltconfigs = False):
  38. self.build_names_index = []
  39. self.run_names_index = []
  40. self.fn_index = []
  41. self.build_targets = {}
  42. self.run_targets = {}
  43. self.external_targets = []
  44. self.tasks_fnid = []
  45. self.tasks_name = []
  46. self.tasks_tdepends = []
  47. self.tasks_idepends = []
  48. # Cache to speed up task ID lookups
  49. self.tasks_lookup = {}
  50. self.depids = {}
  51. self.rdepids = {}
  52. self.consider_msgs_cache = []
  53. self.failed_deps = []
  54. self.failed_rdeps = []
  55. self.failed_fnids = []
  56. self.abort = abort
  57. self.tryaltconfigs = tryaltconfigs
  58. def getbuild_id(self, name):
  59. """
  60. Return an ID number for the build target name.
  61. If it doesn't exist, create one.
  62. """
  63. if not name in self.build_names_index:
  64. self.build_names_index.append(name)
  65. return len(self.build_names_index) - 1
  66. return self.build_names_index.index(name)
  67. def getrun_id(self, name):
  68. """
  69. Return an ID number for the run target name.
  70. If it doesn't exist, create one.
  71. """
  72. if not name in self.run_names_index:
  73. self.run_names_index.append(name)
  74. return len(self.run_names_index) - 1
  75. return self.run_names_index.index(name)
  76. def getfn_id(self, name):
  77. """
  78. Return an ID number for the filename.
  79. If it doesn't exist, create one.
  80. """
  81. if not name in self.fn_index:
  82. self.fn_index.append(name)
  83. return len(self.fn_index) - 1
  84. return self.fn_index.index(name)
  85. def gettask_ids(self, fnid):
  86. """
  87. Return an array of the ID numbers matching a given fnid.
  88. """
  89. ids = []
  90. if fnid in self.tasks_lookup:
  91. for task in self.tasks_lookup[fnid]:
  92. ids.append(self.tasks_lookup[fnid][task])
  93. return ids
  94. def gettask_id(self, fn, task, create = True):
  95. """
  96. Return an ID number for the task matching fn and task.
  97. If it doesn't exist, create one by default.
  98. Optionally return None instead.
  99. """
  100. fnid = self.getfn_id(fn)
  101. if fnid in self.tasks_lookup:
  102. if task in self.tasks_lookup[fnid]:
  103. return self.tasks_lookup[fnid][task]
  104. if not create:
  105. return None
  106. self.tasks_name.append(task)
  107. self.tasks_fnid.append(fnid)
  108. self.tasks_tdepends.append([])
  109. self.tasks_idepends.append([])
  110. listid = len(self.tasks_name) - 1
  111. if fnid not in self.tasks_lookup:
  112. self.tasks_lookup[fnid] = {}
  113. self.tasks_lookup[fnid][task] = listid
  114. return listid
  115. def add_tasks(self, fn, dataCache):
  116. """
  117. Add tasks for a given fn to the database
  118. """
  119. task_deps = dataCache.task_deps[fn]
  120. fnid = self.getfn_id(fn)
  121. if fnid in self.failed_fnids:
  122. bb.msg.fatal(bb.msg.domain.TaskData, "Trying to re-add a failed file? Something is broken...")
  123. # Check if we've already seen this fn
  124. if fnid in self.tasks_fnid:
  125. return
  126. for task in task_deps['tasks']:
  127. # Work out task dependencies
  128. parentids = []
  129. for dep in task_deps['parents'][task]:
  130. parentid = self.gettask_id(fn, dep)
  131. parentids.append(parentid)
  132. taskid = self.gettask_id(fn, task)
  133. self.tasks_tdepends[taskid].extend(parentids)
  134. # Touch all intertask dependencies
  135. if 'depends' in task_deps and task in task_deps['depends']:
  136. ids = []
  137. for dep in task_deps['depends'][task].split():
  138. if dep:
  139. if ":" not in dep:
  140. bb.msg.fatal(bb.msg.domain.TaskData, "Error, dependency %s does not contain ':' character\n. Task 'depends' should be specified in the form 'packagename:task'" % (dep, fn))
  141. ids.append(((self.getbuild_id(dep.split(":")[0])), dep.split(":")[1]))
  142. self.tasks_idepends[taskid].extend(ids)
  143. # Work out build dependencies
  144. if not fnid in self.depids:
  145. dependids = {}
  146. for depend in dataCache.deps[fn]:
  147. logger.debug(2, "Added dependency %s for %s", depend, fn)
  148. dependids[self.getbuild_id(depend)] = None
  149. self.depids[fnid] = dependids.keys()
  150. # Work out runtime dependencies
  151. if not fnid in self.rdepids:
  152. rdependids = {}
  153. rdepends = dataCache.rundeps[fn]
  154. rrecs = dataCache.runrecs[fn]
  155. for package in rdepends:
  156. for rdepend in rdepends[package]:
  157. logger.debug(2, "Added runtime dependency %s for %s", rdepend, fn)
  158. rdependids[self.getrun_id(rdepend)] = None
  159. for package in rrecs:
  160. for rdepend in rrecs[package]:
  161. logger.debug(2, "Added runtime recommendation %s for %s", rdepend, fn)
  162. rdependids[self.getrun_id(rdepend)] = None
  163. self.rdepids[fnid] = rdependids.keys()
  164. for dep in self.depids[fnid]:
  165. if dep in self.failed_deps:
  166. self.fail_fnid(fnid)
  167. return
  168. for dep in self.rdepids[fnid]:
  169. if dep in self.failed_rdeps:
  170. self.fail_fnid(fnid)
  171. return
  172. def have_build_target(self, target):
  173. """
  174. Have we a build target matching this name?
  175. """
  176. targetid = self.getbuild_id(target)
  177. if targetid in self.build_targets:
  178. return True
  179. return False
  180. def have_runtime_target(self, target):
  181. """
  182. Have we a runtime target matching this name?
  183. """
  184. targetid = self.getrun_id(target)
  185. if targetid in self.run_targets:
  186. return True
  187. return False
  188. def add_build_target(self, fn, item):
  189. """
  190. Add a build target.
  191. If already present, append the provider fn to the list
  192. """
  193. targetid = self.getbuild_id(item)
  194. fnid = self.getfn_id(fn)
  195. if targetid in self.build_targets:
  196. if fnid in self.build_targets[targetid]:
  197. return
  198. self.build_targets[targetid].append(fnid)
  199. return
  200. self.build_targets[targetid] = [fnid]
  201. def add_runtime_target(self, fn, item):
  202. """
  203. Add a runtime target.
  204. If already present, append the provider fn to the list
  205. """
  206. targetid = self.getrun_id(item)
  207. fnid = self.getfn_id(fn)
  208. if targetid in self.run_targets:
  209. if fnid in self.run_targets[targetid]:
  210. return
  211. self.run_targets[targetid].append(fnid)
  212. return
  213. self.run_targets[targetid] = [fnid]
  214. def mark_external_target(self, item):
  215. """
  216. Mark a build target as being externally requested
  217. """
  218. targetid = self.getbuild_id(item)
  219. if targetid not in self.external_targets:
  220. self.external_targets.append(targetid)
  221. def get_unresolved_build_targets(self, dataCache):
  222. """
  223. Return a list of build targets who's providers
  224. are unknown.
  225. """
  226. unresolved = []
  227. for target in self.build_names_index:
  228. if re_match_strings(target, dataCache.ignored_dependencies):
  229. continue
  230. if self.build_names_index.index(target) in self.failed_deps:
  231. continue
  232. if not self.have_build_target(target):
  233. unresolved.append(target)
  234. return unresolved
  235. def get_unresolved_run_targets(self, dataCache):
  236. """
  237. Return a list of runtime targets who's providers
  238. are unknown.
  239. """
  240. unresolved = []
  241. for target in self.run_names_index:
  242. if re_match_strings(target, dataCache.ignored_dependencies):
  243. continue
  244. if self.run_names_index.index(target) in self.failed_rdeps:
  245. continue
  246. if not self.have_runtime_target(target):
  247. unresolved.append(target)
  248. return unresolved
  249. def get_provider(self, item):
  250. """
  251. Return a list of providers of item
  252. """
  253. targetid = self.getbuild_id(item)
  254. return self.build_targets[targetid]
  255. def get_dependees(self, itemid):
  256. """
  257. Return a list of targets which depend on item
  258. """
  259. dependees = []
  260. for fnid in self.depids:
  261. if itemid in self.depids[fnid]:
  262. dependees.append(fnid)
  263. return dependees
  264. def get_dependees_str(self, item):
  265. """
  266. Return a list of targets which depend on item as a user readable string
  267. """
  268. itemid = self.getbuild_id(item)
  269. dependees = []
  270. for fnid in self.depids:
  271. if itemid in self.depids[fnid]:
  272. dependees.append(self.fn_index[fnid])
  273. return dependees
  274. def get_rdependees(self, itemid):
  275. """
  276. Return a list of targets which depend on runtime item
  277. """
  278. dependees = []
  279. for fnid in self.rdepids:
  280. if itemid in self.rdepids[fnid]:
  281. dependees.append(fnid)
  282. return dependees
  283. def get_rdependees_str(self, item):
  284. """
  285. Return a list of targets which depend on runtime item as a user readable string
  286. """
  287. itemid = self.getrun_id(item)
  288. dependees = []
  289. for fnid in self.rdepids:
  290. if itemid in self.rdepids[fnid]:
  291. dependees.append(self.fn_index[fnid])
  292. return dependees
  293. def add_provider(self, cfgData, dataCache, item):
  294. try:
  295. self.add_provider_internal(cfgData, dataCache, item)
  296. except bb.providers.NoProvider:
  297. if self.abort:
  298. raise
  299. self.remove_buildtarget(self.getbuild_id(item))
  300. self.mark_external_target(item)
  301. def add_provider_internal(self, cfgData, dataCache, item):
  302. """
  303. Add the providers of item to the task data
  304. Mark entries were specifically added externally as against dependencies
  305. added internally during dependency resolution
  306. """
  307. if re_match_strings(item, dataCache.ignored_dependencies):
  308. return
  309. if not item in dataCache.providers:
  310. bb.event.fire(bb.event.NoProvider(item, dependees=self.get_rdependees_str(item)), cfgData)
  311. raise bb.providers.NoProvider(item)
  312. if self.have_build_target(item):
  313. return
  314. all_p = dataCache.providers[item]
  315. eligible, foundUnique = bb.providers.filterProviders(all_p, item, cfgData, dataCache)
  316. eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids]
  317. if not eligible:
  318. bb.event.fire(bb.event.NoProvider(item, dependees=self.get_dependees_str(item)), cfgData)
  319. raise bb.providers.NoProvider(item)
  320. if len(eligible) > 1 and foundUnique == False:
  321. if item not in self.consider_msgs_cache:
  322. providers_list = []
  323. for fn in eligible:
  324. providers_list.append(dataCache.pkg_fn[fn])
  325. bb.event.fire(bb.event.MultipleProviders(item, providers_list), cfgData)
  326. self.consider_msgs_cache.append(item)
  327. for fn in eligible:
  328. fnid = self.getfn_id(fn)
  329. if fnid in self.failed_fnids:
  330. continue
  331. logger.debug(2, "adding %s to satisfy %s", fn, item)
  332. self.add_build_target(fn, item)
  333. self.add_tasks(fn, dataCache)
  334. #item = dataCache.pkg_fn[fn]
  335. def add_rprovider(self, cfgData, dataCache, item):
  336. """
  337. Add the runtime providers of item to the task data
  338. (takes item names from RDEPENDS/PACKAGES namespace)
  339. """
  340. if re_match_strings(item, dataCache.ignored_dependencies):
  341. return
  342. if self.have_runtime_target(item):
  343. return
  344. all_p = bb.providers.getRuntimeProviders(dataCache, item)
  345. if not all_p:
  346. bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item)), cfgData)
  347. raise bb.providers.NoRProvider(item)
  348. eligible, numberPreferred = bb.providers.filterProvidersRunTime(all_p, item, cfgData, dataCache)
  349. eligible = [p for p in eligible if not self.getfn_id(p) in self.failed_fnids]
  350. if not eligible:
  351. bb.event.fire(bb.event.NoProvider(item, runtime=True, dependees=self.get_rdependees_str(item)), cfgData)
  352. raise bb.providers.NoRProvider(item)
  353. if len(eligible) > 1 and numberPreferred == 0:
  354. if item not in self.consider_msgs_cache:
  355. providers_list = []
  356. for fn in eligible:
  357. providers_list.append(dataCache.pkg_fn[fn])
  358. bb.event.fire(bb.event.MultipleProviders(item, providers_list, runtime=True), cfgData)
  359. self.consider_msgs_cache.append(item)
  360. if numberPreferred > 1:
  361. if item not in self.consider_msgs_cache:
  362. providers_list = []
  363. for fn in eligible:
  364. providers_list.append(dataCache.pkg_fn[fn])
  365. bb.event.fire(bb.event.MultipleProviders(item, providers_list, runtime=True), cfgData)
  366. self.consider_msgs_cache.append(item)
  367. # run through the list until we find one that we can build
  368. for fn in eligible:
  369. fnid = self.getfn_id(fn)
  370. if fnid in self.failed_fnids:
  371. continue
  372. logger.debug(2, "adding '%s' to satisfy runtime '%s'", fn, item)
  373. self.add_runtime_target(fn, item)
  374. self.add_tasks(fn, dataCache)
  375. def fail_fnid(self, fnid, missing_list = []):
  376. """
  377. Mark a file as failed (unbuildable)
  378. Remove any references from build and runtime provider lists
  379. missing_list, A list of missing requirements for this target
  380. """
  381. if fnid in self.failed_fnids:
  382. return
  383. logger.debug(1, "File '%s' is unbuildable, removing...", self.fn_index[fnid])
  384. self.failed_fnids.append(fnid)
  385. for target in self.build_targets:
  386. if fnid in self.build_targets[target]:
  387. self.build_targets[target].remove(fnid)
  388. if len(self.build_targets[target]) == 0:
  389. self.remove_buildtarget(target, missing_list)
  390. for target in self.run_targets:
  391. if fnid in self.run_targets[target]:
  392. self.run_targets[target].remove(fnid)
  393. if len(self.run_targets[target]) == 0:
  394. self.remove_runtarget(target, missing_list)
  395. def remove_buildtarget(self, targetid, missing_list = []):
  396. """
  397. Mark a build target as failed (unbuildable)
  398. Trigger removal of any files that have this as a dependency
  399. """
  400. if not missing_list:
  401. missing_list = [self.build_names_index[targetid]]
  402. else:
  403. missing_list = [self.build_names_index[targetid]] + missing_list
  404. logger.verbose("Target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", self.build_names_index[targetid], missing_list)
  405. self.failed_deps.append(targetid)
  406. dependees = self.get_dependees(targetid)
  407. for fnid in dependees:
  408. self.fail_fnid(fnid, missing_list)
  409. for taskid in xrange(len(self.tasks_idepends)):
  410. idepends = self.tasks_idepends[taskid]
  411. for (idependid, idependtask) in idepends:
  412. if idependid == targetid:
  413. self.fail_fnid(self.tasks_fnid[taskid], missing_list)
  414. if self.abort and targetid in self.external_targets:
  415. target = self.build_names_index[targetid]
  416. logger.error("Required build target '%s' has no buildable providers.\nMissing or unbuildable dependency chain was: %s", target, missing_list)
  417. raise bb.providers.NoProvider(target)
  418. def remove_runtarget(self, targetid, missing_list = []):
  419. """
  420. Mark a run target as failed (unbuildable)
  421. Trigger removal of any files that have this as a dependency
  422. """
  423. if not missing_list:
  424. missing_list = [self.run_names_index[targetid]]
  425. else:
  426. missing_list = [self.run_names_index[targetid]] + missing_list
  427. logger.info("Runtime target '%s' is unbuildable, removing...\nMissing or unbuildable dependency chain was: %s", self.run_names_index[targetid], missing_list)
  428. self.failed_rdeps.append(targetid)
  429. dependees = self.get_rdependees(targetid)
  430. for fnid in dependees:
  431. self.fail_fnid(fnid, missing_list)
  432. def add_unresolved(self, cfgData, dataCache):
  433. """
  434. Resolve all unresolved build and runtime targets
  435. """
  436. logger.info("Resolving any missing task queue dependencies")
  437. while True:
  438. added = 0
  439. for target in self.get_unresolved_build_targets(dataCache):
  440. try:
  441. self.add_provider_internal(cfgData, dataCache, target)
  442. added = added + 1
  443. except bb.providers.NoProvider:
  444. targetid = self.getbuild_id(target)
  445. if self.abort and targetid in self.external_targets:
  446. raise
  447. self.remove_buildtarget(targetid)
  448. for target in self.get_unresolved_run_targets(dataCache):
  449. try:
  450. self.add_rprovider(cfgData, dataCache, target)
  451. added = added + 1
  452. except bb.providers.NoRProvider:
  453. self.remove_runtarget(self.getrun_id(target))
  454. logger.debug(1, "Resolved " + str(added) + " extra dependencies")
  455. if added == 0:
  456. break
  457. # self.dump_data()
  458. def dump_data(self):
  459. """
  460. Dump some debug information on the internal data structures
  461. """
  462. logger.debug(3, "build_names:")
  463. logger.debug(3, ", ".join(self.build_names_index))
  464. logger.debug(3, "run_names:")
  465. logger.debug(3, ", ".join(self.run_names_index))
  466. logger.debug(3, "build_targets:")
  467. for buildid in xrange(len(self.build_names_index)):
  468. target = self.build_names_index[buildid]
  469. targets = "None"
  470. if buildid in self.build_targets:
  471. targets = self.build_targets[buildid]
  472. logger.debug(3, " (%s)%s: %s", buildid, target, targets)
  473. logger.debug(3, "run_targets:")
  474. for runid in xrange(len(self.run_names_index)):
  475. target = self.run_names_index[runid]
  476. targets = "None"
  477. if runid in self.run_targets:
  478. targets = self.run_targets[runid]
  479. logger.debug(3, " (%s)%s: %s", runid, target, targets)
  480. logger.debug(3, "tasks:")
  481. for task in xrange(len(self.tasks_name)):
  482. logger.debug(3, " (%s)%s - %s: %s",
  483. task,
  484. self.fn_index[self.tasks_fnid[task]],
  485. self.tasks_name[task],
  486. self.tasks_tdepends[task])
  487. logger.debug(3, "dependency ids (per fn):")
  488. for fnid in self.depids:
  489. logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.depids[fnid])
  490. logger.debug(3, "runtime dependency ids (per fn):")
  491. for fnid in self.rdepids:
  492. logger.debug(3, " %s %s: %s", fnid, self.fn_index[fnid], self.rdepids[fnid])