Package SCons :: Package Script :: Module Interactive
[hide private]
[frames] | no frames]

Source Code for Module SCons.Script.Interactive

  1  # 
  2  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation 
  3  # 
  4  # Permission is hereby granted, free of charge, to any person obtaining 
  5  # a copy of this software and associated documentation files (the 
  6  # "Software"), to deal in the Software without restriction, including 
  7  # without limitation the rights to use, copy, modify, merge, publish, 
  8  # distribute, sublicense, and/or sell copies of the Software, and to 
  9  # permit persons to whom the Software is furnished to do so, subject to 
 10  # the following conditions: 
 11  # 
 12  # The above copyright notice and this permission notice shall be included 
 13  # in all copies or substantial portions of the Software. 
 14  # 
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
 16  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
 17  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 18  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
 19  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
 20  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
 21  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
 22  # 
 23   
 24  __revision__ = "src/engine/SCons/Script/Interactive.py 2725 2008/03/31 12:52:02 knight" 
 25   
 26  __doc__ = """ 
 27  SCons interactive mode 
 28  """ 
 29   
 30  # TODO: 
 31  # 
 32  # This has the potential to grow into something with a really big life 
 33  # of its own, which might or might not be a good thing.  Nevertheless, 
 34  # here are some enhancements that will probably be requested some day 
 35  # and are worth keeping in mind (assuming this takes off): 
 36  #  
 37  # - A command to re-read / re-load the SConscript files.  This may 
 38  #   involve allowing people to specify command-line options (e.g. -f, 
 39  #   -I, --no-site-dir) that affect how the SConscript files are read. 
 40  # 
 41  # - Additional command-line options on the "build" command. 
 42  # 
 43  #   Of the supported options that seemed to make sense (after a quick 
 44  #   pass through the list), the ones that seemed likely enough to be 
 45  #   used are listed in the man page and have explicit test scripts. 
 46  # 
 47  #   These had code changed in Script/Main.py to support them, but didn't 
 48  #   seem likely to be used regularly, so had no test scripts added: 
 49  # 
 50  #       build --diskcheck=* 
 51  #       build --implicit-cache=* 
 52  #       build --implicit-deps-changed=* 
 53  #       build --implicit-deps-unchanged=* 
 54  # 
 55  #   These look like they should "just work" with no changes to the 
 56  #   existing code, but like those above, look unlikely to be used and 
 57  #   therefore had no test scripts added: 
 58  # 
 59  #       build --random 
 60  # 
 61  #   These I'm not sure about.  They might be useful for individual 
 62  #   "build" commands, and may even work, but they seem unlikely enough 
 63  #   that we'll wait until they're requested before spending any time on 
 64  #   writing test scripts for them, or investigating whether they work. 
 65  # 
 66  #       build -q [???  is there a useful analog to the exit status?] 
 67  #       build --duplicate= 
 68  #       build --profile= 
 69  #       build --max-drift= 
 70  #       build --warn=* 
 71  #       build --Y 
 72  # 
 73  # - Most of the SCons command-line options that the "build" command 
 74  #   supports should be settable as default options that apply to all 
 75  #   subsequent "build" commands.  Maybe a "set {option}" command that 
 76  #   maps to "SetOption('{option}')". 
 77  # 
 78  # - Need something in the 'help' command that prints the -h output. 
 79  # 
 80  # - A command to run the configure subsystem separately (must see how 
 81  #   this interacts with the new automake model). 
 82  # 
 83  # - Command-line completion of target names; maybe even of SCons options? 
 84  #   Completion is something that's supported by the Python cmd module, 
 85  #   so this should be doable without too much trouble. 
 86  # 
 87   
 88  import cmd 
 89  import copy 
 90  import os 
 91  import re 
 92  import shlex 
 93  import string 
 94  import sys 
 95   
 96  try: 
 97      import readline 
 98  except ImportError: 
 99      pass 
