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.py451
1 files changed, 451 insertions, 0 deletions
diff --git a/bitbake/lib/bb/command.py b/bitbake/lib/bb/command.py
new file mode 100644
index 0000000000..60f9ac08aa
--- /dev/null
+++ b/bitbake/lib/bb/command.py
@@ -0,0 +1,451 @@
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.cooker.state in (bb.cooker.state.error, bb.cooker.state.shutdown, bb.cooker.state.forceshutdown):
90 # updateCache will trigger a shutdown of the parser
91 # and then raise BBHandledException triggering an exit
92 self.cooker.updateCache()
93 return False
94 if self.currentAsyncCommand is not None:
95 (command, options) = self.currentAsyncCommand
96 commandmethod = getattr(CommandsAsync, command)
97 needcache = getattr( commandmethod, "needcache" )
98 if needcache and self.cooker.state != bb.cooker.state.running:
99 self.cooker.updateCache()
100 return True
101 else:
102 commandmethod(self.cmds_async, self, options)
103 return False
104 else:
105 return False
106 except KeyboardInterrupt as exc:
107 self.finishAsyncCommand("Interrupted")
108 return False
109 except SystemExit as exc:
110 arg = exc.args[0]
111 if isinstance(arg, basestring):
112 self.finishAsyncCommand(arg)
113 else:
114 self.finishAsyncCommand("Exited with %s" % arg)
115 return False
116 except Exception as exc:
117 import traceback
118 if isinstance(exc, bb.BBHandledException):
119 self.finishAsyncCommand("")
120 else:
121 self.finishAsyncCommand(traceback.format_exc())
122 return False
123
124 def finishAsyncCommand(self, msg=None, code=None):
125 if msg or msg == "":
126 bb.event.fire(CommandFailed(msg), self.cooker.event_data)
127 elif code:
128 bb.event.fire(CommandExit(code), self.cooker.event_data)
129 else:
130 bb.event.fire(CommandCompleted(), self.cooker.event_data)
131 self.currentAsyncCommand = None
132 self.cooker.finishcommand()
133
134class CommandsSync:
135 """
136 A class of synchronous commands
137 These should run quickly so as not to hurt interactive performance.
138 These must not influence any running synchronous command.
139 """
140
141 def stateShutdown(self, command, params):
142 """
143 Trigger cooker 'shutdown' mode
144 """
145 command.cooker.shutdown(False)
146
147 def stateForceShutdown(self, command, params):
148 """
149 Stop the cooker
150 """
151 command.cooker.shutdown(True)
152
153 def getAllKeysWithFlags(self, command, params):
154 """
155 Returns a dump of the global state. Call with
156 variable flags to be retrieved as params.
157 """
158 flaglist = params[0]
159 return command.cooker.getAllKeysWithFlags(flaglist)
160 getAllKeysWithFlags.readonly = True
161
162 def getVariable(self, command, params):
163 """
164 Read the value of a variable from data
165 """
166 varname = params[0]
167 expand = True
168 if len(params) > 1:
169 expand = (params[1] == "True")
170
171 return command.cooker.data.getVar(varname, expand)
172 getVariable.readonly = True
173
174 def setVariable(self, command, params):
175 """
176 Set the value of variable in data
177 """
178 varname = params[0]
179 value = str(params[1])
180 command.cooker.data.setVar(varname, value)
181
182 def setConfig(self, command, params):
183 """
184 Set the value of variable in configuration
185 """
186 varname = params[0]
187 value = str(params[1])
188 setattr(command.cooker.configuration, varname, value)
189
190 def enableDataTracking(self, command, params):
191 """
192 Enable history tracking for variables
193 """
194 command.cooker.enableDataTracking()
195
196 def disableDataTracking(self, command, params):
197 """
198 Disable history tracking for variables
199 """
200 command.cooker.disableDataTracking()
201
202 def setPrePostConfFiles(self, command, params):
203 prefiles = params[0].split()
204 postfiles = params[1].split()
205 command.cooker.configuration.prefile = prefiles
206 command.cooker.configuration.postfile = postfiles
207
208 def getCpuCount(self, command, params):
209 """
210 Get the CPU count on the bitbake server
211 """
212 return bb.utils.cpu_count()
213 getCpuCount.readonly = True
214
215 def matchFile(self, command, params):
216 fMatch = params[0]
217 return command.cooker.matchFile(fMatch)
218
219 def generateNewImage(self, command, params):
220 image = params[0]
221 base_image = params[1]
222 package_queue = params[2]
223 timestamp = params[3]
224 description = params[4]
225 return command.cooker.generateNewImage(image, base_image,
226 package_queue, timestamp, description)
227
228 def ensureDir(self, command, params):
229 directory = params[0]
230 bb.utils.mkdirhier(directory)
231
232 def setVarFile(self, command, params):
233 """
234 Save a variable in a file; used for saving in a configuration file
235 """
236 var = params[0]
237 val = params[1]
238 default_file = params[2]
239 op = params[3]
240 command.cooker.modifyConfigurationVar(var, val, default_file, op)
241
242 def removeVarFile(self, command, params):
243 """
244 Remove a variable declaration from a file
245 """
246 var = params[0]
247 command.cooker.removeConfigurationVar(var)
248
249 def createConfigFile(self, command, params):
250 """
251 Create an extra configuration file
252 """
253 name = params[0]
254 command.cooker.createConfigFile(name)
255
256 def setEventMask(self, command, params):
257 handlerNum = params[0]
258 llevel = params[1]
259 debug_domains = params[2]
260 mask = params[3]
261 return bb.event.set_UIHmask(handlerNum, llevel, debug_domains, mask)
262
263 def setFeatures(self, command, params):
264 """
265 Set the cooker features to include the passed list of features
266 """
267 features = params[0]
268 command.cooker.setFeatures(features)
269
270 # although we change the internal state of the cooker, this is transparent since
271 # we always take and leave the cooker in state.initial
272 setFeatures.readonly = True
273
274 def updateConfig(self, command, params):
275 options = params[0]
276 command.cooker.updateConfigOpts(options)
277
278class CommandsAsync:
279 """
280 A class of asynchronous commands
281 These functions communicate via generated events.
282 Any function that requires metadata parsing should be here.
283 """
284
285 def buildFile(self, command, params):
286 """
287 Build a single specified .bb file
288 """
289 bfile = params[0]
290 task = params[1]
291
292 command.cooker.buildFile(bfile, task)
293 buildFile.needcache = False
294
295 def buildTargets(self, command, params):
296 """
297 Build a set of targets
298 """
299 pkgs_to_build = params[0]
300 task = params[1]
301
302 command.cooker.buildTargets(pkgs_to_build, task)
303 buildTargets.needcache = True
304
305 def generateDepTreeEvent(self, command, params):
306 """
307 Generate an event containing the dependency information
308 """
309 pkgs_to_build = params[0]
310 task = params[1]
311
312 command.cooker.generateDepTreeEvent(pkgs_to_build, task)
313 command.finishAsyncCommand()
314 generateDepTreeEvent.needcache = True
315
316 def generateDotGraph(self, command, params):
317 """
318 Dump dependency information to disk as .dot files
319 """
320 pkgs_to_build = params[0]
321 task = params[1]
322
323 command.cooker.generateDotGraphFiles(pkgs_to_build, task)
324 command.finishAsyncCommand()
325 generateDotGraph.needcache = True
326
327 def generateTargetsTree(self, command, params):
328 """
329 Generate a tree of buildable targets.
330 If klass is provided ensure all recipes that inherit the class are
331 included in the package list.
332 If pkg_list provided use that list (plus any extras brought in by
333 klass) rather than generating a tree for all packages.
334 """
335 klass = params[0]
336 pkg_list = params[1]
337
338 command.cooker.generateTargetsTree(klass, pkg_list)
339 command.finishAsyncCommand()
340 generateTargetsTree.needcache = True
341
342 def findCoreBaseFiles(self, command, params):
343 """
344 Find certain files in COREBASE directory. i.e. Layers
345 """
346 subdir = params[0]
347 filename = params[1]
348
349 command.cooker.findCoreBaseFiles(subdir, filename)
350 command.finishAsyncCommand()
351 findCoreBaseFiles.needcache = False
352
353 def findConfigFiles(self, command, params):
354 """
355 Find config files which provide appropriate values
356 for the passed configuration variable. i.e. MACHINE
357 """
358 varname = params[0]
359
360 command.cooker.findConfigFiles(varname)
361 command.finishAsyncCommand()
362 findConfigFiles.needcache = False
363
364 def findFilesMatchingInDir(self, command, params):
365 """
366 Find implementation files matching the specified pattern
367 in the requested subdirectory of a BBPATH
368 """
369 pattern = params[0]
370 directory = params[1]
371
372 command.cooker.findFilesMatchingInDir(pattern, directory)
373 command.finishAsyncCommand()
374 findFilesMatchingInDir.needcache = False
375
376 def findConfigFilePath(self, command, params):
377 """
378 Find the path of the requested configuration file
379 """
380 configfile = params[0]
381
382 command.cooker.findConfigFilePath(configfile)
383 command.finishAsyncCommand()
384 findConfigFilePath.needcache = False
385
386 def showVersions(self, command, params):
387 """
388 Show the currently selected versions
389 """
390 command.cooker.showVersions()
391 command.finishAsyncCommand()
392 showVersions.needcache = True
393
394 def showEnvironmentTarget(self, command, params):
395 """
396 Print the environment of a target recipe
397 (needs the cache to work out which recipe to use)
398 """
399 pkg = params[0]
400
401 command.cooker.showEnvironment(None, pkg)
402 command.finishAsyncCommand()
403 showEnvironmentTarget.needcache = True
404
405 def showEnvironment(self, command, params):
406 """
407 Print the standard environment
408 or if specified the environment for a specified recipe
409 """
410 bfile = params[0]
411
412 command.cooker.showEnvironment(bfile)
413 command.finishAsyncCommand()
414 showEnvironment.needcache = False
415
416 def parseFiles(self, command, params):
417 """
418 Parse the .bb files
419 """
420 command.cooker.updateCache()
421 command.finishAsyncCommand()
422 parseFiles.needcache = True
423
424 def compareRevisions(self, command, params):
425 """
426 Parse the .bb files
427 """
428 if bb.fetch.fetcher_compare_revisions(command.cooker.data):
429 command.finishAsyncCommand(code=1)
430 else:
431 command.finishAsyncCommand()
432 compareRevisions.needcache = True
433
434 def triggerEvent(self, command, params):
435 """
436 Trigger a certain event
437 """
438 event = params[0]
439 bb.event.fire(eval(event), command.cooker.data)
440 command.currentAsyncCommand = None
441 triggerEvent.needcache = False
442
443 def resetCooker(self, command, params):
444 """
445 Reset the cooker to its initial state, thus forcing a reparse for
446 any async command that has the needcache property set to True
447 """
448 command.cooker.reset()
449 command.finishAsyncCommand()
450 resetCooker.needcache = False
451