taskdata.py 23 KB

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