hobeventhandler.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. #
  2. # BitBake Graphical GTK User Interface
  3. #
  4. # Copyright (C) 2011 Intel Corporation
  5. #
  6. # Authored by Joshua Lock <josh@linux.intel.com>
  7. # Authored by Dongxiao Xu <dongxiao.xu@intel.com>
  8. #
  9. # This program is free software; you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License version 2 as
  11. # published by the Free Software Foundation.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License along
  19. # with this program; if not, write to the Free Software Foundation, Inc.,
  20. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. import gobject
  22. import logging
  23. from bb.ui.crumbs.runningbuild import RunningBuild
  24. from bb.ui.crumbs.hobwidget import hcc
  25. class HobHandler(gobject.GObject):
  26. """
  27. This object does BitBake event handling for the hob gui.
  28. """
  29. __gsignals__ = {
  30. "package-formats-updated" : (gobject.SIGNAL_RUN_LAST,
  31. gobject.TYPE_NONE,
  32. (gobject.TYPE_PYOBJECT,)),
  33. "config-updated" : (gobject.SIGNAL_RUN_LAST,
  34. gobject.TYPE_NONE,
  35. (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT,)),
  36. "command-succeeded" : (gobject.SIGNAL_RUN_LAST,
  37. gobject.TYPE_NONE,
  38. (gobject.TYPE_INT,)),
  39. "command-failed" : (gobject.SIGNAL_RUN_LAST,
  40. gobject.TYPE_NONE,
  41. (gobject.TYPE_STRING,)),
  42. "sanity-failed" : (gobject.SIGNAL_RUN_LAST,
  43. gobject.TYPE_NONE,
  44. (gobject.TYPE_STRING,)),
  45. "generating-data" : (gobject.SIGNAL_RUN_LAST,
  46. gobject.TYPE_NONE,
  47. ()),
  48. "data-generated" : (gobject.SIGNAL_RUN_LAST,
  49. gobject.TYPE_NONE,
  50. ()),
  51. "parsing-started" : (gobject.SIGNAL_RUN_LAST,
  52. gobject.TYPE_NONE,
  53. (gobject.TYPE_PYOBJECT,)),
  54. "parsing" : (gobject.SIGNAL_RUN_LAST,
  55. gobject.TYPE_NONE,
  56. (gobject.TYPE_PYOBJECT,)),
  57. "parsing-completed" : (gobject.SIGNAL_RUN_LAST,
  58. gobject.TYPE_NONE,
  59. (gobject.TYPE_PYOBJECT,)),
  60. "recipe-populated" : (gobject.SIGNAL_RUN_LAST,
  61. gobject.TYPE_NONE,
  62. ()),
  63. "package-populated" : (gobject.SIGNAL_RUN_LAST,
  64. gobject.TYPE_NONE,
  65. ()),
  66. }
  67. (GENERATE_CONFIGURATION, GENERATE_RECIPES, GENERATE_PACKAGES, GENERATE_IMAGE, POPULATE_PACKAGEINFO, SANITY_CHECK) = range(6)
  68. (SUB_PATH_LAYERS, SUB_FILES_DISTRO, SUB_FILES_MACH, SUB_FILES_SDKMACH, SUB_MATCH_CLASS, SUB_PARSE_CONFIG, SUB_SANITY_CHECK, SUB_GNERATE_TGTS, SUB_GENERATE_PKGINFO, SUB_BUILD_RECIPES, SUB_BUILD_IMAGE) = range(11)
  69. def __init__(self, server, recipe_model, package_model):
  70. super(HobHandler, self).__init__()
  71. self.build = RunningBuild(sequential=True)
  72. self.recipe_model = recipe_model
  73. self.package_model = package_model
  74. self.commands_async = []
  75. self.generating = False
  76. self.current_phase = None
  77. self.building = False
  78. self.recipe_queue = []
  79. self.package_queue = []
  80. self.server = server
  81. self.error_msg = ""
  82. self.initcmd = None
  83. def set_busy(self):
  84. if not self.generating:
  85. self.emit("generating-data")
  86. self.generating = True
  87. def clear_busy(self):
  88. if self.generating:
  89. self.emit("data-generated")
  90. self.generating = False
  91. def runCommand(self, commandline):
  92. try:
  93. result, error = self.server.runCommand(commandline)
  94. if error:
  95. raise Exception("Error running command '%s': %s" % (commandline, error))
  96. return result
  97. except Exception as e:
  98. self.commands_async = []
  99. self.clear_busy()
  100. self.emit("command-failed", "Hob Exception - %s" % (str(e)))
  101. return None
  102. def run_next_command(self, initcmd=None):
  103. if initcmd != None:
  104. self.initcmd = initcmd
  105. if self.commands_async:
  106. self.set_busy()
  107. next_command = self.commands_async.pop(0)
  108. else:
  109. self.clear_busy()
  110. if self.initcmd != None:
  111. self.emit("command-succeeded", self.initcmd)
  112. return
  113. if next_command == self.SUB_PATH_LAYERS:
  114. self.runCommand(["findConfigFilePath", "bblayers.conf"])
  115. elif next_command == self.SUB_FILES_DISTRO:
  116. self.runCommand(["findConfigFiles", "DISTRO"])
  117. elif next_command == self.SUB_FILES_MACH:
  118. self.runCommand(["findConfigFiles", "MACHINE"])
  119. elif next_command == self.SUB_FILES_SDKMACH:
  120. self.runCommand(["findConfigFiles", "MACHINE-SDK"])
  121. elif next_command == self.SUB_MATCH_CLASS:
  122. self.runCommand(["findFilesMatchingInDir", "rootfs_", "classes"])
  123. elif next_command == self.SUB_PARSE_CONFIG:
  124. self.runCommand(["parseConfigurationFiles", "", ""])
  125. elif next_command == self.SUB_GNERATE_TGTS:
  126. self.runCommand(["generateTargetsTree", "classes/image.bbclass", []])
  127. elif next_command == self.SUB_GENERATE_PKGINFO:
  128. self.runCommand(["triggerEvent", "bb.event.RequestPackageInfo()"])
  129. elif next_command == self.SUB_SANITY_CHECK:
  130. self.runCommand(["triggerEvent", "bb.event.SanityCheck()"])
  131. elif next_command == self.SUB_BUILD_RECIPES:
  132. self.clear_busy()
  133. self.building = True
  134. self.runCommand(["buildTargets", self.recipe_queue, self.default_task])
  135. self.recipe_queue = []
  136. elif next_command == self.SUB_BUILD_IMAGE:
  137. self.clear_busy()
  138. self.building = True
  139. targets = [self.image]
  140. if self.package_queue:
  141. self.runCommand(["setVariable", "LINGUAS_INSTALL", ""])
  142. self.runCommand(["setVariable", "PACKAGE_INSTALL", " ".join(self.package_queue)])
  143. if self.toolchain_packages:
  144. self.runCommand(["setVariable", "TOOLCHAIN_TARGET_TASK", " ".join(self.toolchain_packages)])
  145. targets.append(self.toolchain)
  146. self.runCommand(["buildTargets", targets, self.default_task])
  147. def handle_event(self, event):
  148. if not event:
  149. return
  150. if self.building:
  151. self.current_phase = "building"
  152. self.build.handle_event(event)
  153. if isinstance(event, bb.event.PackageInfo):
  154. self.package_model.populate(event._pkginfolist)
  155. self.emit("package-populated")
  156. self.run_next_command()
  157. elif isinstance(event, bb.event.SanityCheckPassed):
  158. self.run_next_command()
  159. elif isinstance(event, bb.event.SanityCheckFailed):
  160. self.emit("sanity-failed", event._msg)
  161. elif isinstance(event, logging.LogRecord):
  162. if event.levelno >= logging.ERROR:
  163. formatter = bb.msg.BBLogFormatter()
  164. formatter.format(event)
  165. self.error_msg += event.message + '\n'
  166. elif isinstance(event, bb.event.TargetsTreeGenerated):
  167. self.current_phase = "data generation"
  168. if event._model:
  169. self.recipe_model.populate(event._model)
  170. self.emit("recipe-populated")
  171. elif isinstance(event, bb.event.ConfigFilesFound):
  172. self.current_phase = "configuration lookup"
  173. var = event._variable
  174. values = event._values
  175. values.sort()
  176. self.emit("config-updated", var, values)
  177. elif isinstance(event, bb.event.ConfigFilePathFound):
  178. self.current_phase = "configuration lookup"
  179. elif isinstance(event, bb.event.FilesMatchingFound):
  180. self.current_phase = "configuration lookup"
  181. # FIXME: hard coding, should at least be a variable shared between
  182. # here and the caller
  183. if event._pattern == "rootfs_":
  184. formats = []
  185. for match in event._matches:
  186. classname, sep, cls = match.rpartition(".")
  187. fs, sep, format = classname.rpartition("_")
  188. formats.append(format)
  189. formats.sort()
  190. self.emit("package-formats-updated", formats)
  191. elif isinstance(event, bb.command.CommandCompleted):
  192. self.current_phase = None
  193. self.run_next_command()
  194. elif isinstance(event, bb.command.CommandFailed):
  195. self.commands_async = []
  196. self.clear_busy()
  197. self.emit("command-failed", self.error_msg)
  198. self.error_msg = ""
  199. if self.building:
  200. self.building = False
  201. elif isinstance(event, (bb.event.ParseStarted,
  202. bb.event.CacheLoadStarted,
  203. bb.event.TreeDataPreparationStarted,
  204. )):
  205. message = {}
  206. message["eventname"] = bb.event.getName(event)
  207. message["current"] = 0
  208. message["total"] = None
  209. message["title"] = "Parsing recipes: "
  210. self.emit("parsing-started", message)
  211. elif isinstance(event, (bb.event.ParseProgress,
  212. bb.event.CacheLoadProgress,
  213. bb.event.TreeDataPreparationProgress)):
  214. message = {}
  215. message["eventname"] = bb.event.getName(event)
  216. message["current"] = event.current
  217. message["total"] = event.total
  218. message["title"] = "Parsing recipes: "
  219. self.emit("parsing", message)
  220. elif isinstance(event, (bb.event.ParseCompleted,
  221. bb.event.CacheLoadCompleted,
  222. bb.event.TreeDataPreparationCompleted)):
  223. message = {}
  224. message["eventname"] = bb.event.getName(event)
  225. message["current"] = event.total
  226. message["total"] = event.total
  227. message["title"] = "Parsing recipes: "
  228. self.emit("parsing-completed", message)
  229. return
  230. def init_cooker(self):
  231. self.runCommand(["initCooker"])
  232. def set_extra_inherit(self, bbclass):
  233. inherits = self.runCommand(["getVariable", "INHERIT"]) or ""
  234. inherits = inherits + " " + bbclass
  235. self.runCommand(["setVariable", "INHERIT", inherits])
  236. def set_bblayers(self, bblayers):
  237. self.runCommand(["setVariable", "BBLAYERS_HOB", " ".join(bblayers)])
  238. def set_machine(self, machine):
  239. if machine:
  240. self.runCommand(["setVariable", "MACHINE_HOB", machine])
  241. def set_sdk_machine(self, sdk_machine):
  242. self.runCommand(["setVariable", "SDKMACHINE_HOB", sdk_machine])
  243. def set_image_fstypes(self, image_fstypes):
  244. self.runCommand(["setVariable", "IMAGE_FSTYPES", image_fstypes])
  245. def set_distro(self, distro):
  246. self.runCommand(["setVariable", "DISTRO_HOB", distro])
  247. def set_package_format(self, format):
  248. package_classes = ""
  249. for pkgfmt in format.split():
  250. package_classes += ("package_%s" % pkgfmt + " ")
  251. self.runCommand(["setVariable", "PACKAGE_CLASSES_HOB", package_classes])
  252. def set_bbthreads(self, threads):
  253. self.runCommand(["setVariable", "BB_NUMBER_THREADS_HOB", threads])
  254. def set_pmake(self, threads):
  255. pmake = "-j %s" % threads
  256. self.runCommand(["setVariable", "PARALLEL_MAKE_HOB", pmake])
  257. def set_dl_dir(self, directory):
  258. self.runCommand(["setVariable", "DL_DIR_HOB", directory])
  259. def set_sstate_dir(self, directory):
  260. self.runCommand(["setVariable", "SSTATE_DIR_HOB", directory])
  261. def set_sstate_mirror(self, url):
  262. self.runCommand(["setVariable", "SSTATE_MIRROR_HOB", url])
  263. def set_extra_size(self, image_extra_size):
  264. self.runCommand(["setVariable", "IMAGE_ROOTFS_EXTRA_SPACE", str(image_extra_size)])
  265. def set_rootfs_size(self, image_rootfs_size):
  266. self.runCommand(["setVariable", "IMAGE_ROOTFS_SIZE", str(image_rootfs_size)])
  267. def set_incompatible_license(self, incompat_license):
  268. self.runCommand(["setVariable", "INCOMPATIBLE_LICENSE_HOB", incompat_license])
  269. def set_extra_config(self, extra_setting):
  270. for key in extra_setting.keys():
  271. value = extra_setting[key]
  272. self.runCommand(["setVariable", key, value])
  273. def set_config_filter(self, config_filter):
  274. self.runCommand(["setConfFilter", config_filter])
  275. def set_http_proxy(self, http_proxy):
  276. self.runCommand(["setVariable", "http_proxy", http_proxy])
  277. def set_https_proxy(self, https_proxy):
  278. self.runCommand(["setVariable", "https_proxy", https_proxy])
  279. def set_ftp_proxy(self, ftp_proxy):
  280. self.runCommand(["setVariable", "ftp_proxy", ftp_proxy])
  281. def set_all_proxy(self, all_proxy):
  282. self.runCommand(["setVariable", "all_proxy", all_proxy])
  283. def set_git_proxy(self, host, port):
  284. self.runCommand(["setVariable", "GIT_PROXY_HOST", host])
  285. self.runCommand(["setVariable", "GIT_PROXY_PORT", port])
  286. def set_cvs_proxy(self, host, port):
  287. self.runCommand(["setVariable", "CVS_PROXY_HOST", host])
  288. self.runCommand(["setVariable", "CVS_PROXY_PORT", port])
  289. def request_package_info(self):
  290. self.commands_async.append(self.SUB_GENERATE_PKGINFO)
  291. self.run_next_command(self.POPULATE_PACKAGEINFO)
  292. def trigger_sanity_check(self):
  293. self.commands_async.append(self.SUB_SANITY_CHECK)
  294. self.run_next_command(self.SANITY_CHECK)
  295. def generate_configuration(self):
  296. self.commands_async.append(self.SUB_PARSE_CONFIG)
  297. self.commands_async.append(self.SUB_PATH_LAYERS)
  298. self.commands_async.append(self.SUB_FILES_DISTRO)
  299. self.commands_async.append(self.SUB_FILES_MACH)
  300. self.commands_async.append(self.SUB_FILES_SDKMACH)
  301. self.commands_async.append(self.SUB_MATCH_CLASS)
  302. self.run_next_command(self.GENERATE_CONFIGURATION)
  303. def generate_recipes(self):
  304. self.commands_async.append(self.SUB_PARSE_CONFIG)
  305. self.commands_async.append(self.SUB_GNERATE_TGTS)
  306. self.run_next_command(self.GENERATE_RECIPES)
  307. def generate_packages(self, tgts, default_task="build"):
  308. targets = []
  309. targets.extend(tgts)
  310. self.recipe_queue = targets
  311. self.default_task = default_task
  312. self.commands_async.append(self.SUB_PARSE_CONFIG)
  313. self.commands_async.append(self.SUB_BUILD_RECIPES)
  314. self.run_next_command(self.GENERATE_PACKAGES)
  315. def generate_image(self, image, toolchain, image_packages=[], toolchain_packages=[], default_task="build"):
  316. self.image = image
  317. self.toolchain = toolchain
  318. self.package_queue = image_packages
  319. self.toolchain_packages = toolchain_packages
  320. self.default_task = default_task
  321. self.commands_async.append(self.SUB_PARSE_CONFIG)
  322. self.commands_async.append(self.SUB_BUILD_IMAGE)
  323. self.run_next_command(self.GENERATE_IMAGE)
  324. def build_succeeded_async(self):
  325. self.building = False
  326. def build_failed_async(self):
  327. self.initcmd = None
  328. self.commands_async = []
  329. self.building = False
  330. def cancel_parse(self):
  331. self.runCommand(["stateStop"])
  332. def cancel_build(self, force=False):
  333. if force:
  334. # Force the cooker to stop as quickly as possible
  335. self.runCommand(["stateStop"])
  336. else:
  337. # Wait for tasks to complete before shutting down, this helps
  338. # leave the workdir in a usable state
  339. self.runCommand(["stateShutdown"])
  340. def reset_build(self):
  341. self.build.reset()
  342. def _remove_redundant(self, string):
  343. ret = []
  344. for i in string.split():
  345. if i not in ret:
  346. ret.append(i)
  347. return " ".join(ret)
  348. def get_parameters(self):
  349. # retrieve the parameters from bitbake
  350. params = {}
  351. params["core_base"] = self.runCommand(["getVariable", "COREBASE"]) or ""
  352. hob_layer = params["core_base"] + "/meta-hob"
  353. params["layer"] = self.runCommand(["getVariable", "BBLAYERS"]) or ""
  354. if hob_layer not in params["layer"].split():
  355. params["layer"] += (" " + hob_layer)
  356. params["dldir"] = self.runCommand(["getVariable", "DL_DIR"]) or ""
  357. params["machine"] = self.runCommand(["getVariable", "MACHINE"]) or ""
  358. params["distro"] = self.runCommand(["getVariable", "DISTRO"]) or "defaultsetup"
  359. params["pclass"] = self.runCommand(["getVariable", "PACKAGE_CLASSES"]) or ""
  360. params["sstatedir"] = self.runCommand(["getVariable", "SSTATE_DIR"]) or ""
  361. params["sstatemirror"] = self.runCommand(["getVariable", "SSTATE_MIRROR"]) or ""
  362. num_threads = self.runCommand(["getCpuCount"])
  363. if not num_threads:
  364. num_threads = 1
  365. max_threads = 65536
  366. else:
  367. try:
  368. num_threads = int(num_threads)
  369. max_threads = 16 * num_threads
  370. except:
  371. num_threads = 1
  372. max_threads = 65536
  373. params["max_threads"] = max_threads
  374. bbthread = self.runCommand(["getVariable", "BB_NUMBER_THREADS"])
  375. if not bbthread:
  376. bbthread = num_threads
  377. else:
  378. try:
  379. bbthread = int(bbthread)
  380. except:
  381. bbthread = num_threads
  382. params["bbthread"] = bbthread
  383. pmake = self.runCommand(["getVariable", "PARALLEL_MAKE"])
  384. if not pmake:
  385. pmake = num_threads
  386. elif isinstance(pmake, int):
  387. pass
  388. else:
  389. try:
  390. pmake = int(pmake.lstrip("-j "))
  391. except:
  392. pmake = num_threads
  393. params["pmake"] = "-j %s" % pmake
  394. params["image_addr"] = self.runCommand(["getVariable", "DEPLOY_DIR_IMAGE"]) or ""
  395. image_extra_size = self.runCommand(["getVariable", "IMAGE_ROOTFS_EXTRA_SPACE"])
  396. if not image_extra_size:
  397. image_extra_size = 0
  398. else:
  399. try:
  400. image_extra_size = int(image_extra_size)
  401. except:
  402. image_extra_size = 0
  403. params["image_extra_size"] = image_extra_size
  404. image_rootfs_size = self.runCommand(["getVariable", "IMAGE_ROOTFS_SIZE"])
  405. if not image_rootfs_size:
  406. image_rootfs_size = 0
  407. else:
  408. try:
  409. image_rootfs_size = int(image_rootfs_size)
  410. except:
  411. image_rootfs_size = 0
  412. params["image_rootfs_size"] = image_rootfs_size
  413. image_overhead_factor = self.runCommand(["getVariable", "IMAGE_OVERHEAD_FACTOR"])
  414. if not image_overhead_factor:
  415. image_overhead_factor = 1
  416. else:
  417. try:
  418. image_overhead_factor = float(image_overhead_factor)
  419. except:
  420. image_overhead_factor = 1
  421. params['image_overhead_factor'] = image_overhead_factor
  422. params["incompat_license"] = self._remove_redundant(self.runCommand(["getVariable", "INCOMPATIBLE_LICENSE"]) or "")
  423. params["sdk_machine"] = self.runCommand(["getVariable", "SDKMACHINE"]) or self.runCommand(["getVariable", "SDK_ARCH"]) or ""
  424. params["image_fstypes"] = self._remove_redundant(self.runCommand(["getVariable", "IMAGE_FSTYPES"]) or "")
  425. params["image_types"] = self._remove_redundant(self.runCommand(["getVariable", "IMAGE_TYPES"]) or "")
  426. params["conf_version"] = self.runCommand(["getVariable", "CONF_VERSION"]) or ""
  427. params["lconf_version"] = self.runCommand(["getVariable", "LCONF_VERSION"]) or ""
  428. params["runnable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_IMAGE_TYPES"]) or "")
  429. params["runnable_machine_patterns"] = self._remove_redundant(self.runCommand(["getVariable", "RUNNABLE_MACHINE_PATTERNS"]) or "")
  430. params["deployable_image_types"] = self._remove_redundant(self.runCommand(["getVariable", "DEPLOYABLE_IMAGE_TYPES"]) or "")
  431. params["tmpdir"] = self.runCommand(["getVariable", "TMPDIR"]) or ""
  432. params["distro_version"] = self.runCommand(["getVariable", "DISTRO_VERSION"]) or ""
  433. params["target_os"] = self.runCommand(["getVariable", "TARGET_OS"]) or ""
  434. params["target_arch"] = self.runCommand(["getVariable", "TARGET_ARCH"]) or ""
  435. params["tune_pkgarch"] = self.runCommand(["getVariable", "TUNE_PKGARCH"]) or ""
  436. params["bb_version"] = self.runCommand(["getVariable", "BB_MIN_VERSION"]) or ""
  437. params["default_task"] = self.runCommand(["getVariable", "BB_DEFAULT_TASK"]) or "build"
  438. params["git_proxy_host"] = self.runCommand(["getVariable", "GIT_PROXY_HOST"]) or ""
  439. params["git_proxy_port"] = self.runCommand(["getVariable", "GIT_PROXY_PORT"]) or ""
  440. params["http_proxy"] = self.runCommand(["getVariable", "http_proxy"]) or ""
  441. params["ftp_proxy"] = self.runCommand(["getVariable", "ftp_proxy"]) or ""
  442. params["https_proxy"] = self.runCommand(["getVariable", "https_proxy"]) or ""
  443. params["all_proxy"] = self.runCommand(["getVariable", "all_proxy"]) or ""
  444. params["cvs_proxy_host"] = self.runCommand(["getVariable", "CVS_PROXY_HOST"]) or ""
  445. params["cvs_proxy_port"] = self.runCommand(["getVariable", "CVS_PROXY_PORT"]) or ""
  446. return params