Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 1 | """distutils.core |
| 2 | |
| 3 | The only module that needs to be imported to use the Distutils; provides |
| 4 | the 'setup' function (which must be called); the 'Distribution' class |
| 5 | (which may be subclassed if additional functionality is desired), and |
| 6 | the 'Command' class (which is used both internally by Distutils, and |
| 7 | may be subclassed by clients for still more flexibility).""" |
| 8 | |
| 9 | # created 1999/03/01, Greg Ward |
| 10 | |
| 11 | __rcsid__ = "$Id$" |
| 12 | |
Greg Ward | 06ca948 | 1999-04-04 02:58:07 +0000 | [diff] [blame] | 13 | import sys, os |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 14 | import string, re |
Greg Ward | 1ea8af2 | 1999-08-29 18:20:32 +0000 | [diff] [blame] | 15 | from types import * |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 16 | from distutils.errors import * |
| 17 | from distutils.fancy_getopt import fancy_getopt |
Greg Ward | 06ca948 | 1999-04-04 02:58:07 +0000 | [diff] [blame] | 18 | from distutils import util |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 19 | |
| 20 | # This is not *quite* the same as a Python NAME; I don't allow leading |
| 21 | # underscores. The fact that they're very similar is no coincidence... |
| 22 | command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') |
| 23 | |
| 24 | # Defining this as a global is probably inadequate -- what about |
| 25 | # listing the available options (or even commands, which can vary |
| 26 | # quite late as well) |
| 27 | usage = '%s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]' % sys.argv[0] |
| 28 | |
| 29 | |
| 30 | |
| 31 | def setup (**attrs): |
| 32 | """The gateway to the Distutils: do everything your setup script |
| 33 | needs to do, in a highly flexible and user-driven way. Briefly: |
| 34 | create a Distribution instance; parse the command-line, creating |
| 35 | and customizing instances of the command class for each command |
| 36 | found on the command-line; run each of those commands. |
| 37 | |
| 38 | The Distribution instance might be an instance of a class |
| 39 | supplied via the 'distclass' keyword argument to 'setup'; if no |
| 40 | such class is supplied, then the 'Distribution' class (also in |
| 41 | this module) is instantiated. All other arguments to 'setup' |
| 42 | (except for 'cmdclass') are used to set attributes of the |
| 43 | Distribution instance. |
| 44 | |
| 45 | The 'cmdclass' argument, if supplied, is a dictionary mapping |
| 46 | command names to command classes. Each command encountered on the |
| 47 | command line will be turned into a command class, which is in turn |
| 48 | instantiated; any class found in 'cmdclass' is used in place of the |
| 49 | default, which is (for command 'foo_bar') class 'FooBar' in module |
| 50 | 'distutils.command.foo_bar'. The command object must provide an |
| 51 | 'options' attribute which is a list of option specifiers for |
| 52 | 'distutils.fancy_getopt'. Any command-line options between the |
| 53 | current and the next command are used to set attributes in the |
| 54 | current command object. |
| 55 | |
| 56 | When the entire command-line has been successfully parsed, calls the |
| 57 | 'run' method on each command object in turn. This method will be |
| 58 | driven entirely by the Distribution object (which each command |
| 59 | object has a reference to, thanks to its constructor), and the |
| 60 | command-specific options that became attributes of each command |
| 61 | object.""" |
| 62 | |
| 63 | # Determine the distribution class -- either caller-supplied or |
| 64 | # our Distribution (see below). |
| 65 | klass = attrs.get ('distclass') |
| 66 | if klass: |
| 67 | del attrs['distclass'] |
| 68 | else: |
| 69 | klass = Distribution |
| 70 | |
| 71 | # Create the Distribution instance, using the remaining arguments |
| 72 | # (ie. everything except distclass) to initialize it |
| 73 | dist = klass (attrs) |
| 74 | |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 75 | # If we had a config file, this is where we would parse it: override |
| 76 | # the client-supplied command options, but be overridden by the |
| 77 | # command line. |
| 78 | |
| 79 | # Parse the command line; any command-line errors are the end-users |
| 80 | # fault, so turn them into SystemExit to suppress tracebacks. |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 81 | try: |
| 82 | dist.parse_command_line (sys.argv[1:]) |
| 83 | except DistutilsArgError, msg: |
| 84 | raise SystemExit, msg |
| 85 | |
| 86 | # And finally, run all the commands found on the command line. |
| 87 | dist.run_commands () |
| 88 | |
| 89 | # setup () |
| 90 | |
| 91 | |
| 92 | class Distribution: |
| 93 | """The core of the Distutils. Most of the work hiding behind |
| 94 | 'setup' is really done within a Distribution instance, which |
| 95 | farms the work out to the Distutils commands specified on the |
| 96 | command line. |
| 97 | |
| 98 | Clients will almost never instantiate Distribution directly, |
| 99 | unless the 'setup' function is totally inadequate to their needs. |
| 100 | However, it is conceivable that a client might wish to subclass |
| 101 | Distribution for some specialized purpose, and then pass the |
| 102 | subclass to 'setup' as the 'distclass' keyword argument. If so, |
| 103 | it is necessary to respect the expectations that 'setup' has of |
| 104 | Distribution: it must have a constructor and methods |
| 105 | 'parse_command_line()' and 'run_commands()' with signatures like |
| 106 | those described below.""" |
| 107 | |
| 108 | |
| 109 | # 'global_options' describes the command-line options that may |
| 110 | # be supplied to the client (setup.py) prior to any actual |
| 111 | # commands. Eg. "./setup.py -nv" or "./setup.py --verbose" |
| 112 | # both take advantage of these global options. |
| 113 | global_options = [('verbose', 'v', "run verbosely"), |
| 114 | ('dry-run', 'n', "don't actually do anything"), |
| 115 | ] |
| 116 | |
| 117 | |
| 118 | # -- Creation/initialization methods ------------------------------- |
| 119 | |
| 120 | def __init__ (self, attrs=None): |
| 121 | """Construct a new Distribution instance: initialize all the |
| 122 | attributes of a Distribution, and then uses 'attrs' (a |
| 123 | dictionary mapping attribute names to values) to assign |
| 124 | some of those attributes their "real" values. (Any attributes |
| 125 | not mentioned in 'attrs' will be assigned to some null |
| 126 | value: 0, None, an empty list or dictionary, etc.) Most |
| 127 | importantly, initialize the 'command_obj' attribute |
| 128 | to the empty dictionary; this will be filled in with real |
| 129 | command objects by 'parse_command_line()'.""" |
| 130 | |
| 131 | # Default values for our command-line options |
| 132 | self.verbose = 0 |
| 133 | self.dry_run = 0 |
| 134 | |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 135 | # And the "distribution meta-data" options -- these can only |
| 136 | # come from setup.py (the caller), not the command line |
| 137 | # (or a hypothetical config file).. |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 138 | self.name = None |
| 139 | self.version = None |
| 140 | self.author = None |
Greg Ward | df0d335 | 1999-09-21 18:41:36 +0000 | [diff] [blame^] | 141 | self.author_email = None |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 142 | self.url = None |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 143 | self.licence = None |
| 144 | self.description = None |
| 145 | |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 146 | # 'cmdclass' maps command names to class objects, so we |
| 147 | # can 1) quickly figure out which class to instantiate when |
| 148 | # we need to create a new command object, and 2) have a way |
| 149 | # for the client to override command classes |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 150 | self.cmdclass = {} |
| 151 | |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 152 | # These options are really the business of various commands, rather |
| 153 | # than of the Distribution itself. We provide aliases for them in |
| 154 | # Distribution as a convenience to the developer. |
| 155 | # dictionary. |
Greg Ward | df0d335 | 1999-09-21 18:41:36 +0000 | [diff] [blame^] | 156 | self.packages = None |
| 157 | self.package_dir = None |
| 158 | self.py_modules = None |
| 159 | self.ext_modules = None |
| 160 | self.ext_package = None |
| 161 | self.include_dirs = None |
| 162 | self.install_path = None |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 163 | |
| 164 | # And now initialize bookkeeping stuff that can't be supplied by |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 165 | # the caller at all. 'command_obj' maps command names to |
| 166 | # Command instances -- that's how we enforce that every command |
| 167 | # class is a singleton. |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 168 | self.command_obj = {} |
| 169 | |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 170 | # 'have_run' maps command names to boolean values; it keeps track |
| 171 | # of whether we have actually run a particular command, to make it |
| 172 | # cheap to "run" a command whenever we think we might need to -- if |
| 173 | # it's already been done, no need for expensive filesystem |
| 174 | # operations, we just check the 'have_run' dictionary and carry on. |
Greg Ward | 7f65c65 | 1999-08-14 23:47:21 +0000 | [diff] [blame] | 175 | # It's only safe to query 'have_run' for a command class that has |
| 176 | # been instantiated -- a false value will be inserted when the |
| 177 | # command object is created, and replaced with a true value when |
| 178 | # the command is succesfully run. Thus it's probably best to use |
| 179 | # '.get()' rather than a straight lookup. |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 180 | self.have_run = {} |
| 181 | |
Greg Ward | df0d335 | 1999-09-21 18:41:36 +0000 | [diff] [blame^] | 182 | # Now we'll use the attrs dictionary (ultimately, keyword args from |
| 183 | # the client) to possibly override any or all of these distribution |
| 184 | # options. |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 185 | if attrs: |
| 186 | |
| 187 | # Pull out the set of command options and work on them |
| 188 | # specifically. Note that this order guarantees that aliased |
| 189 | # command options will override any supplied redundantly |
| 190 | # through the general options dictionary. |
| 191 | options = attrs.get ('options') |
| 192 | if options: |
| 193 | del attrs['options'] |
| 194 | for (command, cmd_options) in options.items(): |
| 195 | cmd_obj = self.find_command_obj (command) |
| 196 | for (key, val) in cmd_options.items(): |
| 197 | cmd_obj.set_option (key, val) |
| 198 | # loop over commands |
| 199 | # if any command options |
| 200 | |
Greg Ward | df0d335 | 1999-09-21 18:41:36 +0000 | [diff] [blame^] | 201 | # Now work on the rest of the attributes. Any attribute that's |
| 202 | # not already defined is invalid! |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 203 | for (key,val) in attrs.items(): |
Greg Ward | df0d335 | 1999-09-21 18:41:36 +0000 | [diff] [blame^] | 204 | if hasattr (self, key): |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 205 | setattr (self, key, val) |
| 206 | else: |
| 207 | raise DistutilsOptionError, \ |
| 208 | "invalid distribution option '%s'" % key |
| 209 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 210 | # __init__ () |
| 211 | |
| 212 | |
| 213 | def parse_command_line (self, args): |
| 214 | """Parse the client's command line: set any Distribution |
| 215 | attributes tied to command-line options, create all command |
| 216 | objects, and set their options from the command-line. 'args' |
| 217 | must be a list of command-line arguments, most likely |
| 218 | 'sys.argv[1:]' (see the 'setup()' function). This list is |
| 219 | first processed for "global options" -- options that set |
| 220 | attributes of the Distribution instance. Then, it is |
| 221 | alternately scanned for Distutils command and options for |
| 222 | that command. Each new command terminates the options for |
| 223 | the previous command. The allowed options for a command are |
| 224 | determined by the 'options' attribute of the command object |
| 225 | -- thus, we instantiate (and cache) every command object |
| 226 | here, in order to access its 'options' attribute. Any error |
| 227 | in that 'options' attribute raises DistutilsGetoptError; any |
| 228 | error on the command-line raises DistutilsArgError. If no |
| 229 | Distutils commands were found on the command line, raises |
| 230 | DistutilsArgError.""" |
| 231 | |
| 232 | # We have to parse the command line a bit at a time -- global |
| 233 | # options, then the first command, then its options, and so on -- |
| 234 | # because each command will be handled by a different class, and |
| 235 | # the options that are valid for a particular class aren't |
| 236 | # known until we instantiate the command class, which doesn't |
| 237 | # happen until we know what the command is. |
| 238 | |
| 239 | self.commands = [] |
| 240 | args = fancy_getopt (self.global_options, self, sys.argv[1:]) |
| 241 | |
| 242 | while args: |
| 243 | # Pull the current command from the head of the command line |
| 244 | command = args[0] |
| 245 | if not command_re.match (command): |
| 246 | raise SystemExit, "invalid command name '%s'" % command |
| 247 | self.commands.append (command) |
| 248 | |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 249 | # Make sure we have a command object to put the options into |
| 250 | # (this either pulls it out of a cache of command objects, |
| 251 | # or finds and instantiates the command class). |
| 252 | cmd_obj = self.find_command_obj (command) |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 253 | |
| 254 | # Require that the command class be derived from Command -- |
| 255 | # that way, we can be sure that we at least have the 'run' |
| 256 | # and 'get_option' methods. |
| 257 | if not isinstance (cmd_obj, Command): |
| 258 | raise DistutilsClassError, \ |
| 259 | "command class %s must subclass Command" % \ |
| 260 | cmd_obj.__class__ |
| 261 | |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 262 | # Also make sure that the command object provides a list of its |
| 263 | # known options |
| 264 | if not (hasattr (cmd_obj, 'options') and |
| 265 | type (cmd_obj.options) is ListType): |
| 266 | raise DistutilsClasserror, \ |
| 267 | ("command class %s must provide an 'options' attribute "+ |
| 268 | "(a list of tuples)") % \ |
| 269 | cmd_obj.__class__ |
| 270 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 271 | args = fancy_getopt (cmd_obj.options, cmd_obj, args[1:]) |
| 272 | self.command_obj[command] = cmd_obj |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 273 | self.have_run[command] = 0 |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 274 | |
| 275 | # while args |
| 276 | |
| 277 | # Oops, no commands found -- an end-user error |
| 278 | if not self.commands: |
| 279 | sys.stderr.write (usage + "\n") |
| 280 | raise DistutilsArgError, "no commands supplied" |
| 281 | |
| 282 | # parse_command_line() |
| 283 | |
| 284 | |
| 285 | # -- Command class/object methods ---------------------------------- |
| 286 | |
| 287 | # This is a method just so it can be overridden if desired; it doesn't |
| 288 | # actually use or change any attributes of the Distribution instance. |
| 289 | def find_command_class (self, command): |
| 290 | """Given a command, derives the names of the module and class |
| 291 | expected to implement the command: eg. 'foo_bar' becomes |
| 292 | 'distutils.command.foo_bar' (the module) and 'FooBar' (the |
| 293 | class within that module). Loads the module, extracts the |
| 294 | class from it, and returns the class object. |
| 295 | |
| 296 | Raises DistutilsModuleError with a semi-user-targeted error |
| 297 | message if the expected module could not be loaded, or the |
| 298 | expected class was not found in it.""" |
| 299 | |
| 300 | module_name = 'distutils.command.' + command |
| 301 | klass_name = string.join \ |
| 302 | (map (string.capitalize, string.split (command, '_')), '') |
| 303 | |
| 304 | try: |
| 305 | __import__ (module_name) |
| 306 | module = sys.modules[module_name] |
| 307 | except ImportError: |
| 308 | raise DistutilsModuleError, \ |
| 309 | "invalid command '%s' (no module named %s)" % \ |
| 310 | (command, module_name) |
| 311 | |
| 312 | try: |
| 313 | klass = vars(module)[klass_name] |
| 314 | except KeyError: |
| 315 | raise DistutilsModuleError, \ |
| 316 | "invalid command '%s' (no class '%s' in module '%s')" \ |
| 317 | % (command, klass_name, module_name) |
| 318 | |
| 319 | return klass |
| 320 | |
| 321 | # find_command_class () |
| 322 | |
| 323 | |
| 324 | def create_command_obj (self, command): |
| 325 | """Figure out the class that should implement a command, |
| 326 | instantiate it, cache and return the new "command object". |
| 327 | The "command class" is determined either by looking it up in |
| 328 | the 'cmdclass' attribute (this is the mechanism whereby |
| 329 | clients may override default Distutils commands or add their |
| 330 | own), or by calling the 'find_command_class()' method (if the |
| 331 | command name is not in 'cmdclass'.""" |
| 332 | |
| 333 | # Determine the command class -- either it's in the command_class |
| 334 | # dictionary, or we have to divine the module and class name |
| 335 | klass = self.cmdclass.get(command) |
| 336 | if not klass: |
| 337 | klass = self.find_command_class (command) |
| 338 | self.cmdclass[command] = klass |
| 339 | |
| 340 | # Found the class OK -- instantiate it |
| 341 | cmd_obj = klass (self) |
| 342 | return cmd_obj |
| 343 | |
| 344 | |
| 345 | def find_command_obj (self, command, create=1): |
| 346 | """Look up and return a command object in the cache maintained by |
| 347 | 'create_command_obj()'. If none found, the action taken |
| 348 | depends on 'create': if true (the default), create a new |
| 349 | command object by calling 'create_command_obj()' and return |
| 350 | it; otherwise, return None.""" |
| 351 | |
| 352 | cmd_obj = self.command_obj.get (command) |
| 353 | if not cmd_obj and create: |
| 354 | cmd_obj = self.create_command_obj (command) |
| 355 | self.command_obj[command] = cmd_obj |
| 356 | |
| 357 | return cmd_obj |
| 358 | |
| 359 | |
| 360 | # -- Methods that operate on the Distribution ---------------------- |
| 361 | |
| 362 | def announce (self, msg, level=1): |
| 363 | """Print 'msg' if 'level' is greater than or equal to the verbosity |
| 364 | level recorded in the 'verbose' attribute (which, currently, |
| 365 | can be only 0 or 1).""" |
| 366 | |
| 367 | if self.verbose >= level: |
| 368 | print msg |
| 369 | |
| 370 | |
| 371 | def run_commands (self): |
| 372 | """Run each command that was seen on the client command line. |
| 373 | Uses the list of commands found and cache of command objects |
| 374 | created by 'create_command_obj()'.""" |
| 375 | |
| 376 | for cmd in self.commands: |
| 377 | self.run_command (cmd) |
| 378 | |
| 379 | |
| 380 | def get_option (self, option): |
| 381 | """Return the value of a distribution option. Raise |
| 382 | DistutilsOptionError if 'option' is not known.""" |
| 383 | |
| 384 | try: |
| 385 | return getattr (self, opt) |
| 386 | except AttributeError: |
| 387 | raise DistutilsOptionError, \ |
| 388 | "unknown distribution option %s" % option |
| 389 | |
| 390 | |
| 391 | def get_options (self, *options): |
| 392 | """Return (as a tuple) the values of several distribution |
| 393 | options. Raise DistutilsOptionError if any element of |
| 394 | 'options' is not known.""" |
| 395 | |
| 396 | values = [] |
| 397 | try: |
| 398 | for opt in options: |
| 399 | values.append (getattr (self, opt)) |
| 400 | except AttributeError, name: |
| 401 | raise DistutilsOptionError, \ |
| 402 | "unknown distribution option %s" % name |
| 403 | |
| 404 | return tuple (values) |
| 405 | |
| 406 | |
| 407 | # -- Methods that operate on its Commands -------------------------- |
| 408 | |
| 409 | def run_command (self, command): |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 410 | |
| 411 | """Do whatever it takes to run a command (including nothing at all, |
| 412 | if the command has already been run). Specifically: if we have |
| 413 | already created and run the command named by 'command', return |
| 414 | silently without doing anything. If the command named by |
| 415 | 'command' doesn't even have a command object yet, create one. |
| 416 | Then invoke 'run()' on that command object (or an existing |
| 417 | one).""" |
| 418 | |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 419 | # XXX currently, this is the only place where we invoke a |
| 420 | # command object's 'run()' method -- so it might make sense to |
| 421 | # put the 'set_final_options()' call here, too, instead of |
| 422 | # requiring every command's 'run()' to call it first. |
| 423 | |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 424 | # Already been here, done that? then return silently. |
| 425 | if self.have_run.get (command): |
| 426 | return |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 427 | |
| 428 | self.announce ("running " + command) |
| 429 | cmd_obj = self.find_command_obj (command) |
| 430 | cmd_obj.run () |
Greg Ward | c997334 | 1999-06-08 02:02:00 +0000 | [diff] [blame] | 431 | self.have_run[command] = 1 |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 432 | |
| 433 | |
| 434 | def get_command_option (self, command, option): |
| 435 | """Create a command object for 'command' if necessary, finalize |
| 436 | its option values by invoking its 'set_final_options()' |
| 437 | method, and return the value of its 'option' option. Raise |
| 438 | DistutilsOptionError if 'option' is not known for |
| 439 | that 'command'.""" |
| 440 | |
| 441 | cmd_obj = self.find_command_obj (command) |
| 442 | cmd_obj.set_final_options () |
| 443 | return cmd_obj.get_option (option) |
| 444 | try: |
| 445 | return getattr (cmd_obj, option) |
| 446 | except AttributeError: |
| 447 | raise DistutilsOptionError, \ |
| 448 | "command %s: no such option %s" % (command, option) |
| 449 | |
| 450 | |
| 451 | def get_command_options (self, command, *options): |
| 452 | """Create a command object for 'command' if necessary, finalize |
| 453 | its option values by invoking its 'set_final_options()' |
| 454 | method, and return the values of all the options listed in |
| 455 | 'options' for that command. Raise DistutilsOptionError if |
| 456 | 'option' is not known for that 'command'.""" |
| 457 | |
| 458 | cmd_obj = self.find_command_obj (command) |
| 459 | cmd_obj.set_final_options () |
| 460 | values = [] |
| 461 | try: |
| 462 | for opt in options: |
| 463 | values.append (getattr (cmd_obj, option)) |
| 464 | except AttributeError, name: |
| 465 | raise DistutilsOptionError, \ |
| 466 | "command %s: no such option %s" % (command, name) |
| 467 | |
| 468 | return tuple (values) |
| 469 | |
| 470 | # end class Distribution |
| 471 | |
| 472 | |
| 473 | class Command: |
| 474 | """Abstract base class for defining command classes, the "worker bees" |
| 475 | of the Distutils. A useful analogy for command classes is to |
| 476 | think of them as subroutines with local variables called |
| 477 | "options". The options are "declared" in 'set_initial_options()' |
| 478 | and "initialized" (given their real values) in |
| 479 | 'set_final_options()', both of which must be defined by every |
| 480 | command class. The distinction between the two is necessary |
| 481 | because option values might come from the outside world (command |
| 482 | line, option file, ...), and any options dependent on other |
| 483 | options must be computed *after* these outside influences have |
| 484 | been processed -- hence 'set_final_values()'. The "body" of the |
| 485 | subroutine, where it does all its work based on the values of its |
| 486 | options, is the 'run()' method, which must also be implemented by |
| 487 | every command class.""" |
| 488 | |
| 489 | # -- Creation/initialization methods ------------------------------- |
| 490 | |
| 491 | def __init__ (self, dist): |
| 492 | """Create and initialize a new Command object. Most importantly, |
| 493 | invokes the 'set_default_options()' method, which is the |
| 494 | real initializer and depends on the actual command being |
| 495 | instantiated.""" |
| 496 | |
| 497 | if not isinstance (dist, Distribution): |
| 498 | raise TypeError, "dist must be a Distribution instance" |
| 499 | if self.__class__ is Command: |
| 500 | raise RuntimeError, "Command is an abstract class" |
| 501 | |
| 502 | self.distribution = dist |
| 503 | self.set_default_options () |
| 504 | |
| 505 | # end __init__ () |
| 506 | |
| 507 | # Subclasses must define: |
| 508 | # set_default_options() |
| 509 | # provide default values for all options; may be overridden |
| 510 | # by Distutils client, by command-line options, or by options |
| 511 | # from option file |
| 512 | # set_final_options() |
| 513 | # decide on the final values for all options; this is called |
| 514 | # after all possible intervention from the outside world |
| 515 | # (command-line, option file, etc.) has been processed |
| 516 | # run() |
| 517 | # run the command: do whatever it is we're here to do, |
| 518 | # controlled by the command's various option values |
| 519 | |
| 520 | def set_default_options (self): |
| 521 | """Set default values for all the options that this command |
| 522 | supports. Note that these defaults may be overridden |
| 523 | by the command-line supplied by the user; thus, this is |
| 524 | not the place to code dependencies between options; generally, |
| 525 | 'set_default_options()' implementations are just a bunch |
| 526 | of "self.foo = None" assignments. |
| 527 | |
| 528 | This method must be implemented by all command classes.""" |
| 529 | |
| 530 | raise RuntimeError, \ |
| 531 | "abstract method -- subclass %s must override" % self.__class__ |
| 532 | |
| 533 | def set_final_options (self): |
| 534 | """Set final values for all the options that this command |
| 535 | supports. This is always called as late as possible, ie. |
| 536 | after any option assignments from the command-line or from |
| 537 | other commands have been done. Thus, this is the place to to |
| 538 | code option dependencies: if 'foo' depends on 'bar', then it |
| 539 | is safe to set 'foo' from 'bar' as long as 'foo' still has |
| 540 | the same value it was assigned in 'set_default_options()'. |
| 541 | |
| 542 | This method must be implemented by all command classes.""" |
| 543 | |
| 544 | raise RuntimeError, \ |
| 545 | "abstract method -- subclass %s must override" % self.__class__ |
| 546 | |
| 547 | def run (self): |
| 548 | """A command's raison d'etre: carry out the action it exists |
| 549 | to perform, controlled by the options initialized in |
| 550 | 'set_initial_options()', customized by the user and other |
| 551 | commands, and finalized in 'set_final_options()'. All |
| 552 | terminal output and filesystem interaction should be done by |
| 553 | 'run()'. |
| 554 | |
| 555 | This method must be implemented by all command classes.""" |
| 556 | |
| 557 | raise RuntimeError, \ |
| 558 | "abstract method -- subclass %s must override" % self.__class__ |
| 559 | |
| 560 | def announce (self, msg, level=1): |
| 561 | """If the Distribution instance to which this command belongs |
| 562 | has a verbosity level of greater than or equal to 'level' |
| 563 | print 'msg' to stdout.""" |
| 564 | if self.distribution.verbose >= level: |
| 565 | print msg |
| 566 | |
| 567 | |
| 568 | # -- Option query/set methods -------------------------------------- |
| 569 | |
| 570 | def get_option (self, option): |
| 571 | """Return the value of a single option for this command. Raise |
| 572 | DistutilsOptionError if 'option' is not known.""" |
| 573 | try: |
| 574 | return getattr (self, option) |
| 575 | except AttributeError: |
| 576 | raise DistutilsOptionError, \ |
| 577 | "command %s: no such option %s" % \ |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 578 | (self.get_command_name(), option) |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 579 | |
| 580 | |
| 581 | def get_options (self, *options): |
| 582 | """Return (as a tuple) the values of several options for this |
| 583 | command. Raise DistutilsOptionError if any of the options in |
| 584 | 'options' are not known.""" |
| 585 | |
| 586 | values = [] |
| 587 | try: |
| 588 | for opt in options: |
| 589 | values.append (getattr (self, opt)) |
| 590 | except AttributeError, name: |
| 591 | raise DistutilsOptionError, \ |
| 592 | "command %s: no such option %s" % \ |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 593 | (self.get_command_name(), name) |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 594 | |
| 595 | return tuple (values) |
| 596 | |
| 597 | |
| 598 | def set_option (self, option, value): |
| 599 | """Set the value of a single option for this command. Raise |
| 600 | DistutilsOptionError if 'option' is not known.""" |
| 601 | |
| 602 | if not hasattr (self, option): |
| 603 | raise DistutilsOptionError, \ |
Greg Ward | 1ae3246 | 1999-09-13 03:03:01 +0000 | [diff] [blame] | 604 | "command '%s': no such option '%s'" % \ |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 605 | (self.get_command_name(), option) |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 606 | if value is not None: |
| 607 | setattr (self, option, value) |
| 608 | |
| 609 | def set_options (self, **optval): |
| 610 | """Set the values of several options for this command. Raise |
| 611 | DistutilsOptionError if any of the options specified as |
| 612 | keyword arguments are not known.""" |
| 613 | |
| 614 | for k in optval.keys(): |
| 615 | if optval[k] is not None: |
| 616 | self.set_option (k, optval[k]) |
| 617 | |
| 618 | |
| 619 | # -- Convenience methods for commands ------------------------------ |
| 620 | |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 621 | def get_command_name (self): |
| 622 | if hasattr (self, 'command_name'): |
| 623 | return self.command_name |
| 624 | else: |
| 625 | class_name = self.__class__.__name__ |
| 626 | |
| 627 | # The re.split here returs empty strings delimited by the |
| 628 | # words we're actually interested in -- e.g. "FooBarBaz" |
| 629 | # splits to ['', 'Foo', '', 'Bar', '', 'Baz', '']. Hence |
| 630 | # the 'filter' to strip out the empties. |
| 631 | words = filter (None, re.split (r'([A-Z][a-z]+)', class_name)) |
Greg Ward | df0d335 | 1999-09-21 18:41:36 +0000 | [diff] [blame^] | 632 | self.command_name = string.join (map (string.lower, words), "_") |
| 633 | return self.command_name |
Greg Ward | 42926dd | 1999-09-08 02:41:09 +0000 | [diff] [blame] | 634 | |
| 635 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 636 | def set_undefined_options (self, src_cmd, *option_pairs): |
| 637 | """Set the values of any "undefined" options from corresponding |
| 638 | option values in some other command object. "Undefined" here |
| 639 | means "is None", which is the convention used to indicate |
| 640 | that an option has not been changed between |
| 641 | 'set_initial_values()' and 'set_final_values()'. Usually |
| 642 | called from 'set_final_values()' for options that depend on |
| 643 | some other command rather than another option of the same |
| 644 | command. 'src_cmd' is the other command from which option |
| 645 | values will be taken (a command object will be created for it |
| 646 | if necessary); the remaining arguments are |
| 647 | '(src_option,dst_option)' tuples which mean "take the value |
| 648 | of 'src_option' in the 'src_cmd' command object, and copy it |
| 649 | to 'dst_option' in the current command object".""" |
| 650 | |
| 651 | # Option_pairs: list of (src_option, dst_option) tuples |
| 652 | |
| 653 | src_cmd_obj = self.distribution.find_command_obj (src_cmd) |
| 654 | src_cmd_obj.set_final_options () |
| 655 | try: |
| 656 | for (src_option, dst_option) in option_pairs: |
| 657 | if getattr (self, dst_option) is None: |
| 658 | self.set_option (dst_option, |
| 659 | src_cmd_obj.get_option (src_option)) |
| 660 | except AttributeError, name: |
| 661 | # duh, which command? |
| 662 | raise DistutilsOptionError, "unknown option %s" % name |
| 663 | |
| 664 | |
| 665 | def set_peer_option (self, command, option, value): |
| 666 | """Attempt to simulate a command-line override of some option |
| 667 | value in another command. Creates a command object for |
| 668 | 'command' if necessary, sets 'option' to 'value', and invokes |
| 669 | 'set_final_options()' on that command object. This will only |
| 670 | have the desired effect if the command object for 'command' |
| 671 | has not previously been created. Generally this is used to |
| 672 | ensure that the options in 'command' dependent on 'option' |
| 673 | are computed, hopefully (but not necessarily) deriving from |
| 674 | 'value'. It might be more accurate to call this method |
| 675 | 'influence_dependent_peer_options()'.""" |
| 676 | |
| 677 | cmd_obj = self.distribution.find_command_obj (command) |
| 678 | cmd_obj.set_option (option, value) |
| 679 | cmd_obj.set_final_options () |
| 680 | |
| 681 | |
Greg Ward | 1ae3246 | 1999-09-13 03:03:01 +0000 | [diff] [blame] | 682 | def get_peer_option (self, command, option): |
| 683 | cmd_obj = self.distribution.find_command_obj (command) |
| 684 | return cmd_obj.get_option (option) |
| 685 | |
| 686 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 687 | def run_peer (self, command): |
| 688 | """Run some other command: uses the 'run_command()' method of |
| 689 | Distribution, which creates the command object if necessary |
| 690 | and then invokes its 'run()' method.""" |
| 691 | |
| 692 | self.distribution.run_command (command) |
| 693 | |
Greg Ward | 06ca948 | 1999-04-04 02:58:07 +0000 | [diff] [blame] | 694 | |
| 695 | # -- External world manipulation ----------------------------------- |
| 696 | |
Greg Ward | df0d335 | 1999-09-21 18:41:36 +0000 | [diff] [blame^] | 697 | def warn (self, msg): |
| 698 | sys.stderr.write ("warning: %s: %s\n" % |
| 699 | (self.get_command_name(), msg)) |
| 700 | |
| 701 | |
Greg Ward | 06ca948 | 1999-04-04 02:58:07 +0000 | [diff] [blame] | 702 | def execute (self, func, args, msg=None, level=1): |
| 703 | """Perform some action that affects the outside world (eg. |
| 704 | by writing to the filesystem). Such actions are special because |
| 705 | they should be disabled by the "dry run" flag (carried around by |
| 706 | the Command's Distribution), and should announce themselves if |
| 707 | the current verbosity level is high enough. This method takes |
| 708 | care of all that bureaucracy for you; all you have to do is |
| 709 | supply the funtion to call and an argument tuple for it (to |
| 710 | embody the "external action" being performed), a message to |
| 711 | print if the verbosity level is high enough, and an optional |
| 712 | verbosity threshold.""" |
| 713 | |
| 714 | |
| 715 | # Generate a message if we weren't passed one |
| 716 | if msg is None: |
| 717 | msg = "%s %s" % (func.__name__, `args`) |
| 718 | if msg[-2:] == ',)': # correct for singleton tuple |
| 719 | msg = msg[0:-2] + ')' |
| 720 | |
| 721 | # Print it if verbosity level is high enough |
| 722 | self.announce (msg, level) |
| 723 | |
| 724 | # And do it, as long as we're not in dry-run mode |
| 725 | if not self.distribution.dry_run: |
| 726 | apply (func, args) |
| 727 | |
| 728 | # execute() |
| 729 | |
| 730 | |
| 731 | def mkpath (self, name, mode=0777): |
| 732 | util.mkpath (name, mode, |
| 733 | self.distribution.verbose, self.distribution.dry_run) |
| 734 | |
| 735 | |
| 736 | def copy_file (self, infile, outfile, |
| 737 | preserve_mode=1, preserve_times=1, update=1, level=1): |
| 738 | """Copy a file respecting verbose and dry-run flags.""" |
| 739 | |
Greg Ward | 884df45 | 1999-05-02 21:42:05 +0000 | [diff] [blame] | 740 | return util.copy_file (infile, outfile, |
| 741 | preserve_mode, preserve_times, |
| 742 | update, self.distribution.verbose >= level, |
| 743 | self.distribution.dry_run) |
Greg Ward | 06ca948 | 1999-04-04 02:58:07 +0000 | [diff] [blame] | 744 | |
| 745 | |
| 746 | def copy_tree (self, infile, outfile, |
| 747 | preserve_mode=1, preserve_times=1, preserve_symlinks=0, |
| 748 | update=1, level=1): |
| 749 | """Copy an entire directory tree respecting verbose and dry-run |
| 750 | flags.""" |
| 751 | |
Greg Ward | 884df45 | 1999-05-02 21:42:05 +0000 | [diff] [blame] | 752 | return util.copy_tree (infile, outfile, |
| 753 | preserve_mode,preserve_times,preserve_symlinks, |
| 754 | update, self.distribution.verbose >= level, |
| 755 | self.distribution.dry_run) |
Greg Ward | 06ca948 | 1999-04-04 02:58:07 +0000 | [diff] [blame] | 756 | |
| 757 | |
Greg Ward | 1ae3246 | 1999-09-13 03:03:01 +0000 | [diff] [blame] | 758 | def move_file (self, src, dst, level=1): |
| 759 | """Move a file respecting verbose and dry-run flags.""" |
| 760 | return util.move_file (src, dst, |
| 761 | self.distribution.verbose >= level, |
| 762 | self.distribution.dry_run) |
| 763 | |
| 764 | |
Greg Ward | 06ca948 | 1999-04-04 02:58:07 +0000 | [diff] [blame] | 765 | def make_file (self, infiles, outfile, func, args, |
| 766 | exec_msg=None, skip_msg=None, level=1): |
| 767 | |
| 768 | """Special case of 'execute()' for operations that process one or |
| 769 | more input files and generate one output file. Works just like |
| 770 | 'execute()', except the operation is skipped and a different |
| 771 | message printed if 'outfile' already exists and is newer than |
| 772 | all files listed in 'infiles'.""" |
| 773 | |
| 774 | |
| 775 | if exec_msg is None: |
| 776 | exec_msg = "generating %s from %s" % \ |
| 777 | (outfile, string.join (infiles, ', ')) |
| 778 | if skip_msg is None: |
| 779 | skip_msg = "skipping %s (inputs unchanged)" % outfile |
| 780 | |
| 781 | |
| 782 | # Allow 'infiles' to be a single string |
| 783 | if type (infiles) is StringType: |
| 784 | infiles = (infiles,) |
| 785 | elif type (infiles) not in (ListType, TupleType): |
| 786 | raise TypeError, \ |
| 787 | "'infiles' must be a string, or a list or tuple of strings" |
| 788 | |
| 789 | # XXX this stuff should probably be moved off to a function |
| 790 | # in 'distutils.util' |
| 791 | from stat import * |
| 792 | |
| 793 | if os.path.exists (outfile): |
| 794 | out_mtime = os.stat (outfile)[ST_MTIME] |
| 795 | |
| 796 | # Loop over all infiles. If any infile is newer than outfile, |
| 797 | # then we'll have to regenerate outfile |
| 798 | for f in infiles: |
| 799 | in_mtime = os.stat (f)[ST_MTIME] |
| 800 | if in_mtime > out_mtime: |
| 801 | runit = 1 |
| 802 | break |
| 803 | else: |
| 804 | runit = 0 |
| 805 | |
| 806 | else: |
| 807 | runit = 1 |
| 808 | |
| 809 | # If we determined that 'outfile' must be regenerated, then |
| 810 | # perform the action that presumably regenerates it |
| 811 | if runit: |
| 812 | self.execute (func, args, exec_msg, level) |
| 813 | |
| 814 | # Otherwise, print the "skip" message |
| 815 | else: |
| 816 | self.announce (skip_msg, level) |
| 817 | |
| 818 | # make_file () |
| 819 | |
| 820 | |
| 821 | # def make_files (self, infiles, outfiles, func, args, |
| 822 | # exec_msg=None, skip_msg=None, level=1): |
| 823 | |
| 824 | # """Special case of 'execute()' for operations that process one or |
| 825 | # more input files and generate one or more output files. Works |
| 826 | # just like 'execute()', except the operation is skipped and a |
| 827 | # different message printed if all files listed in 'outfiles' |
| 828 | # already exist and are newer than all files listed in |
| 829 | # 'infiles'.""" |
| 830 | |
| 831 | # pass |
| 832 | |
| 833 | |
| 834 | |
Greg Ward | 2689e3d | 1999-03-22 14:52:19 +0000 | [diff] [blame] | 835 | # end class Command |