taskdata.py 21 KB

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