100   
101  from SCons.Debug import Trace 
102   
103 -class SConsInteractiveCmd(cmd.Cmd):
104 """\ 105 build [TARGETS] Build the specified TARGETS and their dependencies. 106 'b' is a synonym. 107 clean [TARGETS] Clean (remove) the specified TARGETS and their 108 dependencies. 'c' is a synonym. 109 exit Exit SCons interactive mode. 110 help [COMMAND] Prints help for the specified COMMAND. 'h' and 111 '?' are synonyms. 112 shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!' 113 are synonyms. 114 version Prints SCons version information. 115 """ 116 117 synonyms = { 118 'b' : 'build', 119 'c' : 'clean', 120 'h' : 'help', 121 'scons' : 'build', 122 'sh' : 'shell', 123 } 124
125 - def __init__(self, **kw):
126 cmd.Cmd.__init__(self) 127 for key, val in kw.items(): 128 setattr(self, key, val) 129 130 if sys.platform == 'win32': 131 self.shell_variable = 'COMSPEC' 132 else: 133 self.shell_variable = 'SHELL'
134
135 - def default(self, argv):
136 print "*** Unknown command: %s" % argv[0]
137
138 - def onecmd(self, line):
139 line = string.strip(line) 140 if not line: 141 print self.lastcmd 142 return self.emptyline() 143 self.lastcmd = line 144 if line[0] == '!': 145 line = 'shell ' + line[1:] 146 elif line[0] == '?': 147 line = 'help ' + line[1:] 148 argv = shlex.split(line) 149 argv[0] = self.synonyms.get(argv[0], argv[0]) 150 if not argv[0]: 151 return self.default(line) 152 else: 153 try: 154 func = getattr(self, 'do_' + argv[0]) 155 except AttributeError: 156 return self.default(argv) 157 return func(argv)
158
159 - def do_build(self, argv):
160 """\ 161 build [TARGETS] Build the specified TARGETS and their 162 dependencies. 'b' is a synonym. 163 """ 164 import SCons.SConsign 165 import SCons.Script.Main 166 167 options = copy.deepcopy(self.options) 168 169 options, targets = self.parser.parse_args(argv[1:], values=options) 170 171 SCons.Script.COMMAND_LINE_TARGETS = targets 172 173 if targets: 174 SCons.Script.BUILD_TARGETS = targets 175 else: 176 # If the user didn't specify any targets on the command line, 177 # use the list of default targets. 178 SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default 179 180 nodes = SCons.Script.Main._build_targets(self.fs, 181 options, 182 targets, 183 self.target_top) 184 185 if not nodes: 186 return 187 188 # Clean up so that we can perform the next build correctly. 189 # 190 # We do this by walking over all the children of the targets, 191 # and clearing their state. 192 # 193 # We currently have to re-scan each node to find their 194 # children, because built nodes have already been partially 195 # cleared and don't remember their children. (In scons 196 # 0.96.1 and earlier, this wasn't the case, and we didn't 197 # have to re-scan the nodes.) 198 # 199 # Because we have to re-scan each node, we can't clear the 200 # nodes as we walk over them, because we may end up rescanning 201 # a cleared node as we scan a later node. Therefore, only 202 # store the list of nodes that need to be cleared as we walk 203 # the tree, and clear them in a separate pass. 204 # 205 # XXX: Someone more familiar with the inner workings of scons 206 # may be able to point out a more efficient way to do this. 207 208 SCons.Script.Main.progress_display("scons: Clearing cached node information ...") 209 210 seen_nodes = {} 211 212 def get_unseen_children(node, parent, seen_nodes=seen_nodes): 213 def is_unseen(node, seen_nodes=seen_nodes): 214 return not seen_nodes.has_key(node)
215 return filter(is_unseen, node.children(scan=1))
216 217 def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes): 218 seen_nodes[node] = 1 219 220 # If this file is in a VariantDir and has a 221 # corresponding source file in the source tree, remember the 222 # node in the source tree, too. This is needed in 223 # particular to clear cached implicit dependencies on the 224 # source file, since the scanner will scan it if the 225 # VariantDir was created with duplicate=0. 226 try: 227 rfile_method = node.rfile 228 except AttributeError: 229 return 230 else: 231 rfile = rfile_method() 232 if rfile != node: 233 seen_nodes[rfile] = 1 234 235 for node in nodes: 236 walker = SCons.Node.Walker(node, 237 kids_func=get_unseen_children, 238 eval_func=add_to_seen_nodes) 239 n = walker.next() 240 while n: 241 n = walker.next() 242 243 for node in seen_nodes.keys(): 244 # Call node.clear() to clear most of the state 245 node.clear() 246 # node.clear() doesn't reset node.state, so call 247 # node.set_state() to reset it manually 248 node.set_state(SCons.Node.no_state) 249 node.implicit = None 250 251 SCons.SConsign.Reset() 252 SCons.Script.Main.progress_display("scons: done clearing node information.") 253
254 - def do_clean(self, argv):
255 """\ 256 clean [TARGETS] Clean (remove) the specified TARGETS 257 and their dependencies. 'c' is a synonym. 258 """ 259 return self.do_build(['build', '--clean'] + argv[1:])
260
261 - def do_EOF(self, argv):
262 print 263 self.do_exit(argv)
264
265 - def _do_one_help(self, arg):
266 try: 267 # If help_<arg>() exists, then call it. 268 func = getattr(self, 'help_' + arg) 269 except AttributeError: 270 try: 271 func = getattr(self, 'do_' + arg) 272 except AttributeError: 273 doc = None 274 else: 275 doc = self._doc_to_help(func) 276 if doc: 277 sys.stdout.write(doc + '\n') 278 sys.stdout.flush() 279 else: 280 doc = self.strip_initial_spaces(func()) 281 if doc: 282 sys.stdout.write(doc + '\n') 283 sys.stdout.flush()
284
285 - def _doc_to_help(self, obj):
286 doc = obj.__doc__ 287 if doc is None: 288 return '' 289 return self._strip_initial_spaces(doc)
290
291 - def _strip_initial_spaces(self, s):
292 #lines = s.split('\n') 293 lines = string.split(s, '\n') 294 spaces = re.match(' *', lines[0]).group(0) 295 #def strip_spaces(l): 296 # if l.startswith(spaces): 297 # l = l[len(spaces):] 298 # return l 299 #return '\n'.join([ strip_spaces(l) for l in lines ]) 300 def strip_spaces(l, spaces=spaces): 301 if l[:len(spaces)] == spaces: 302 l = l[len(spaces):] 303 return l
304 lines = map(strip_spaces, lines) 305 return string.join(lines, '\n') 306
307 - def do_exit(self, argv):
308 """\ 309 exit Exit SCons interactive mode. 310 """ 311 sys.exit(0)
312
313 - def do_help(self, argv):
314 """\ 315 help [COMMAND] Prints help for the specified COMMAND. 'h' 316 and '?' are synonyms. 317 """ 318 if argv[1:]: 319 for arg in argv[1:]: 320 if self._do_one_help(arg): 321 break 322 else: 323 # If bare 'help' is called, print this class's doc 324 # string (if it has one). 325 doc = self._doc_to_help(self.__class__) 326 if doc: 327 sys.stdout.write(doc + '\n') 328 sys.stdout.flush()
329
330 - def do_shell(self, argv):
331 """\ 332 shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and 333 '!' are synonyms. 334 """ 335 import subprocess 336 argv = argv[1:] 337 if not argv: 338 argv = os.environ[self.shell_variable] 339 try: 340 p = subprocess.Popen(argv) 341 except EnvironmentError, e: 342 sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror)) 343 else: 344 p.wait()
345
346 - def do_version(self, argv):
347 """\ 348 version Prints SCons version information. 349 """ 350 sys.stdout.write(self.parser.version + '\n')
351
352 -def interact(fs, parser, options, targets, target_top):
353 c = SConsInteractiveCmd(prompt = 'scons>>> ', 354 fs = fs, 355 parser = parser, 356 options = options, 357 targets = targets, 358 target_top = target_top) 359 c.cmdloop()
360