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