2
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation
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:
12
# The above copyright notice and this permission notice shall be included
13
# in all copies or substantial portions of the Software.
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.
23
__revision__ = "src/engine/SCons/Script/Interactive.py 2013/03/03 09:48:35 garyo"
26
SCons interactive mode
31
# This has the potential to grow into something with a really big life
32
# of its own, which might or might not be a good thing. Nevertheless,
33
# here are some enhancements that will probably be requested some day
34
# and are worth keeping in mind (assuming this takes off):
36
# - A command to re-read / re-load the SConscript files. This may
37
# involve allowing people to specify command-line options (e.g. -f,
38
# -I, --no-site-dir) that affect how the SConscript files are read.
40
# - Additional command-line options on the "build" command.
42
# Of the supported options that seemed to make sense (after a quick
43
# pass through the list), the ones that seemed likely enough to be
44
# used are listed in the man page and have explicit test scripts.
46
# These had code changed in Script/Main.py to support them, but didn't
47
# seem likely to be used regularly, so had no test scripts added:
50
# build --implicit-cache=*
51
# build --implicit-deps-changed=*
52
# build --implicit-deps-unchanged=*
54
# These look like they should "just work" with no changes to the
55
# existing code, but like those above, look unlikely to be used and
56
# therefore had no test scripts added:
60
# These I'm not sure about. They might be useful for individual
61
# "build" commands, and may even work, but they seem unlikely enough
62
# that we'll wait until they're requested before spending any time on
63
# writing test scripts for them, or investigating whether they work.
65
# build -q [??? is there a useful analog to the exit status?]
72
# - Most of the SCons command-line options that the "build" command
73
# supports should be settable as default options that apply to all
74
# subsequent "build" commands. Maybe a "set {option}" command that
75
# maps to "SetOption('{option}')".
77
# - Need something in the 'help' command that prints the -h output.
79
# - A command to run the configure subsystem separately (must see how
80
# this interacts with the new automake model).
82
# - Command-line completion of target names; maybe even of SCons options?
83
# Completion is something that's supported by the Python cmd module,
84
# so this should be doable without too much trouble.
99
class SConsInteractiveCmd(cmd.Cmd):
101
build [TARGETS] Build the specified TARGETS and their dependencies.
103
clean [TARGETS] Clean (remove) the specified TARGETS and their
104
dependencies. 'c' is a synonym.
105
exit Exit SCons interactive mode.
106
help [COMMAND] Prints help for the specified COMMAND. 'h' and
108
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!'
110
version Prints SCons version information.
121
def __init__(self, **kw):
122
cmd.Cmd.__init__(self)
123
for key, val in kw.items():
124
setattr(self, key, val)
126
if sys.platform == 'win32':
127
self.shell_variable = 'COMSPEC'
129
self.shell_variable = 'SHELL'
131
def default(self, argv):
132
print "*** Unknown command: %s" % argv[0]
134
def onecmd(self, line):
138
return self.emptyline()
141
line = 'shell ' + line[1:]
143
line = 'help ' + line[1:]
145
line = line.replace('\\', '\\\\')
146
argv = shlex.split(line)
147
argv[0] = self.synonyms.get(argv[0], argv[0])
149
return self.default(line)
152
func = getattr(self, 'do_' + argv[0])
153
except AttributeError:
154
return self.default(argv)
157
def do_build(self, argv):
159
build [TARGETS] Build the specified TARGETS and their
160
dependencies. 'b' is a synonym.
163
import SCons.SConsign
164
import SCons.Script.Main
166
options = copy.deepcopy(self.options)
168
options, targets = self.parser.parse_args(argv[1:], values=options)
170
SCons.Script.COMMAND_LINE_TARGETS = targets
173
SCons.Script.BUILD_TARGETS = targets
175
# If the user didn't specify any targets on the command line,
176
# use the list of default targets.
177
SCons.Script.BUILD_TARGETS = SCons.Script._build_plus_default
179
nodes = SCons.Script.Main._build_targets(self.fs,
187
# Call each of the Node's alter_targets() methods, which may
188
# provide additional targets that ended up as part of the build
189
# (the canonical example being a VariantDir() when we're building
190
# from a source directory) and which we therefore need their
191
# state cleared, too.
194
x.extend(n.alter_targets()[0])
197
# Clean up so that we can perform the next build correctly.
199
# We do this by walking over all the children of the targets,
200
# and clearing their state.
202
# We currently have to re-scan each node to find their
203
# children, because built nodes have already been partially
204
# cleared and don't remember their children. (In scons
205
# 0.96.1 and earlier, this wasn't the case, and we didn't
206
# have to re-scan the nodes.)
208
# Because we have to re-scan each node, we can't clear the
209
# nodes as we walk over them, because we may end up rescanning
210
# a cleared node as we scan a later node. Therefore, only
211
# store the list of nodes that need to be cleared as we walk
212
# the tree, and clear them in a separate pass.
214
# XXX: Someone more familiar with the inner workings of scons
215
# may be able to point out a more efficient way to do this.
217
SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
221
def get_unseen_children(node, parent, seen_nodes=seen_nodes):
222
def is_unseen(node, seen_nodes=seen_nodes):
223
return node not in seen_nodes
224
return list(filter(is_unseen, node.children(scan=1)))
226
def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
229
# If this file is in a VariantDir and has a
230
# corresponding source file in the source tree, remember the
231
# node in the source tree, too. This is needed in
232
# particular to clear cached implicit dependencies on the
233
# source file, since the scanner will scan it if the
234
# VariantDir was created with duplicate=0.
236
rfile_method = node.rfile
237
except AttributeError:
240
rfile = rfile_method()
242
seen_nodes[rfile] = 1
245
walker = SCons.Node.Walker(node,
246
kids_func=get_unseen_children,
247
eval_func=add_to_seen_nodes)
248
n = walker.get_next()
250
n = walker.get_next()
252
for node in seen_nodes.keys():
253
# Call node.clear() to clear most of the state
255
# node.clear() doesn't reset node.state, so call
256
# node.set_state() to reset it manually
257
node.set_state(SCons.Node.no_state)
260
# Debug: Uncomment to verify that all Taskmaster reference
261
# counts have been reset to zero.
262
#if node.ref_count != 0:
263
# from SCons.Debug import Trace
264
# Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
266
SCons.SConsign.Reset()
267
SCons.Script.Main.progress_display("scons: done clearing node information.")
269
def do_clean(self, argv):
271
clean [TARGETS] Clean (remove) the specified TARGETS
272
and their dependencies. 'c' is a synonym.
274
return self.do_build(['build', '--clean'] + argv[1:])
276
def do_EOF(self, argv):
280
def _do_one_help(self, arg):
282
# If help_<arg>() exists, then call it.
283
func = getattr(self, 'help_' + arg)
284
except AttributeError:
286
func = getattr(self, 'do_' + arg)
287
except AttributeError:
290
doc = self._doc_to_help(func)
292
sys.stdout.write(doc + '\n')
295
doc = self.strip_initial_spaces(func())
297
sys.stdout.write(doc + '\n')
300
def _doc_to_help(self, obj):
304
return self._strip_initial_spaces(doc)
306
def _strip_initial_spaces(self, s):
307
#lines = s.split('\n')
308
lines = s.split('\n')
309
spaces = re.match(' *', lines[0]).group(0)
310
#def strip_spaces(l):
311
# if l.startswith(spaces):
312
# l = l[len(spaces):]
314
#return '\n'.join([ strip_spaces(l) for l in lines ])
315
def strip_spaces(l, spaces=spaces):
316
if l[:len(spaces)] == spaces:
319
lines = list(map(strip_spaces, lines))
320
return '\n'.join(lines)
322
def do_exit(self, argv):
324
exit Exit SCons interactive mode.
328
def do_help(self, argv):
330
help [COMMAND] Prints help for the specified COMMAND. 'h'
331
and '?' are synonyms.
335
if self._do_one_help(arg):
338
# If bare 'help' is called, print this class's doc
339
# string (if it has one).
340
doc = self._doc_to_help(self.__class__)
342
sys.stdout.write(doc + '\n')
345
def do_shell(self, argv):
347
shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and
353
argv = os.environ[self.shell_variable]
355
# Per "[Python-Dev] subprocess insufficiently platform-independent?"
356
# http://mail.python.org/pipermail/python-dev/2008-August/081979.html "+
357
# Doing the right thing with an argument list currently
358
# requires different shell= values on Windows and Linux.
359
p = subprocess.Popen(argv, shell=(sys.platform=='win32'))
360
except EnvironmentError, e:
361
sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror))
365
def do_version(self, argv):
367
version Prints SCons version information.
369
sys.stdout.write(self.parser.version + '\n')
371
def interact(fs, parser, options, targets, target_top):
372
c = SConsInteractiveCmd(prompt = 'scons>>> ',
377
target_top = target_top)
382
# indent-tabs-mode:nil
384
# vim: set expandtab tabstop=4 shiftwidth=4: