command.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. """
  2. BitBake 'Command' module
  3. Provide an interface to interact with the bitbake server through 'commands'
  4. """
  5. # Copyright (C) 2006-2007 Richard Purdie
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License version 2 as
  9. # published by the Free Software Foundation.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License along
  17. # with this program; if not, write to the Free Software Foundation, Inc.,
  18. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. """
  20. The bitbake server takes 'commands' from its UI/commandline.
  21. Commands are either synchronous or asynchronous.
  22. Async commands return data to the client in the form of events.
  23. Sync commands must only return data through the function return value
  24. and must not trigger events, directly or indirectly.
  25. Commands are queued in a CommandQueue
  26. """
  27. import bb.event
  28. import bb.cooker
  29. class CommandCompleted(bb.event.Event):
  30. pass
  31. class CommandExit(bb.event.Event):
  32. def __init__(self, exitcode):
  33. bb.event.Event.__init__(self)
  34. self.exitcode = int(exitcode)
  35. class CommandFailed(CommandExit):
  36. def __init__(self, message):
  37. self.error = message
  38. CommandExit.__init__(self, 1)
  39. class CommandError(Exception):
  40. pass
  41. class Command:
  42. """
  43. A queue of asynchronous commands for bitbake
  44. """
  45. def __init__(self, cooker):
  46. self.cooker = cooker
  47. self.cmds_sync = CommandsSync()
  48. self.cmds_async = CommandsAsync()
  49. # FIXME Add lock for this
  50. self.currentAsyncCommand = None
  51. def runCommand(self, commandline, ro_only = False):
  52. command = commandline.pop(0)
  53. if hasattr(CommandsSync, command):
  54. # Can run synchronous commands straight away
  55. command_method = getattr(self.cmds_sync, command)
  56. if ro_only:
  57. if not hasattr(command_method, 'readonly') or False == getattr(command_method, 'readonly'):
  58. return None, "Not able to execute not readonly commands in readonly mode"
  59. try:
  60. if getattr(command_method, 'needconfig', False):
  61. self.cooker.updateCacheSync()
  62. result = command_method(self, commandline)
  63. except CommandError as exc:
  64. return None, exc.args[0]
  65. except (Exception, SystemExit):
  66. import traceback
  67. return None, traceback.format_exc()
  68. else:
  69. return result, None
  70. if self.currentAsyncCommand is not None:
  71. return None, "Busy (%s in progress)" % self.currentAsyncCommand[0]
  72. if command not in CommandsAsync.__dict__:
  73. return None, "No such command"
  74. self.currentAsyncCommand = (command, commandline)
  75. self.cooker.configuration.server_register_idlecallback(self.cooker.runCommands, self.cooker)
  76. return True, None
  77. def runAsyncCommand(self):
  78. try:
  79. if self.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
  80. # updateCache will trigger a shutdown of the parser
  81. # and then raise BBHandledException triggering an exit
  82. self.cooker.updateCache()
  83. return False
  84. if self.currentAsyncCommand is not None:
  85. (command, options) = self.currentAsyncCommand
  86. commandmethod = getattr(CommandsAsync, command)
  87. needcache = getattr( commandmethod, "needcache" )
  88. if needcache and self.cooker.state != bb.cooker.state.running:
  89. self.cooker.updateCache()
  90. return True
  91. else:
  92. commandmethod(self.cmds_async, self, options)
  93. return False
  94. else:
  95. return False
  96. except KeyboardInterrupt as exc:
  97. self.finishAsyncCommand("Interrupted")
  98. return False
  99. except SystemExit as exc:
  100. arg = exc.args[0]
  101. if isinstance(arg, str):
  102. self.finishAsyncCommand(arg)
  103. else:
  104. self.finishAsyncCommand("Exited with %s" % arg)
  105. return False
  106. except Exception as exc:
  107. import traceback
  108. if isinstance(exc, bb.BBHandledException):
  109. self.finishAsyncCommand("")
  110. else:
  111. self.finishAsyncCommand(traceback.format_exc())
  112. return False
  113. def finishAsyncCommand(self, msg=None, code=None):
  114. if msg or msg == "":
  115. bb.event.fire(CommandFailed(msg), self.cooker.expanded_data)
  116. elif code:
  117. bb.event.fire(CommandExit(code), self.cooker.expanded_data)
  118. else:
  119. bb.event.fire(CommandCompleted(), self.cooker.expanded_data)
  120. self.currentAsyncCommand = None
  121. self.cooker.finishcommand()
  122. class CommandsSync:
  123. """
  124. A class of synchronous commands
  125. These should run quickly so as not to hurt interactive performance.
  126. These must not influence any running synchronous command.
  127. """
  128. def stateShutdown(self, command, params):
  129. """
  130. Trigger cooker 'shutdown' mode
  131. """
  132. command.cooker.shutdown(False)
  133. def stateForceShutdown(self, command, params):
  134. """
  135. Stop the cooker
  136. """
  137. command.cooker.shutdown(True)
  138. def getAllKeysWithFlags(self, command, params):
  139. """
  140. Returns a dump of the global state. Call with
  141. variable flags to be retrieved as params.
  142. """
  143. flaglist = params[0]
  144. return command.cooker.getAllKeysWithFlags(flaglist)
  145. getAllKeysWithFlags.readonly = True
  146. def getVariable(self, command, params):
  147. """
  148. Read the value of a variable from data
  149. """
  150. varname = params[0]
  151. expand = True
  152. if len(params) > 1:
  153. expand = (params[1] == "True")
  154. return command.cooker.data.getVar(varname, expand)
  155. getVariable.readonly = True
  156. def setVariable(self, command, params):
  157. """
  158. Set the value of variable in data
  159. """
  160. varname = params[0]
  161. value = str(params[1])
  162. command.cooker.data.setVar(varname, value)
  163. def getSetVariable(self, command, params):
  164. """
  165. Read the value of a variable from data and set it into the datastore
  166. which effectively expands and locks the value.
  167. """
  168. varname = params[0]
  169. result = self.getVariable(command, params)
  170. command.cooker.data.setVar(varname, result)
  171. return result
  172. def setConfig(self, command, params):
  173. """
  174. Set the value of variable in configuration
  175. """
  176. varname = params[0]
  177. value = str(params[1])
  178. setattr(command.cooker.configuration, varname, value)
  179. def enableDataTracking(self, command, params):
  180. """
  181. Enable history tracking for variables
  182. """
  183. command.cooker.enableDataTracking()
  184. def disableDataTracking(self, command, params):
  185. """
  186. Disable history tracking for variables
  187. """
  188. command.cooker.disableDataTracking()
  189. def setPrePostConfFiles(self, command, params):
  190. prefiles = params[0].split()
  191. postfiles = params[1].split()
  192. command.cooker.configuration.prefile = prefiles
  193. command.cooker.configuration.postfile = postfiles
  194. setPrePostConfFiles.needconfig = False
  195. def getCpuCount(self, command, params):
  196. """
  197. Get the CPU count on the bitbake server
  198. """
  199. return bb.utils.cpu_count()
  200. getCpuCount.readonly = True
  201. getCpuCount.needconfig = False
  202. def matchFile(self, command, params):
  203. fMatch = params[0]
  204. return command.cooker.matchFile(fMatch)
  205. matchFile.needconfig = False
  206. def generateNewImage(self, command, params):
  207. image = params[0]
  208. base_image = params[1]
  209. package_queue = params[2]
  210. timestamp = params[3]
  211. description = params[4]
  212. return command.cooker.generateNewImage(image, base_image,
  213. package_queue, timestamp, description)
  214. def ensureDir(self, command, params):
  215. directory = params[0]
  216. bb.utils.mkdirhier(directory)
  217. ensureDir.needconfig = False
  218. def setVarFile(self, command, params):
  219. """
  220. Save a variable in a file; used for saving in a configuration file
  221. """
  222. var = params[0]
  223. val = params[1]
  224. default_file = params[2]
  225. op = params[3]
  226. command.cooker.modifyConfigurationVar(var, val, default_file, op)
  227. setVarFile.needconfig = False
  228. def removeVarFile(self, command, params):
  229. """
  230. Remove a variable declaration from a file
  231. """
  232. var = params[0]
  233. command.cooker.removeConfigurationVar(var)
  234. removeVarFile.needconfig = False
  235. def createConfigFile(self, command, params):
  236. """
  237. Create an extra configuration file
  238. """
  239. name = params[0]
  240. command.cooker.createConfigFile(name)
  241. createConfigFile.needconfig = False
  242. def setEventMask(self, command, params):
  243. handlerNum = params[0]
  244. llevel = params[1]
  245. debug_domains = params[2]
  246. mask = params[3]
  247. return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
  248. setEventMask.needconfig = False
  249. setEventMask.readonly = True
  250. def setFeatures(self, command, params):
  251. """
  252. Set the cooker features to include the passed list of features
  253. """
  254. features = params[0]
  255. command.cooker.setFeatures(features)
  256. setFeatures.needconfig = False
  257. # although we change the internal state of the cooker, this is transparent since
  258. # we always take and leave the cooker in state.initial
  259. setFeatures.readonly = True
  260. def updateConfig(self, command, params):
  261. options = params[0]
  262. environment = params[1]
  263. command.cooker.updateConfigOpts(options, environment)
  264. updateConfig.needconfig = False
  265. class CommandsAsync:
  266. """
  267. A class of asynchronous commands
  268. These functions communicate via generated events.
  269. Any function that requires metadata parsing should be here.
  270. """
  271. def buildFile(self, command, params):
  272. """
  273. Build a single specified .bb file
  274. """
  275. bfile = params[0]
  276. task = params[1]
  277. command.cooker.buildFile(bfile, task)
  278. buildFile.needcache = False
  279. def buildTargets(self, command, params):
  280. """
  281. Build a set of targets
  282. """
  283. pkgs_to_build = params[0]
  284. task = params[1]
  285. command.cooker.buildTargets(pkgs_to_build, task)
  286. buildTargets.needcache = True
  287. def generateDepTreeEvent(self, command, params):
  288. """
  289. Generate an event containing the dependency information
  290. """
  291. pkgs_to_build = params[0]
  292. task = params[1]
  293. command.cooker.generateDepTreeEvent(pkgs_to_build, task)
  294. command.finishAsyncCommand()
  295. generateDepTreeEvent.needcache = True
  296. def generateDotGraph(self, command, params):
  297. """
  298. Dump dependency information to disk as .dot files
  299. """
  300. pkgs_to_build = params[0]
  301. task = params[1]
  302. command.cooker.generateDotGraphFiles(pkgs_to_build, task)
  303. command.finishAsyncCommand()
  304. generateDotGraph.needcache = True
  305. def generateTargetsTree(self, command, params):
  306. """
  307. Generate a tree of buildable targets.
  308. If klass is provided ensure all recipes that inherit the class are
  309. included in the package list.
  310. If pkg_list provided use that list (plus any extras brought in by
  311. klass) rather than generating a tree for all packages.
  312. """
  313. klass = params[0]
  314. pkg_list = params[1]
  315. command.cooker.generateTargetsTree(klass, pkg_list)
  316. command.finishAsyncCommand()
  317. generateTargetsTree.needcache = True
  318. def findCoreBaseFiles(self, command, params):
  319. """
  320. Find certain files in COREBASE directory. i.e. Layers
  321. """
  322. subdir = params[0]
  323. filename = params[1]
  324. command.cooker.findCoreBaseFiles(subdir, filename)
  325. command.finishAsyncCommand()
  326. findCoreBaseFiles.needcache = False
  327. def findConfigFiles(self, command, params):
  328. """
  329. Find config files which provide appropriate values
  330. for the passed configuration variable. i.e. MACHINE
  331. """
  332. varname = params[0]
  333. command.cooker.findConfigFiles(varname)
  334. command.finishAsyncCommand()
  335. findConfigFiles.needcache = False
  336. def findFilesMatchingInDir(self, command, params):
  337. """
  338. Find implementation files matching the specified pattern
  339. in the requested subdirectory of a BBPATH
  340. """
  341. pattern = params[0]
  342. directory = params[1]
  343. command.cooker.findFilesMatchingInDir(pattern, directory)
  344. command.finishAsyncCommand()
  345. findFilesMatchingInDir.needcache = False
  346. def findConfigFilePath(self, command, params):
  347. """
  348. Find the path of the requested configuration file
  349. """
  350. configfile = params[0]
  351. command.cooker.findConfigFilePath(configfile)
  352. command.finishAsyncCommand()
  353. findConfigFilePath.needcache = False
  354. def showVersions(self, command, params):
  355. """
  356. Show the currently selected versions
  357. """
  358. command.cooker.showVersions()
  359. command.finishAsyncCommand()
  360. showVersions.needcache = True
  361. def showEnvironmentTarget(self, command, params):
  362. """
  363. Print the environment of a target recipe
  364. (needs the cache to work out which recipe to use)
  365. """
  366. pkg = params[0]
  367. command.cooker.showEnvironment(None, pkg)
  368. command.finishAsyncCommand()
  369. showEnvironmentTarget.needcache = True
  370. def showEnvironment(self, command, params):
  371. """
  372. Print the standard environment
  373. or if specified the environment for a specified recipe
  374. """
  375. bfile = params[0]
  376. command.cooker.showEnvironment(bfile)
  377. command.finishAsyncCommand()
  378. showEnvironment.needcache = False
  379. def parseFiles(self, command, params):
  380. """
  381. Parse the .bb files
  382. """
  383. command.cooker.updateCache()
  384. command.finishAsyncCommand()
  385. parseFiles.needcache = True
  386. def compareRevisions(self, command, params):
  387. """
  388. Parse the .bb files
  389. """
  390. if bb.fetch.fetcher_compare_revisions(command.cooker.data):
  391. command.finishAsyncCommand(code=1)
  392. else:
  393. command.finishAsyncCommand()
  394. compareRevisions.needcache = True
  395. def triggerEvent(self, command, params):
  396. """
  397. Trigger a certain event
  398. """
  399. event = params[0]
  400. bb.event.fire(eval(event), command.cooker.data)
  401. command.currentAsyncCommand = None
  402. triggerEvent.needcache = False
  403. def resetCooker(self, command, params):
  404. """
  405. Reset the cooker to its initial state, thus forcing a reparse for
  406. any async command that has the needcache property set to True
  407. """
  408. command.cooker.reset()
  409. command.finishAsyncCommand()
  410. resetCooker.needcache = False