blob: ad78703713e01a1124a90e28883aa3a9fa0dae7e [file] [log] [blame]
Greg Wardfe6462c2000-04-04 01:40:52 +00001"""distutils.cmd
2
3Provides the Command class, the base class for the command classes
4in the distutils.command package."""
5
6# created 2000/04/03, Greg Ward
7# (extricated from core.py; actually dates back to the beginning)
8
9__revision__ = "$Id$"
10
11import sys, string
12from types import *
13from distutils.errors import *
14from distutils import util
15
16
17class Command:
18 """Abstract base class for defining command classes, the "worker bees"
19 of the Distutils. A useful analogy for command classes is to
20 think of them as subroutines with local variables called
21 "options". The options are "declared" in 'initialize_options()'
22 and "defined" (given their final values, aka "finalized") in
23 'finalize_options()', both of which must be defined by every
24 command class. The distinction between the two is necessary
25 because option values might come from the outside world (command
26 line, option file, ...), and any options dependent on other
27 options must be computed *after* these outside influences have
28 been processed -- hence 'finalize_options()'. The "body" of the
29 subroutine, where it does all its work based on the values of its
30 options, is the 'run()' method, which must also be implemented by
31 every command class."""
32
33 # -- Creation/initialization methods -------------------------------
34
35 def __init__ (self, dist):
36 """Create and initialize a new Command object. Most importantly,
37 invokes the 'initialize_options()' method, which is the
38 real initializer and depends on the actual command being
39 instantiated."""
40
41 # late import because of mutual dependence between these classes
42 from distutils.dist import Distribution
43
44 if not isinstance (dist, Distribution):
45 raise TypeError, "dist must be a Distribution instance"
46 if self.__class__ is Command:
47 raise RuntimeError, "Command is an abstract class"
48
49 self.distribution = dist
50 self.initialize_options ()
51
52 # Per-command versions of the global flags, so that the user can
53 # customize Distutils' behaviour command-by-command and let some
54 # commands fallback on the Distribution's behaviour. None means
55 # "not defined, check self.distribution's copy", while 0 or 1 mean
56 # false and true (duh). Note that this means figuring out the real
57 # value of each flag is a touch complicatd -- hence "self.verbose"
58 # (etc.) will be handled by __getattr__, below.
59 self._verbose = None
60 self._dry_run = None
61 self._force = None
62
63 # The 'help' flag is just used for command-line parsing, so
64 # none of that complicated bureaucracy is needed.
65 self.help = 0
66
67 # 'ready' records whether or not 'finalize_options()' has been
68 # called. 'finalize_options()' itself should not pay attention to
69 # this flag: it is the business of 'ensure_ready()', which always
70 # calls 'finalize_options()', to respect/update it.
71 self.ready = 0
72
73 # __init__ ()
74
75
76 def __getattr__ (self, attr):
77 if attr in ('verbose', 'dry_run', 'force'):
78 myval = getattr (self, "_" + attr)
79 if myval is None:
80 return getattr (self.distribution, attr)
81 else:
82 return myval
83 else:
84 raise AttributeError, attr
85
86
87 def ensure_ready (self):
88 if not self.ready:
89 self.finalize_options ()
90 self.ready = 1
91
92
93 # Subclasses must define:
94 # initialize_options()
95 # provide default values for all options; may be overridden
96 # by Distutils client, by command-line options, or by options
97 # from option file
98 # finalize_options()
99 # decide on the final values for all options; this is called
100 # after all possible intervention from the outside world
101 # (command-line, option file, etc.) has been processed
102 # run()
103 # run the command: do whatever it is we're here to do,
104 # controlled by the command's various option values
105
106 def initialize_options (self):
107 """Set default values for all the options that this command
108 supports. Note that these defaults may be overridden
109 by the command-line supplied by the user; thus, this is
110 not the place to code dependencies between options; generally,
111 'initialize_options()' implementations are just a bunch
112 of "self.foo = None" assignments.
113
114 This method must be implemented by all command classes."""
115
116 raise RuntimeError, \
117 "abstract method -- subclass %s must override" % self.__class__
118
119 def finalize_options (self):
120 """Set final values for all the options that this command
121 supports. This is always called as late as possible, ie.
122 after any option assignments from the command-line or from
123 other commands have been done. Thus, this is the place to to
124 code option dependencies: if 'foo' depends on 'bar', then it
125 is safe to set 'foo' from 'bar' as long as 'foo' still has
126 the same value it was assigned in 'initialize_options()'.
127
128 This method must be implemented by all command classes."""
129
130 raise RuntimeError, \
131 "abstract method -- subclass %s must override" % self.__class__
132
133 def run (self):
134 """A command's raison d'etre: carry out the action it exists
135 to perform, controlled by the options initialized in
136 'initialize_options()', customized by the user and other
137 commands, and finalized in 'finalize_options()'. All
138 terminal output and filesystem interaction should be done by
139 'run()'.
140
141 This method must be implemented by all command classes."""
142
143 raise RuntimeError, \
144 "abstract method -- subclass %s must override" % self.__class__
145
146 def announce (self, msg, level=1):
147 """If the Distribution instance to which this command belongs
148 has a verbosity level of greater than or equal to 'level'
149 print 'msg' to stdout."""
150
151 if self.verbose >= level:
152 print msg
153
154
155 # -- Option query/set methods --------------------------------------
156
157 def get_option (self, option):
158 """Return the value of a single option for this command. Raise
159 DistutilsOptionError if 'option' is not known."""
160 try:
161 return getattr (self, option)
162 except AttributeError:
163 raise DistutilsOptionError, \
164 "command %s: no such option %s" % \
165 (self.get_command_name(), option)
166
167
168 def get_options (self, *options):
169 """Return (as a tuple) the values of several options for this
170 command. Raise DistutilsOptionError if any of the options in
171 'options' are not known."""
172
173 values = []
174 try:
175 for opt in options:
176 values.append (getattr (self, opt))
177 except AttributeError, name:
178 raise DistutilsOptionError, \
179 "command %s: no such option %s" % \
180 (self.get_command_name(), name)
181
182 return tuple (values)
183
184
185 def set_option (self, option, value):
186 """Set the value of a single option for this command. Raise
187 DistutilsOptionError if 'option' is not known."""
188
189 if not hasattr (self, option):
190 raise DistutilsOptionError, \
191 "command '%s': no such option '%s'" % \
192 (self.get_command_name(), option)
193 if value is not None:
194 setattr (self, option, value)
195
196 def set_options (self, **optval):
197 """Set the values of several options for this command. Raise
198 DistutilsOptionError if any of the options specified as
199 keyword arguments are not known."""
200
201 for k in optval.keys():
202 if optval[k] is not None:
203 self.set_option (k, optval[k])
204
205
206 # -- Convenience methods for commands ------------------------------
207
208 def get_command_name (self):
209 if hasattr (self, 'command_name'):
210 return self.command_name
211 else:
212 return self.__class__.__name__
213
214
215 def set_undefined_options (self, src_cmd, *option_pairs):
216 """Set the values of any "undefined" options from corresponding
217 option values in some other command object. "Undefined" here
218 means "is None", which is the convention used to indicate
219 that an option has not been changed between
220 'set_initial_values()' and 'set_final_values()'. Usually
221 called from 'set_final_values()' for options that depend on
222 some other command rather than another option of the same
223 command. 'src_cmd' is the other command from which option
224 values will be taken (a command object will be created for it
225 if necessary); the remaining arguments are
226 '(src_option,dst_option)' tuples which mean "take the value
227 of 'src_option' in the 'src_cmd' command object, and copy it
228 to 'dst_option' in the current command object"."""
229
230 # Option_pairs: list of (src_option, dst_option) tuples
231
232 src_cmd_obj = self.distribution.find_command_obj (src_cmd)
233 src_cmd_obj.ensure_ready ()
234 try:
235 for (src_option, dst_option) in option_pairs:
236 if getattr (self, dst_option) is None:
237 self.set_option (dst_option,
238 src_cmd_obj.get_option (src_option))
239 except AttributeError, name:
240 # duh, which command?
241 raise DistutilsOptionError, "unknown option %s" % name
242
243
244 def find_peer (self, command, create=1):
245 """Wrapper around Distribution's 'find_command_obj()' method:
246 find (create if necessary and 'create' is true) the command
247 object for 'command'.."""
248
249 cmd_obj = self.distribution.find_command_obj (command, create)
250 cmd_obj.ensure_ready ()
251 return cmd_obj
252
253
254 def get_peer_option (self, command, option):
255 """Find or create the command object for 'command', and return
256 its 'option' option."""
257
258 cmd_obj = self.find_peer (command)
259 return cmd_obj.get_option (option)
260
261
262 def run_peer (self, command):
263 """Run some other command: uses the 'run_command()' method of
264 Distribution, which creates the command object if necessary
265 and then invokes its 'run()' method."""
266
267 self.distribution.run_command (command)
268
269
270 # -- External world manipulation -----------------------------------
271
272 def warn (self, msg):
273 sys.stderr.write ("warning: %s: %s\n" %
274 (self.get_command_name(), msg))
275
276
277 def execute (self, func, args, msg=None, level=1):
278 """Perform some action that affects the outside world (eg.
279 by writing to the filesystem). Such actions are special because
280 they should be disabled by the "dry run" flag, and should
281 announce themselves if the current verbosity level is high
282 enough. This method takes care of all that bureaucracy for you;
283 all you have to do is supply the funtion to call and an argument
284 tuple for it (to embody the "external action" being performed),
285 a message to print if the verbosity level is high enough, and an
286 optional verbosity threshold."""
287
288 # Generate a message if we weren't passed one
289 if msg is None:
290 msg = "%s %s" % (func.__name__, `args`)
291 if msg[-2:] == ',)': # correct for singleton tuple
292 msg = msg[0:-2] + ')'
293
294 # Print it if verbosity level is high enough
295 self.announce (msg, level)
296
297 # And do it, as long as we're not in dry-run mode
298 if not self.dry_run:
299 apply (func, args)
300
301 # execute()
302
303
304 def mkpath (self, name, mode=0777):
305 util.mkpath (name, mode,
306 self.verbose, self.dry_run)
307
308
309 def copy_file (self, infile, outfile,
310 preserve_mode=1, preserve_times=1, link=None, level=1):
311 """Copy a file respecting verbose, dry-run and force flags."""
312
313 return util.copy_file (infile, outfile,
314 preserve_mode, preserve_times,
315 not self.force,
316 link,
317 self.verbose >= level,
318 self.dry_run)
319
320
321 def copy_tree (self, infile, outfile,
322 preserve_mode=1, preserve_times=1, preserve_symlinks=0,
323 level=1):
324 """Copy an entire directory tree respecting verbose, dry-run,
325 and force flags."""
326
327 return util.copy_tree (infile, outfile,
328 preserve_mode,preserve_times,preserve_symlinks,
329 not self.force,
330 self.verbose >= level,
331 self.dry_run)
332
333
334 def move_file (self, src, dst, level=1):
335 """Move a file respecting verbose and dry-run flags."""
336 return util.move_file (src, dst,
337 self.verbose >= level,
338 self.dry_run)
339
340
341 def spawn (self, cmd, search_path=1, level=1):
342 from distutils.spawn import spawn
343 spawn (cmd, search_path,
344 self.verbose >= level,
345 self.dry_run)
346
347
348 def make_archive (self, base_name, format,
349 root_dir=None, base_dir=None):
350 util.make_archive (base_name, format, root_dir, base_dir,
351 self.verbose, self.dry_run)
352
353
354 def make_file (self, infiles, outfile, func, args,
355 exec_msg=None, skip_msg=None, level=1):
356
357 """Special case of 'execute()' for operations that process one or
358 more input files and generate one output file. Works just like
359 'execute()', except the operation is skipped and a different
360 message printed if 'outfile' already exists and is newer than
361 all files listed in 'infiles'."""
362
363
364 if exec_msg is None:
365 exec_msg = "generating %s from %s" % \
366 (outfile, string.join (infiles, ', '))
367 if skip_msg is None:
368 skip_msg = "skipping %s (inputs unchanged)" % outfile
369
370
371 # Allow 'infiles' to be a single string
372 if type (infiles) is StringType:
373 infiles = (infiles,)
374 elif type (infiles) not in (ListType, TupleType):
375 raise TypeError, \
376 "'infiles' must be a string, or a list or tuple of strings"
377
378 # If 'outfile' must be regenerated (either because it doesn't
379 # exist, is out-of-date, or the 'force' flag is true) then
380 # perform the action that presumably regenerates it
381 if self.force or util.newer_group (infiles, outfile):
382 self.execute (func, args, exec_msg, level)
383
384 # Otherwise, print the "skip" message
385 else:
386 self.announce (skip_msg, level)
387
388 # make_file ()
389
390# class Command