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.
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
27 SCons interactive mode
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):
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.
41 # - Additional command-line options on the "build" command.
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.
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:
51 # build --implicit-cache=*
52 # build --implicit-deps-changed=*
53 # build --implicit-deps-unchanged=*
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:
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.
66 # build -q [??? is there a useful analog to the exit status?]
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}')".
78 # - Need something in the 'help' command that prints the -h output.
80 # - A command to run the configure subsystem separately (must see how
81 # this interacts with the new automake model).
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.
101 from SCons.Debug import Trace
103 class SConsInteractiveCmd(cmd.Cmd):
105 build [TARGETS] Build the specified TARGETS and their dependencies.
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
112 shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and '!'
114 version Prints SCons version information.
125 def __init__(self, **kw):
126 cmd.Cmd.__init__(self)
127 for key, val in kw.items():
128 setattr(self, key, val)
130 if sys.platform == 'win32':
131 self.shell_variable = 'COMSPEC'
133 self.shell_variable = 'SHELL'
135 def default(self, argv):
136 print "*** Unknown command: %s" % argv[0]
138 def onecmd(self, line):
139 line = string.strip(line)
142 return self.emptyline()
145 line = 'shell ' + line[1:]
147 line = 'help ' + line[1:]
148 argv = shlex.split(line)
149 argv[0] = self.synonyms.get(argv[0], argv[0])
151 return self.default(line)
154 func = getattr(self, 'do_' + argv[0])
155 except AttributeError:
156 return self.default(argv)
159 def do_build(self, argv):
161 build [TARGETS] Build the specified TARGETS and their
162 dependencies. 'b' is a synonym.
164 import SCons.SConsign
165 import SCons.Script.Main
167 options = copy.deepcopy(self.options)
169 options, targets = self.parser.parse_args(argv[1:], values=options)
171 SCons.Script.COMMAND_LINE_TARGETS = targets
174 SCons.Script.BUILD_TARGETS = targets
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
180 nodes = SCons.Script.Main._build_targets(self.fs,
188 # Clean up so that we can perform the next build correctly.
190 # We do this by walking over all the children of the targets,
191 # and clearing their state.
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.)
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.
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.
208 SCons.Script.Main.progress_display("scons: Clearing cached node information ...")
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))
217 def add_to_seen_nodes(node, parent, seen_nodes=seen_nodes):
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.
227 rfile_method = node.rfile
228 except AttributeError:
231 rfile = rfile_method()
233 seen_nodes[rfile] = 1
236 walker = SCons.Node.Walker(node,
237 kids_func=get_unseen_children,
238 eval_func=add_to_seen_nodes)
243 for node in seen_nodes.keys():
244 # Call node.clear() to clear most of the state
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)
251 SCons.SConsign.Reset()
252 SCons.Script.Main.progress_display("scons: done clearing node information.")
254 def do_clean(self, argv):
256 clean [TARGETS] Clean (remove) the specified TARGETS
257 and their dependencies. 'c' is a synonym.
259 return self.do_build(['build', '--clean'] + argv[1:])
261 def do_EOF(self, argv):
265 def _do_one_help(self, arg):
267 # If help_<arg>() exists, then call it.
268 func = getattr(self, 'help_' + arg)
269 except AttributeError:
271 func = getattr(self, 'do_' + arg)
272 except AttributeError:
275 doc = self._doc_to_help(func)
277 sys.stdout.write(doc + '\n')
280 doc = self.strip_initial_spaces(func())
282 sys.stdout.write(doc + '\n')
285 def _doc_to_help(self, obj):
289 return self._strip_initial_spaces(doc)
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):]
299 #return '\n'.join([ strip_spaces(l) for l in lines ])
300 def strip_spaces(l, spaces=spaces):
301 if l[:len(spaces)] == spaces:
304 lines = map(strip_spaces, lines)
305 return string.join(lines, '\n')
307 def do_exit(self, argv):
309 exit Exit SCons interactive mode.
313 def do_help(self, argv):
315 help [COMMAND] Prints help for the specified COMMAND. 'h'
316 and '?' are synonyms.
320 if self._do_one_help(arg):
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__)
327 sys.stdout.write(doc + '\n')
330 def do_shell(self, argv):
332 shell [COMMANDLINE] Execute COMMANDLINE in a subshell. 'sh' and
338 argv = os.environ[self.shell_variable]
340 p = subprocess.Popen(argv)
341 except EnvironmentError, e:
342 sys.stderr.write('scons: %s: %s\n' % (argv[0], e.strerror))
346 def do_version(self, argv):
348 version Prints SCons version information.
350 sys.stdout.write(self.parser.version + '\n')
352 def interact(fs, parser, options, targets, target_top):
353 c = SConsInteractiveCmd(prompt = 'scons>>> ',
358 target_top = target_top)