blob: d8e413798e6228e28dc58ec20c3357fa609424db [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
Greg Wardfe6462c2000-04-04 01:40:52 +000061
Greg Wardd197a3a2000-04-10 13:11:51 +000062 # Some commands define a 'self.force' option to ignore file
63 # timestamps, but methods defined *here* assume that
64 # 'self.force' exists for all commands. So define it here
65 # just to be safe.
66 self.force = None
67
Greg Wardfe6462c2000-04-04 01:40:52 +000068 # The 'help' flag is just used for command-line parsing, so
69 # none of that complicated bureaucracy is needed.
70 self.help = 0
71
72 # 'ready' records whether or not 'finalize_options()' has been
73 # called. 'finalize_options()' itself should not pay attention to
74 # this flag: it is the business of 'ensure_ready()', which always
75 # calls 'finalize_options()', to respect/update it.
76 self.ready = 0
77
78 # __init__ ()
79
80
81 def __getattr__ (self, attr):
Greg Ward68a07572000-04-10 00:18:16 +000082 if attr in ('verbose', 'dry_run'):
Greg Wardfe6462c2000-04-04 01:40:52 +000083 myval = getattr (self, "_" + attr)
84 if myval is None:
85 return getattr (self.distribution, attr)
86 else:
87 return myval
88 else:
89 raise AttributeError, attr
90
91
92 def ensure_ready (self):
93 if not self.ready:
94 self.finalize_options ()
95 self.ready = 1
96
97
98 # Subclasses must define:
99 # initialize_options()
100 # provide default values for all options; may be overridden
101 # by Distutils client, by command-line options, or by options
102 # from option file
103 # finalize_options()
104 # decide on the final values for all options; this is called
105 # after all possible intervention from the outside world
106 # (command-line, option file, etc.) has been processed
107 # run()
108 # run the command: do whatever it is we're here to do,
109 # controlled by the command's various option values
110
111 def initialize_options (self):
112 """Set default values for all the options that this command
113 supports. Note that these defaults may be overridden
114 by the command-line supplied by the user; thus, this is
115 not the place to code dependencies between options; generally,
116 'initialize_options()' implementations are just a bunch
117 of "self.foo = None" assignments.
118
119 This method must be implemented by all command classes."""
120
121 raise RuntimeError, \
122 "abstract method -- subclass %s must override" % self.__class__
123
124 def finalize_options (self):
125 """Set final values for all the options that this command
126 supports. This is always called as late as possible, ie.
127 after any option assignments from the command-line or from
128 other commands have been done. Thus, this is the place to to
129 code option dependencies: if 'foo' depends on 'bar', then it
130 is safe to set 'foo' from 'bar' as long as 'foo' still has
131 the same value it was assigned in 'initialize_options()'.
132
133 This method must be implemented by all command classes."""
134
135 raise RuntimeError, \
136 "abstract method -- subclass %s must override" % self.__class__
137
138 def run (self):
139 """A command's raison d'etre: carry out the action it exists
140 to perform, controlled by the options initialized in
141 'initialize_options()', customized by the user and other
142 commands, and finalized in 'finalize_options()'. All
143 terminal output and filesystem interaction should be done by
144 'run()'.
145
146 This method must be implemented by all command classes."""
147
148 raise RuntimeError, \
149 "abstract method -- subclass %s must override" % self.__class__
150
151 def announce (self, msg, level=1):
152 """If the Distribution instance to which this command belongs
153 has a verbosity level of greater than or equal to 'level'
154 print 'msg' to stdout."""
155
156 if self.verbose >= level:
157 print msg
158
159
160 # -- Option query/set methods --------------------------------------
161
162 def get_option (self, option):
163 """Return the value of a single option for this command. Raise
Greg Ward02a1a2b2000-04-15 22:15:07 +0000164 AttributeError if 'option' is not known."""
165 return getattr (self, option)
Greg Wardfe6462c2000-04-04 01:40:52 +0000166
167
168 def get_options (self, *options):
169 """Return (as a tuple) the values of several options for this
Greg Ward02a1a2b2000-04-15 22:15:07 +0000170 command. Raise AttributeError if any of the options in
Greg Wardfe6462c2000-04-04 01:40:52 +0000171 'options' are not known."""
172
173 values = []
Greg Ward02a1a2b2000-04-15 22:15:07 +0000174 for opt in options:
175 values.append (getattr (self, opt))
176
Greg Wardfe6462c2000-04-04 01:40:52 +0000177 return tuple (values)
Greg Ward02a1a2b2000-04-15 22:15:07 +0000178
Greg Wardfe6462c2000-04-04 01:40:52 +0000179
180 def set_option (self, option, value):
181 """Set the value of a single option for this command. Raise
Greg Ward02a1a2b2000-04-15 22:15:07 +0000182 AttributeError if 'option' is not known."""
Greg Wardfe6462c2000-04-04 01:40:52 +0000183
184 if not hasattr (self, option):
Greg Ward02a1a2b2000-04-15 22:15:07 +0000185 raise AttributeError, \
Greg Wardfe6462c2000-04-04 01:40:52 +0000186 "command '%s': no such option '%s'" % \
187 (self.get_command_name(), option)
188 if value is not None:
189 setattr (self, option, value)
190
191 def set_options (self, **optval):
192 """Set the values of several options for this command. Raise
Greg Ward02a1a2b2000-04-15 22:15:07 +0000193 AttributeError if any of the options specified as
Greg Wardfe6462c2000-04-04 01:40:52 +0000194 keyword arguments are not known."""
195
196 for k in optval.keys():
197 if optval[k] is not None:
198 self.set_option (k, optval[k])
199
200
201 # -- Convenience methods for commands ------------------------------
202
203 def get_command_name (self):
204 if hasattr (self, 'command_name'):
205 return self.command_name
206 else:
207 return self.__class__.__name__
208
209
210 def set_undefined_options (self, src_cmd, *option_pairs):
211 """Set the values of any "undefined" options from corresponding
212 option values in some other command object. "Undefined" here
213 means "is None", which is the convention used to indicate
214 that an option has not been changed between
215 'set_initial_values()' and 'set_final_values()'. Usually
216 called from 'set_final_values()' for options that depend on
217 some other command rather than another option of the same
218 command. 'src_cmd' is the other command from which option
219 values will be taken (a command object will be created for it
220 if necessary); the remaining arguments are
221 '(src_option,dst_option)' tuples which mean "take the value
222 of 'src_option' in the 'src_cmd' command object, and copy it
223 to 'dst_option' in the current command object"."""
224
225 # Option_pairs: list of (src_option, dst_option) tuples
226
227 src_cmd_obj = self.distribution.find_command_obj (src_cmd)
228 src_cmd_obj.ensure_ready ()
Greg Ward02a1a2b2000-04-15 22:15:07 +0000229 for (src_option, dst_option) in option_pairs:
230 if getattr (self, dst_option) is None:
231 self.set_option (dst_option,
232 src_cmd_obj.get_option (src_option))
Greg Wardfe6462c2000-04-04 01:40:52 +0000233
234
235 def find_peer (self, command, create=1):
236 """Wrapper around Distribution's 'find_command_obj()' method:
237 find (create if necessary and 'create' is true) the command
238 object for 'command'.."""
239
240 cmd_obj = self.distribution.find_command_obj (command, create)
241 cmd_obj.ensure_ready ()
242 return cmd_obj
243
244
245 def get_peer_option (self, command, option):
246 """Find or create the command object for 'command', and return
247 its 'option' option."""
248
249 cmd_obj = self.find_peer (command)
250 return cmd_obj.get_option (option)
251
252
253 def run_peer (self, command):
254 """Run some other command: uses the 'run_command()' method of
255 Distribution, which creates the command object if necessary
256 and then invokes its 'run()' method."""
257
258 self.distribution.run_command (command)
259
260
261 # -- External world manipulation -----------------------------------
262
263 def warn (self, msg):
264 sys.stderr.write ("warning: %s: %s\n" %
265 (self.get_command_name(), msg))
266
267
268 def execute (self, func, args, msg=None, level=1):
269 """Perform some action that affects the outside world (eg.
270 by writing to the filesystem). Such actions are special because
271 they should be disabled by the "dry run" flag, and should
272 announce themselves if the current verbosity level is high
273 enough. This method takes care of all that bureaucracy for you;
274 all you have to do is supply the funtion to call and an argument
275 tuple for it (to embody the "external action" being performed),
276 a message to print if the verbosity level is high enough, and an
277 optional verbosity threshold."""
278
279 # Generate a message if we weren't passed one
280 if msg is None:
281 msg = "%s %s" % (func.__name__, `args`)
282 if msg[-2:] == ',)': # correct for singleton tuple
283 msg = msg[0:-2] + ')'
284
285 # Print it if verbosity level is high enough
286 self.announce (msg, level)
287
288 # And do it, as long as we're not in dry-run mode
289 if not self.dry_run:
290 apply (func, args)
291
292 # execute()
293
294
295 def mkpath (self, name, mode=0777):
296 util.mkpath (name, mode,
297 self.verbose, self.dry_run)
298
299
300 def copy_file (self, infile, outfile,
301 preserve_mode=1, preserve_times=1, link=None, level=1):
Greg Warde9613ae2000-04-10 01:30:44 +0000302 """Copy a file respecting verbose, dry-run and force flags. (The
303 former two default to whatever is in the Distribution object, and
304 the latter defaults to false for commands that don't define it.)"""
Greg Wardfe6462c2000-04-04 01:40:52 +0000305
306 return util.copy_file (infile, outfile,
307 preserve_mode, preserve_times,
308 not self.force,
309 link,
310 self.verbose >= level,
311 self.dry_run)
312
313
314 def copy_tree (self, infile, outfile,
315 preserve_mode=1, preserve_times=1, preserve_symlinks=0,
316 level=1):
317 """Copy an entire directory tree respecting verbose, dry-run,
Greg Warde9613ae2000-04-10 01:30:44 +0000318 and force flags."""
Greg Wardfe6462c2000-04-04 01:40:52 +0000319
320 return util.copy_tree (infile, outfile,
321 preserve_mode,preserve_times,preserve_symlinks,
322 not self.force,
323 self.verbose >= level,
324 self.dry_run)
325
326
327 def move_file (self, src, dst, level=1):
328 """Move a file respecting verbose and dry-run flags."""
329 return util.move_file (src, dst,
330 self.verbose >= level,
331 self.dry_run)
332
333
334 def spawn (self, cmd, search_path=1, level=1):
335 from distutils.spawn import spawn
336 spawn (cmd, search_path,
337 self.verbose >= level,
338 self.dry_run)
339
340
341 def make_archive (self, base_name, format,
342 root_dir=None, base_dir=None):
343 util.make_archive (base_name, format, root_dir, base_dir,
344 self.verbose, self.dry_run)
345
346
347 def make_file (self, infiles, outfile, func, args,
Greg Ward68a07572000-04-10 00:18:16 +0000348 exec_msg=None, skip_msg=None, level=1):
Greg Wardfe6462c2000-04-04 01:40:52 +0000349
350 """Special case of 'execute()' for operations that process one or
Greg Ward68a07572000-04-10 00:18:16 +0000351 more input files and generate one output file. Works just like
352 'execute()', except the operation is skipped and a different
353 message printed if 'outfile' already exists and is newer than all
354 files listed in 'infiles'. If the command defined 'self.force',
355 and it is true, then the command is unconditionally run -- does no
356 timestamp checks."""
Greg Wardfe6462c2000-04-04 01:40:52 +0000357
358
359 if exec_msg is None:
360 exec_msg = "generating %s from %s" % \
361 (outfile, string.join (infiles, ', '))
362 if skip_msg is None:
363 skip_msg = "skipping %s (inputs unchanged)" % outfile
364
365
366 # Allow 'infiles' to be a single string
367 if type (infiles) is StringType:
368 infiles = (infiles,)
369 elif type (infiles) not in (ListType, TupleType):
370 raise TypeError, \
371 "'infiles' must be a string, or a list or tuple of strings"
372
373 # If 'outfile' must be regenerated (either because it doesn't
374 # exist, is out-of-date, or the 'force' flag is true) then
375 # perform the action that presumably regenerates it
Greg Warde9613ae2000-04-10 01:30:44 +0000376 if self.force or util.newer_group (infiles, outfile):
Greg Wardfe6462c2000-04-04 01:40:52 +0000377 self.execute (func, args, exec_msg, level)
378
379 # Otherwise, print the "skip" message
380 else:
381 self.announce (skip_msg, level)
382
383 # make_file ()
384
385# class Command
Greg Wardb3612332000-04-09 03:48:37 +0000386
387
388if __name__ == "__main__":
389 print "ok"