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