blob: 50e755697ba24133f7644f668b742c7dafa003f8 [file] [log] [blame]
Greg Wardfe6462c2000-04-04 01:40:52 +00001"""distutils.dist
2
3Provides the Distribution class, which represents the module distribution
4being built/installed/distributed."""
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, re
12from types import *
13from copy import copy
14from distutils.errors import *
15from distutils.fancy_getopt import fancy_getopt, print_help
16
17
18# Regex to define acceptable Distutils command names. This is not *quite*
19# the same as a Python NAME -- I don't allow leading underscores. The fact
20# that they're very similar is no coincidence; the default naming scheme is
21# to look for a Python module named after the command.
22command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
23
24
25class Distribution:
26 """The core of the Distutils. Most of the work hiding behind
27 'setup' is really done within a Distribution instance, which
28 farms the work out to the Distutils commands specified on the
29 command line.
30
31 Clients will almost never instantiate Distribution directly,
32 unless the 'setup' function is totally inadequate to their needs.
33 However, it is conceivable that a client might wish to subclass
34 Distribution for some specialized purpose, and then pass the
35 subclass to 'setup' as the 'distclass' keyword argument. If so,
36 it is necessary to respect the expectations that 'setup' has of
37 Distribution: it must have a constructor and methods
38 'parse_command_line()' and 'run_commands()' with signatures like
39 those described below."""
40
41
42 # 'global_options' describes the command-line options that may be
43 # supplied to the client (setup.py) prior to any actual commands.
44 # Eg. "./setup.py -nv" or "./setup.py --verbose" both take advantage of
45 # these global options. This list should be kept to a bare minimum,
46 # since every global option is also valid as a command option -- and we
47 # don't want to pollute the commands with too many options that they
48 # have minimal control over.
49 global_options = [('verbose', 'v',
50 "run verbosely (default)"),
51 ('quiet', 'q',
52 "run quietly (turns verbosity off)"),
53 ('dry-run', 'n',
54 "don't actually do anything"),
55 ('force', 'f',
56 "skip dependency checking between files"),
57 ('help', 'h',
58 "show this help message"),
59 ]
60 negative_opt = {'quiet': 'verbose'}
61
62
63 # -- Creation/initialization methods -------------------------------
64
65 def __init__ (self, attrs=None):
66 """Construct a new Distribution instance: initialize all the
67 attributes of a Distribution, and then uses 'attrs' (a
68 dictionary mapping attribute names to values) to assign
69 some of those attributes their "real" values. (Any attributes
70 not mentioned in 'attrs' will be assigned to some null
71 value: 0, None, an empty list or dictionary, etc.) Most
72 importantly, initialize the 'command_obj' attribute
73 to the empty dictionary; this will be filled in with real
74 command objects by 'parse_command_line()'."""
75
76 # Default values for our command-line options
77 self.verbose = 1
78 self.dry_run = 0
79 self.force = 0
80 self.help = 0
81 self.help_commands = 0
82
83 # And the "distribution meta-data" options -- these can only
84 # come from setup.py (the caller), not the command line
85 # (or a hypothetical config file).
86 self.name = None
87 self.version = None
88 self.author = None
89 self.author_email = None
90 self.maintainer = None
91 self.maintainer_email = None
92 self.url = None
93 self.licence = None
94 self.description = None
95
96 # 'cmdclass' maps command names to class objects, so we
97 # can 1) quickly figure out which class to instantiate when
98 # we need to create a new command object, and 2) have a way
99 # for the client to override command classes
100 self.cmdclass = {}
101
102 # These options are really the business of various commands, rather
103 # than of the Distribution itself. We provide aliases for them in
104 # Distribution as a convenience to the developer.
105 # dictionary.
106 self.packages = None
107 self.package_dir = None
108 self.py_modules = None
109 self.libraries = None
110 self.ext_modules = None
111 self.ext_package = None
112 self.include_dirs = None
113 self.extra_path = None
114
115 # And now initialize bookkeeping stuff that can't be supplied by
116 # the caller at all. 'command_obj' maps command names to
117 # Command instances -- that's how we enforce that every command
118 # class is a singleton.
119 self.command_obj = {}
120
121 # 'have_run' maps command names to boolean values; it keeps track
122 # of whether we have actually run a particular command, to make it
123 # cheap to "run" a command whenever we think we might need to -- if
124 # it's already been done, no need for expensive filesystem
125 # operations, we just check the 'have_run' dictionary and carry on.
126 # It's only safe to query 'have_run' for a command class that has
127 # been instantiated -- a false value will be inserted when the
128 # command object is created, and replaced with a true value when
129 # the command is succesfully run. Thus it's probably best to use
130 # '.get()' rather than a straight lookup.
131 self.have_run = {}
132
133 # Now we'll use the attrs dictionary (ultimately, keyword args from
134 # the client) to possibly override any or all of these distribution
135 # options.
136 if attrs:
137
138 # Pull out the set of command options and work on them
139 # specifically. Note that this order guarantees that aliased
140 # command options will override any supplied redundantly
141 # through the general options dictionary.
142 options = attrs.get ('options')
143 if options:
144 del attrs['options']
145 for (command, cmd_options) in options.items():
146 cmd_obj = self.find_command_obj (command)
147 for (key, val) in cmd_options.items():
148 cmd_obj.set_option (key, val)
149 # loop over commands
150 # if any command options
151
152 # Now work on the rest of the attributes. Any attribute that's
153 # not already defined is invalid!
154 for (key,val) in attrs.items():
155 if hasattr (self, key):
156 setattr (self, key, val)
157 else:
158 raise DistutilsOptionError, \
159 "invalid distribution option '%s'" % key
160
161 # __init__ ()
162
163
164 def parse_command_line (self, args):
165 """Parse the setup script's command line: set any Distribution
166 attributes tied to command-line options, create all command
167 objects, and set their options from the command-line. 'args'
168 must be a list of command-line arguments, most likely
169 'sys.argv[1:]' (see the 'setup()' function). This list is first
170 processed for "global options" -- options that set attributes of
171 the Distribution instance. Then, it is alternately scanned for
172 Distutils command and options for that command. Each new
173 command terminates the options for the previous command. The
174 allowed options for a command are determined by the 'options'
175 attribute of the command object -- thus, we instantiate (and
176 cache) every command object here, in order to access its
177 'options' attribute. Any error in that 'options' attribute
178 raises DistutilsGetoptError; any error on the command-line
179 raises DistutilsArgError. If no Distutils commands were found
180 on the command line, raises DistutilsArgError. Return true if
181 command-line successfully parsed and we should carry on with
182 executing commands; false if no errors but we shouldn't execute
183 commands (currently, this only happens if user asks for
184 help)."""
185
186 # late import because of mutual dependence between these classes
187 from distutils.cmd import Command
188
189
190 # We have to parse the command line a bit at a time -- global
191 # options, then the first command, then its options, and so on --
192 # because each command will be handled by a different class, and
193 # the options that are valid for a particular class aren't
194 # known until we instantiate the command class, which doesn't
195 # happen until we know what the command is.
196
197 self.commands = []
198 options = self.global_options + \
199 [('help-commands', None,
200 "list all available commands")]
201 args = fancy_getopt (options, self.negative_opt,
202 self, sys.argv[1:])
203
204 # User just wants a list of commands -- we'll print it out and stop
205 # processing now (ie. if they ran "setup --help-commands foo bar",
206 # we ignore "foo bar").
207 if self.help_commands:
208 self.print_commands ()
209 print
210 print usage
211 return
212
213 while args:
214 # Pull the current command from the head of the command line
215 command = args[0]
216 if not command_re.match (command):
217 raise SystemExit, "invalid command name '%s'" % command
218 self.commands.append (command)
219
220 # Make sure we have a command object to put the options into
221 # (this either pulls it out of a cache of command objects,
222 # or finds and instantiates the command class).
223 try:
224 cmd_obj = self.find_command_obj (command)
225 except DistutilsModuleError, msg:
226 raise DistutilsArgError, msg
227
228 # Require that the command class be derived from Command --
229 # that way, we can be sure that we at least have the 'run'
230 # and 'get_option' methods.
231 if not isinstance (cmd_obj, Command):
232 raise DistutilsClassError, \
233 "command class %s must subclass Command" % \
234 cmd_obj.__class__
235
236 # Also make sure that the command object provides a list of its
237 # known options
238 if not (hasattr (cmd_obj, 'user_options') and
239 type (cmd_obj.user_options) is ListType):
240 raise DistutilsClassError, \
241 ("command class %s must provide " +
242 "'user_options' attribute (a list of tuples)") % \
243 cmd_obj.__class__
244
245 # Poof! like magic, all commands support the global
246 # options too, just by adding in 'global_options'.
247 negative_opt = self.negative_opt
248 if hasattr (cmd_obj, 'negative_opt'):
249 negative_opt = copy (negative_opt)
250 negative_opt.update (cmd_obj.negative_opt)
251
252 options = self.global_options + cmd_obj.user_options
253 args = fancy_getopt (options, negative_opt,
254 cmd_obj, args[1:])
255 if cmd_obj.help:
256 print_help (self.global_options,
257 header="Global options:")
258 print
259 print_help (cmd_obj.user_options,
260 header="Options for '%s' command:" % command)
261 print
262 print usage
263 return
264
265 self.command_obj[command] = cmd_obj
266 self.have_run[command] = 0
267
268 # while args
269
270 # If the user wants help -- ie. they gave the "--help" option --
271 # give it to 'em. We do this *after* processing the commands in
272 # case they want help on any particular command, eg.
273 # "setup.py --help foo". (This isn't the documented way to
274 # get help on a command, but I support it because that's how
275 # CVS does it -- might as well be consistent.)
276 if self.help:
277 print_help (self.global_options, header="Global options:")
278 print
279
280 for command in self.commands:
281 klass = self.find_command_class (command)
282 print_help (klass.user_options,
283 header="Options for '%s' command:" % command)
284 print
285
286 print usage
287 return
288
289 # Oops, no commands found -- an end-user error
290 if not self.commands:
291 raise DistutilsArgError, "no commands supplied"
292
293 # All is well: return true
294 return 1
295
296 # parse_command_line()
297
298
299 def print_command_list (self, commands, header, max_length):
300 """Print a subset of the list of all commands -- used by
301 'print_commands()'."""
302
303 print header + ":"
304
305 for cmd in commands:
306 klass = self.cmdclass.get (cmd)
307 if not klass:
308 klass = self.find_command_class (cmd)
309 try:
310 description = klass.description
311 except AttributeError:
312 description = "(no description available)"
313
314 print " %-*s %s" % (max_length, cmd, description)
315
316 # print_command_list ()
317
318
319 def print_commands (self):
320 """Print out a help message listing all available commands with
321 a description of each. The list is divided into "standard
322 commands" (listed in distutils.command.__all__) and "extra
323 commands" (mentioned in self.cmdclass, but not a standard
324 command). The descriptions come from the command class
325 attribute 'description'."""
326
327 import distutils.command
328 std_commands = distutils.command.__all__
329 is_std = {}
330 for cmd in std_commands:
331 is_std[cmd] = 1
332
333 extra_commands = []
334 for cmd in self.cmdclass.keys():
335 if not is_std.get(cmd):
336 extra_commands.append (cmd)
337
338 max_length = 0
339 for cmd in (std_commands + extra_commands):
340 if len (cmd) > max_length:
341 max_length = len (cmd)
342
343 self.print_command_list (std_commands,
344 "Standard commands",
345 max_length)
346 if extra_commands:
347 print
348 self.print_command_list (extra_commands,
349 "Extra commands",
350 max_length)
351
352 # print_commands ()
353
354
355
356 # -- Command class/object methods ----------------------------------
357
358 # This is a method just so it can be overridden if desired; it doesn't
359 # actually use or change any attributes of the Distribution instance.
360 def find_command_class (self, command):
361 """Given a command, derives the names of the module and class
362 expected to implement the command: eg. 'foo_bar' becomes
363 'distutils.command.foo_bar' (the module) and 'FooBar' (the
364 class within that module). Loads the module, extracts the
365 class from it, and returns the class object.
366
367 Raises DistutilsModuleError with a semi-user-targeted error
368 message if the expected module could not be loaded, or the
369 expected class was not found in it."""
370
371 module_name = 'distutils.command.' + command
372 klass_name = command
373
374 try:
375 __import__ (module_name)
376 module = sys.modules[module_name]
377 except ImportError:
378 raise DistutilsModuleError, \
379 "invalid command '%s' (no module named '%s')" % \
380 (command, module_name)
381
382 try:
383 klass = vars(module)[klass_name]
384 except KeyError:
385 raise DistutilsModuleError, \
386 "invalid command '%s' (no class '%s' in module '%s')" \
387 % (command, klass_name, module_name)
388
389 return klass
390
391 # find_command_class ()
392
393
394 def create_command_obj (self, command):
395 """Figure out the class that should implement a command,
396 instantiate it, cache and return the new "command object".
397 The "command class" is determined either by looking it up in
398 the 'cmdclass' attribute (this is the mechanism whereby
399 clients may override default Distutils commands or add their
400 own), or by calling the 'find_command_class()' method (if the
401 command name is not in 'cmdclass'."""
402
403 # Determine the command class -- either it's in the command_class
404 # dictionary, or we have to divine the module and class name
405 klass = self.cmdclass.get(command)
406 if not klass:
407 klass = self.find_command_class (command)
408 self.cmdclass[command] = klass
409
410 # Found the class OK -- instantiate it
411 cmd_obj = klass (self)
412 return cmd_obj
413
414
415 def find_command_obj (self, command, create=1):
416 """Look up and return a command object in the cache maintained by
417 'create_command_obj()'. If none found, the action taken
418 depends on 'create': if true (the default), create a new
419 command object by calling 'create_command_obj()' and return
420 it; otherwise, return None. If 'command' is an invalid
421 command name, then DistutilsModuleError will be raised."""
422
423 cmd_obj = self.command_obj.get (command)
424 if not cmd_obj and create:
425 cmd_obj = self.create_command_obj (command)
426 self.command_obj[command] = cmd_obj
427
428 return cmd_obj
429
430
431 # -- Methods that operate on the Distribution ----------------------
432
433 def announce (self, msg, level=1):
434 """Print 'msg' if 'level' is greater than or equal to the verbosity
435 level recorded in the 'verbose' attribute (which, currently,
436 can be only 0 or 1)."""
437
438 if self.verbose >= level:
439 print msg
440
441
442 def run_commands (self):
443 """Run each command that was seen on the client command line.
444 Uses the list of commands found and cache of command objects
445 created by 'create_command_obj()'."""
446
447 for cmd in self.commands:
448 self.run_command (cmd)
449
450
451 def get_option (self, option):
452 """Return the value of a distribution option. Raise
453 DistutilsOptionError if 'option' is not known."""
454
455 try:
456 return getattr (self, opt)
457 except AttributeError:
458 raise DistutilsOptionError, \
459 "unknown distribution option %s" % option
460
461
462 def get_options (self, *options):
463 """Return (as a tuple) the values of several distribution
464 options. Raise DistutilsOptionError if any element of
465 'options' is not known."""
466
467 values = []
468 try:
469 for opt in options:
470 values.append (getattr (self, opt))
471 except AttributeError, name:
472 raise DistutilsOptionError, \
473 "unknown distribution option %s" % name
474
475 return tuple (values)
476
477
478 # -- Methods that operate on its Commands --------------------------
479
480 def run_command (self, command):
481
482 """Do whatever it takes to run a command (including nothing at all,
483 if the command has already been run). Specifically: if we have
484 already created and run the command named by 'command', return
485 silently without doing anything. If the command named by
486 'command' doesn't even have a command object yet, create one.
487 Then invoke 'run()' on that command object (or an existing
488 one)."""
489
490 # Already been here, done that? then return silently.
491 if self.have_run.get (command):
492 return
493
494 self.announce ("running " + command)
495 cmd_obj = self.find_command_obj (command)
496 cmd_obj.ensure_ready ()
497 cmd_obj.run ()
498 self.have_run[command] = 1
499
500
501 def get_command_option (self, command, option):
502 """Create a command object for 'command' if necessary, ensure that
503 its option values are all set to their final values, and return
504 the value of its 'option' option. Raise DistutilsOptionError if
505 'option' is not known for that 'command'."""
506
507 cmd_obj = self.find_command_obj (command)
508 cmd_obj.ensure_ready ()
509 return cmd_obj.get_option (option)
510 try:
511 return getattr (cmd_obj, option)
512 except AttributeError:
513 raise DistutilsOptionError, \
514 "command %s: no such option %s" % (command, option)
515
516
517 def get_command_options (self, command, *options):
518 """Create a command object for 'command' if necessary, ensure that
519 its option values are all set to their final values, and return
520 a tuple containing the values of all the options listed in
521 'options' for that command. Raise DistutilsOptionError if any
522 invalid option is supplied in 'options'."""
523
524 cmd_obj = self.find_command_obj (command)
525 cmd_obj.ensure_ready ()
526 values = []
527 try:
528 for opt in options:
529 values.append (getattr (cmd_obj, option))
530 except AttributeError, name:
531 raise DistutilsOptionError, \
532 "command %s: no such option %s" % (command, name)
533
534 return tuple (values)
535
536
537 # -- Distribution query methods ------------------------------------
538
539 def has_pure_modules (self):
540 return len (self.packages or self.py_modules or []) > 0
541
542 def has_ext_modules (self):
543 return self.ext_modules and len (self.ext_modules) > 0
544
545 def has_c_libraries (self):
546 return self.libraries and len (self.libraries) > 0
547
548 def has_modules (self):
549 return self.has_pure_modules() or self.has_ext_modules()
550
551 def is_pure (self):
552 return (self.has_pure_modules() and
553 not self.has_ext_modules() and
554 not self.has_c_libraries())
555
556 def get_name (self):
557 return self.name or "UNKNOWN"
558
559 def get_full_name (self):
560 return "%s-%s" % ((self.name or "UNKNOWN"), (self.version or "???"))
561
562# class Distribution
563
564
565if __name__ == "__main__":
566 dist = Distribution ()
567 print "ok"