Tweak svn/git ignores
[zxing.git] / cpp / scons / scons-local-2.0.0.final.0 / SCons / Script / Interactive.py
1 #
2 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 __revision__ = "src/engine/SCons/Script/Interactive.py 5023 2010/06/14 22:05:46 scons"
24
25 __doc__ = """
26 SCons interactive mode
27 """
28
29 # TODO:
30 #
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):
35
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.
39 #
40 # - Additional command-line options on the "build" command.
41 #
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.
45 #
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:
48 #
49 #       build --diskcheck=*
50 #       build --implicit-cache=*
51 #       build --implicit-deps-changed=*
52 #       build --implicit-deps-unchanged=*
53 #
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:
57 #
58 #       build --random
59 #
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.
64 #
65 #       build -q [???  is there a useful analog to the exit status?]
66 #       build --duplicate=
67 #       build --profile=
68 #       build --max-drift=
69 #       build --warn=*
70 #       build --Y
71 #
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}')".
76 #
77 # - Need something in the 'help' command that prints the -h output.
78 #
79 # - A command to run the configure subsystem separately (must see how
80 #   this interacts with the new automake model).
81 #
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.
85 #
86
87 import cmd
88 import copy
89 import os
90 import re
91 import shlex
92 import sys
93
94 try:
95     import readline
96 except ImportError:
97     pass
98
99 class SConsInteractiveCmd(cmd.Cmd):
100     """\
101     build [TARGETS]         Build the specified TARGETS and their dependencies.
102                             'b' is a synonym.
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
107                             '?' are synonyms.
108     shell [COMMANDLINE]     Execute COMMANDLINE in a subshell.  'sh' and '!'
109                             are synonyms.
110     version                 Prints SCons version information.
111     """
112
113     synonyms = {
114         'b'     : 'build',
115         'c'     : 'clean',
116         'h'     : 'help',
117         'scons' : 'build',
118         'sh'    : 'shell',
119     }
120
121     def __init__(self, **kw):
122         cmd.Cmd.__init__(self)
123         for key, val in kw.items():
124             setattr(self, key, val)
125
126         if sys.platform == 'win32':
127             self.shell_variable = 'COMSPEC'
128         else:
129             self.shell_variable = 'SHELL'
130
131     def default(self, argv):
132         print "*** Unknown command: %s" % argv[0]
133
134     def onecmd(self, line):
135         line = line.strip()
136         if not line:
137             print self.lastcmd
138             return self.emptyline()
139         self.lastcmd = line
140         if line[0] == '!':
141             line = 'shell ' + line[1:]
142         elif line[0] == '?':
143             line = 'help ' + line[1:]
144         if os.sep == '\\':
145             line = line.replace('\\', '\\\\')
146         argv = shlex.split(line)
147         argv[0] = self.synonyms.get(argv[0], argv[0])
148         if not argv[0]:
149             return self.default(line)
150         else:
151             try:
152                 func = getattr(self, 'do_' + argv[0])
153             except AttributeError:
154                 return self.default(argv)
155             return func(argv)
156
157     def do_build(self, argv):
158         """\
159         build [TARGETS]         Build the specified TARGETS and their
160                                 dependencies.  'b' is a synonym.
161         """
162         import SCons.Node
163         import SCons.SConsign
164         import SCons.Script.Main
165
166         options = copy.deepcopy(self.options)
167
168         options, targets = self.parser.parse_args(argv[1:], values=options)
169
170         SCons.Script.COMMAND_LINE_TARGETS = targets
171
172         if targets:
173             SCons.Script.BUILD_TARGETS = targets
174         else:
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
178
179         nodes = SCons.Script.Main._build_targets(self.fs,
180                                                  options,
181                                                  targets,
182                                                  self.target_top)
183
184         if not nodes:
185             return
186
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.
192         x = []
193         for n in nodes:
194             x.extend(n.alter_targets()[0])
195         nodes.extend(x)
196
197         # Clean up so that we can perform the next build correctly.
198         #
199         # We do this by walking over all the children of the targets,
200         # and clearing their state.
201         #
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.)
207         #
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.
213         #
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.
216
217         SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
218
219         seen_nodes = {}
220
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)))
225
226         def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
227             seen_nodes[node] = 1
228
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.
235             try:
236                 rfile_method = node.rfile
237             except AttributeError:
238                 return
239             else:
240                 rfile = rfile_method()
241             if rfile != node:
242                 seen_nodes[rfile] = 1
243
244         for node in nodes:
245             walker = SCons.Node.Walker(node,
246                                         kids_func=get_unseen_children,
247                                         eval_func=add_to_seen_nodes)
248             n = walker.get_next()
249             while n:
250                 n = walker.get_next()
251
252         for node in seen_nodes.keys():
253             # Call node.clear() to clear most of the state
254             node.clear()
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)
258             node.implicit = None
259
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))
265
266         SCons.SConsign.Reset()
267         SCons.Script.Main.progress_display("scons: done clearing node information.")
268
269     def do_clean(self, argv):
270         """\
271         clean [TARGETS]         Clean (remove) the specified TARGETS
272                                 and their dependencies.  'c' is a synonym.
273         """
274         return self.do_build(['build', '--clean'] + argv[1:])
275
276     def do_EOF(self, argv):
277         print
278         self.do_exit(argv)
279
280     def _do_one_help(self, arg):
281         try:
282             # If help_<arg>() exists, then call it.
283             func = getattr(self, 'help_' + arg)
284         except AttributeError:
285             try:
286                 func = getattr(self, 'do_' + arg)
287             except AttributeError:
288                 doc = None
289             else:
290                 doc = self._doc_to_help(func)
291             if doc:
292                 sys.stdout.write(doc + '\n')
293                 sys.stdout.flush()
294         else:
295             doc = self.strip_initial_spaces(func())
296             if doc:
297                 sys.stdout.write(doc + '\n')
298                 sys.stdout.flush()
299
300     def _doc_to_help(self, obj):
301         doc = obj.__doc__
302         if doc is None:
303             return ''
304         return self._strip_initial_spaces(doc)
305
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):]
313         #    return l
314         #return '\n'.join([ strip_spaces(l) for l in lines ])
315         def strip_spaces(l, spaces=spaces):
316             if l[:len(spaces)] == spaces:
317                 l = l[len(spaces):]
318             return l
319         lines = list(map(strip_spaces, lines))
320         return '\n'.join(lines)
321
322     def do_exit(self, argv):
323         """\
324         exit                    Exit SCons interactive mode.
325         """
326         sys.exit(0)
327
328     def do_help(self, argv):
329         """\
330         help [COMMAND]          Prints help for the specified COMMAND.  'h'
331                                 and '?' are synonyms.
332         """
333         if argv[1:]:
334             for arg in argv[1:]:
335                 if self._do_one_help(arg):
336                     break
337         else:
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__)
341             if doc:
342                 sys.stdout.write(doc + '\n')
343                 sys.stdout.flush()
344
345     def do_shell(self, argv):
346         """\
347         shell [COMMANDLINE]     Execute COMMANDLINE in a subshell.  'sh' and
348                                 '!' are synonyms.
349         """
350         import subprocess
351         argv = argv[1:]
352         if not argv:
353             argv = os.environ[self.shell_variable]
354         try:
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))
362         else:
363             p.wait()
364
365     def do_version(self, argv):
366         """\
367         version                 Prints SCons version information.
368         """
369         sys.stdout.write(self.parser.version + '\n')
370
371 def interact(fs, parser, options, targets, target_top):
372     c = SConsInteractiveCmd(prompt = 'scons>>> ',
373                             fs = fs,
374                             parser = parser,
375                             options = options,
376                             targets = targets,
377                             target_top = target_top)
378     c.cmdloop()
379
380 # Local Variables:
381 # tab-width:4
382 # indent-tabs-mode:nil
383 # End:
384 # vim: set expandtab tabstop=4 shiftwidth=4: