You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

769 lines
32 KiB

15 years ago
15 years ago
  1. """Class representing the project being built/installed/etc."""
  2. import os
  3. import re
  4. from packaging import logger
  5. from packaging.util import strtobool, resolve_name
  6. from packaging.config import Config
  7. from packaging.errors import (PackagingOptionError, PackagingArgError,
  8. PackagingModuleError, PackagingClassError)
  9. from packaging.command import get_command_class, STANDARD_COMMANDS
  10. from packaging.command.cmd import Command
  11. from packaging.metadata import Metadata
  12. from packaging.fancy_getopt import FancyGetopt
  13. # Regex to define acceptable Packaging command names. This is not *quite*
  14. # the same as a Python name -- leading underscores are not allowed. The fact
  15. # that they're very similar is no coincidence: the default naming scheme is
  16. # to look for a Python module named after the command.
  17. command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
  18. USAGE = """\
  19. usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
  20. or: %(script)s --help [cmd1 cmd2 ...]
  21. or: %(script)s --help-commands
  22. or: %(script)s cmd --help
  23. """
  24. def gen_usage(script_name):
  25. script = os.path.basename(script_name)
  26. return USAGE % {'script': script}
  27. class Distribution:
  28. """Class used to represent a project and work with it.
  29. Most of the work hiding behind 'pysetup run' is really done within a
  30. Distribution instance, which farms the work out to the commands
  31. specified on the command line.
  32. """
  33. # 'global_options' describes the command-line options that may be
  34. # supplied to the setup script prior to any actual commands.
  35. # Eg. "pysetup run -n" or "pysetup run --dry-run" both take advantage of
  36. # these global options. This list should be kept to a bare minimum,
  37. # since every global option is also valid as a command option -- and we
  38. # don't want to pollute the commands with too many options that they
  39. # have minimal control over.
  40. global_options = [
  41. ('dry-run', 'n', "don't actually do anything"),
  42. ('help', 'h', "show detailed help message"),
  43. ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'),
  44. ]
  45. # 'common_usage' is a short (2-3 line) string describing the common
  46. # usage of the setup script.
  47. common_usage = """\
  48. Common commands: (see '--help-commands' for more)
  49. pysetup run build will build the project underneath 'build/'
  50. pysetup run install will install the project
  51. """
  52. # options that are not propagated to the commands
  53. display_options = [
  54. ('help-commands', None,
  55. "list all available commands"),
  56. ('use-2to3', None,
  57. "use 2to3 to make source python 3.x compatible"),
  58. ('convert-2to3-doctests', None,
  59. "use 2to3 to convert doctests in separate text files"),
  60. ]
  61. display_option_names = [x[0].replace('-', '_') for x in display_options]
  62. # negative options are options that exclude other options
  63. negative_opt = {}
  64. # -- Creation/initialization methods -------------------------------
  65. def __init__(self, attrs=None):
  66. """Construct a new Distribution instance: initialize all the
  67. attributes of a Distribution, and then use 'attrs' (a dictionary
  68. mapping attribute names to values) to assign some of those
  69. attributes their "real" values. (Any attributes not mentioned in
  70. 'attrs' will be assigned to some null value: 0, None, an empty list
  71. or dictionary, etc.) Most importantly, initialize the
  72. 'command_obj' attribute to the empty dictionary; this will be
  73. filled in with real command objects by 'parse_command_line()'.
  74. """
  75. # Default values for our command-line options
  76. self.dry_run = False
  77. self.help = False
  78. for attr in self.display_option_names:
  79. setattr(self, attr, False)
  80. # Store the configuration
  81. self.config = Config(self)
  82. # Store the distribution metadata (name, version, author, and so
  83. # forth) in a separate object -- we're getting to have enough
  84. # information here (and enough command-line options) that it's
  85. # worth it.
  86. self.metadata = Metadata()
  87. # 'cmdclass' maps command names to class objects, so we
  88. # can 1) quickly figure out which class to instantiate when
  89. # we need to create a new command object, and 2) have a way
  90. # for the setup script to override command classes
  91. self.cmdclass = {}
  92. # 'script_name' and 'script_args' are usually set to sys.argv[0]
  93. # and sys.argv[1:], but they can be overridden when the caller is
  94. # not necessarily a setup script run from the command line.
  95. self.script_name = None
  96. self.script_args = None
  97. # 'command_options' is where we store command options between
  98. # parsing them (from config files, the command line, etc.) and when
  99. # they are actually needed -- ie. when the command in question is
  100. # instantiated. It is a dictionary of dictionaries of 2-tuples:
  101. # command_options = { command_name : { option : (source, value) } }
  102. self.command_options = {}
  103. # 'dist_files' is the list of (command, pyversion, file) that
  104. # have been created by any dist commands run so far. This is
  105. # filled regardless of whether the run is dry or not. pyversion
  106. # gives sysconfig.get_python_version() if the dist file is
  107. # specific to a Python version, 'any' if it is good for all
  108. # Python versions on the target platform, and '' for a source
  109. # file. pyversion should not be used to specify minimum or
  110. # maximum required Python versions; use the metainfo for that
  111. # instead.
  112. self.dist_files = []
  113. # These options are really the business of various commands, rather
  114. # than of the Distribution itself. We provide aliases for them in
  115. # Distribution as a convenience to the developer.
  116. self.packages = []
  117. self.package_data = {}
  118. self.package_dir = None
  119. self.py_modules = []
  120. self.libraries = []
  121. self.headers = []
  122. self.ext_modules = []
  123. self.ext_package = None
  124. self.include_dirs = []
  125. self.extra_path = None
  126. self.scripts = []
  127. self.data_files = {}
  128. self.password = ''
  129. self.use_2to3 = False
  130. self.convert_2to3_doctests = []
  131. self.extra_files = []
  132. # And now initialize bookkeeping stuff that can't be supplied by
  133. # the caller at all. 'command_obj' maps command names to
  134. # Command instances -- that's how we enforce that every command
  135. # class is a singleton.
  136. self.command_obj = {}
  137. # 'have_run' maps command names to boolean values; it keeps track
  138. # of whether we have actually run a particular command, to make it
  139. # cheap to "run" a command whenever we think we might need to -- if
  140. # it's already been done, no need for expensive filesystem
  141. # operations, we just check the 'have_run' dictionary and carry on.
  142. # It's only safe to query 'have_run' for a command class that has
  143. # been instantiated -- a false value will be inserted when the
  144. # command object is created, and replaced with a true value when
  145. # the command is successfully run. Thus it's probably best to use
  146. # '.get()' rather than a straight lookup.
  147. self.have_run = {}
  148. # Now we'll use the attrs dictionary (ultimately, keyword args from
  149. # the setup script) to possibly override any or all of these
  150. # distribution options.
  151. if attrs is not None:
  152. # Pull out the set of command options and work on them
  153. # specifically. Note that this order guarantees that aliased
  154. # command options will override any supplied redundantly
  155. # through the general options dictionary.
  156. options = attrs.get('options')
  157. if options is not None:
  158. del attrs['options']
  159. for command, cmd_options in options.items():
  160. opt_dict = self.get_option_dict(command)
  161. for opt, val in cmd_options.items():
  162. opt_dict[opt] = ("setup script", val)
  163. # Now work on the rest of the attributes. Any attribute that's
  164. # not already defined is invalid!
  165. for key, val in attrs.items():
  166. if self.metadata.is_metadata_field(key):
  167. self.metadata[key] = val
  168. elif hasattr(self, key):
  169. setattr(self, key, val)
  170. else:
  171. logger.warning(
  172. 'unknown argument given to Distribution: %r', key)
  173. # no-user-cfg is handled before other command line args
  174. # because other args override the config files, and this
  175. # one is needed before we can load the config files.
  176. # If attrs['script_args'] wasn't passed, assume false.
  177. #
  178. # This also make sure we just look at the global options
  179. self.want_user_cfg = True
  180. if self.script_args is not None:
  181. for arg in self.script_args:
  182. if not arg.startswith('-'):
  183. break
  184. if arg == '--no-user-cfg':
  185. self.want_user_cfg = False
  186. break
  187. self.finalize_options()
  188. def get_option_dict(self, command):
  189. """Get the option dictionary for a given command. If that
  190. command's option dictionary hasn't been created yet, then create it
  191. and return the new dictionary; otherwise, return the existing
  192. option dictionary.
  193. """
  194. d = self.command_options.get(command)
  195. if d is None:
  196. d = self.command_options[command] = {}
  197. return d
  198. def get_fullname(self, filesafe=False):
  199. return self.metadata.get_fullname(filesafe)
  200. def dump_option_dicts(self, header=None, commands=None, indent=""):
  201. from pprint import pformat
  202. if commands is None: # dump all command option dicts
  203. commands = sorted(self.command_options)
  204. if header is not None:
  205. logger.info(indent + header)
  206. indent = indent + " "
  207. if not commands:
  208. logger.info(indent + "no commands known yet")
  209. return
  210. for cmd_name in commands:
  211. opt_dict = self.command_options.get(cmd_name)
  212. if opt_dict is None:
  213. logger.info(indent + "no option dict for %r command",
  214. cmd_name)
  215. else:
  216. logger.info(indent + "option dict for %r command:", cmd_name)
  217. out = pformat(opt_dict)
  218. for line in out.split('\n'):
  219. logger.info(indent + " " + line)
  220. # -- Config file finding/parsing methods ---------------------------
  221. # XXX to be removed
  222. def parse_config_files(self, filenames=None):
  223. return self.config.parse_config_files(filenames)
  224. def find_config_files(self):
  225. return self.config.find_config_files()
  226. # -- Command-line parsing methods ----------------------------------
  227. def parse_command_line(self):
  228. """Parse the setup script's command line, taken from the
  229. 'script_args' instance attribute (which defaults to 'sys.argv[1:]'
  230. -- see 'setup()' in run.py). This list is first processed for
  231. "global options" -- options that set attributes of the Distribution
  232. instance. Then, it is alternately scanned for Packaging commands
  233. and options for that command. Each new command terminates the
  234. options for the previous command. The allowed options for a
  235. command are determined by the 'user_options' attribute of the
  236. command class -- thus, we have to be able to load command classes
  237. in order to parse the command line. Any error in that 'options'
  238. attribute raises PackagingGetoptError; any error on the
  239. command line raises PackagingArgError. If no Packaging commands
  240. were found on the command line, raises PackagingArgError. Return
  241. true if command line was successfully parsed and we should carry
  242. on with executing commands; false if no errors but we shouldn't
  243. execute commands (currently, this only happens if user asks for
  244. help).
  245. """
  246. #
  247. # We now have enough information to show the Macintosh dialog
  248. # that allows the user to interactively specify the "command line".
  249. #
  250. toplevel_options = self._get_toplevel_options()
  251. # We have to parse the command line a bit at a time -- global
  252. # options, then the first command, then its options, and so on --
  253. # because each command will be handled by a different class, and
  254. # the options that are valid for a particular class aren't known
  255. # until we have loaded the command class, which doesn't happen
  256. # until we know what the command is.
  257. self.commands = []
  258. parser = FancyGetopt(toplevel_options + self.display_options)
  259. parser.set_negative_aliases(self.negative_opt)
  260. args = parser.getopt(args=self.script_args, object=self)
  261. option_order = parser.get_option_order()
  262. # for display options we return immediately
  263. if self.handle_display_options(option_order):
  264. return
  265. while args:
  266. args = self._parse_command_opts(parser, args)
  267. if args is None: # user asked for help (and got it)
  268. return
  269. # Handle the cases of --help as a "global" option, ie.
  270. # "pysetup run --help" and "pysetup run --help command ...". For the
  271. # former, we show global options (--dry-run, etc.)
  272. # and display-only options (--name, --version, etc.); for the
  273. # latter, we omit the display-only options and show help for
  274. # each command listed on the command line.
  275. if self.help:
  276. self._show_help(parser,
  277. display_options=len(self.commands) == 0,
  278. commands=self.commands)
  279. return
  280. return True
  281. def _get_toplevel_options(self):
  282. """Return the non-display options recognized at the top level.
  283. This includes options that are recognized *only* at the top
  284. level as well as options recognized for commands.
  285. """
  286. return self.global_options
  287. def _parse_command_opts(self, parser, args):
  288. """Parse the command-line options for a single command.
  289. 'parser' must be a FancyGetopt instance; 'args' must be the list
  290. of arguments, starting with the current command (whose options
  291. we are about to parse). Returns a new version of 'args' with
  292. the next command at the front of the list; will be the empty
  293. list if there are no more commands on the command line. Returns
  294. None if the user asked for help on this command.
  295. """
  296. # Pull the current command from the head of the command line
  297. command = args[0]
  298. if not command_re.match(command):
  299. raise SystemExit("invalid command name %r" % command)
  300. self.commands.append(command)
  301. # Dig up the command class that implements this command, so we
  302. # 1) know that it's a valid command, and 2) know which options
  303. # it takes.
  304. try:
  305. cmd_class = get_command_class(command)
  306. except PackagingModuleError as msg:
  307. raise PackagingArgError(msg)
  308. # XXX We want to push this in packaging.command
  309. #
  310. # Require that the command class be derived from Command -- want
  311. # to be sure that the basic "command" interface is implemented.
  312. for meth in ('initialize_options', 'finalize_options', 'run'):
  313. if hasattr(cmd_class, meth):
  314. continue
  315. raise PackagingClassError(
  316. 'command %r must implement %r' % (cmd_class, meth))
  317. # Also make sure that the command object provides a list of its
  318. # known options.
  319. if not (hasattr(cmd_class, 'user_options') and
  320. isinstance(cmd_class.user_options, list)):
  321. raise PackagingClassError(
  322. "command class %s must provide "
  323. "'user_options' attribute (a list of tuples)" % cmd_class)
  324. # If the command class has a list of negative alias options,
  325. # merge it in with the global negative aliases.
  326. negative_opt = self.negative_opt
  327. if hasattr(cmd_class, 'negative_opt'):
  328. negative_opt = negative_opt.copy()
  329. negative_opt.update(cmd_class.negative_opt)
  330. # Check for help_options in command class. They have a different
  331. # format (tuple of four) so we need to preprocess them here.
  332. if (hasattr(cmd_class, 'help_options') and
  333. isinstance(cmd_class.help_options, list)):
  334. help_options = cmd_class.help_options[:]
  335. else:
  336. help_options = []
  337. # All commands support the global options too, just by adding
  338. # in 'global_options'.
  339. parser.set_option_table(self.global_options +
  340. cmd_class.user_options +
  341. help_options)
  342. parser.set_negative_aliases(negative_opt)
  343. args, opts = parser.getopt(args[1:])
  344. if hasattr(opts, 'help') and opts.help:
  345. self._show_help(parser, display_options=False,
  346. commands=[cmd_class])
  347. return
  348. if (hasattr(cmd_class, 'help_options') and
  349. isinstance(cmd_class.help_options, list)):
  350. help_option_found = False
  351. for help_option, short, desc, func in cmd_class.help_options:
  352. if hasattr(opts, help_option.replace('-', '_')):
  353. help_option_found = True
  354. if callable(func):
  355. func()
  356. else:
  357. raise PackagingClassError(
  358. "invalid help function %r for help option %r: "
  359. "must be a callable object (function, etc.)"
  360. % (func, help_option))
  361. if help_option_found:
  362. return
  363. # Put the options from the command line into their official
  364. # holding pen, the 'command_options' dictionary.
  365. opt_dict = self.get_option_dict(command)
  366. for name, value in vars(opts).items():
  367. opt_dict[name] = ("command line", value)
  368. return args
  369. def finalize_options(self):
  370. """Set final values for all the options on the Distribution
  371. instance, analogous to the .finalize_options() method of Command
  372. objects.
  373. """
  374. if getattr(self, 'convert_2to3_doctests', None):
  375. self.convert_2to3_doctests = [os.path.join(p)
  376. for p in self.convert_2to3_doctests]
  377. else:
  378. self.convert_2to3_doctests = []
  379. def _show_help(self, parser, global_options=True, display_options=True,
  380. commands=[]):
  381. """Show help for the setup script command line in the form of
  382. several lists of command-line options. 'parser' should be a
  383. FancyGetopt instance; do not expect it to be returned in the
  384. same state, as its option table will be reset to make it
  385. generate the correct help text.
  386. If 'global_options' is true, lists the global options:
  387. --dry-run, etc. If 'display_options' is true, lists
  388. the "display-only" options: --help-commands. Finally,
  389. lists per-command help for every command name or command class
  390. in 'commands'.
  391. """
  392. if global_options:
  393. if display_options:
  394. options = self._get_toplevel_options()
  395. else:
  396. options = self.global_options
  397. parser.set_option_table(options)
  398. parser.print_help(self.common_usage + "\nGlobal options:")
  399. print()
  400. if display_options:
  401. parser.set_option_table(self.display_options)
  402. parser.print_help(
  403. "Information display options (just display " +
  404. "information, ignore any commands)")
  405. print()
  406. for command in self.commands:
  407. if isinstance(command, type) and issubclass(command, Command):
  408. cls = command
  409. else:
  410. cls = get_command_class(command)
  411. if (hasattr(cls, 'help_options') and
  412. isinstance(cls.help_options, list)):
  413. parser.set_option_table(cls.user_options + cls.help_options)
  414. else:
  415. parser.set_option_table(cls.user_options)
  416. parser.print_help("Options for %r command:" % cls.__name__)
  417. print()
  418. print(gen_usage(self.script_name))
  419. def handle_display_options(self, option_order):
  420. """If there were any non-global "display-only" options
  421. (--help-commands) on the command line, display the requested info and
  422. return true; else return false.
  423. """
  424. # User just wants a list of commands -- we'll print it out and stop
  425. # processing now (ie. if they ran "setup --help-commands foo bar",
  426. # we ignore "foo bar").
  427. if self.help_commands:
  428. self.print_commands()
  429. print()
  430. print(gen_usage(self.script_name))
  431. return True
  432. # If user supplied any of the "display metadata" options, then
  433. # display that metadata in the order in which the user supplied the
  434. # metadata options.
  435. any_display_options = False
  436. is_display_option = set()
  437. for option in self.display_options:
  438. is_display_option.add(option[0])
  439. for opt, val in option_order:
  440. if val and opt in is_display_option:
  441. opt = opt.replace('-', '_')
  442. value = self.metadata[opt]
  443. if opt in ('keywords', 'platform'):
  444. print(','.join(value))
  445. elif opt in ('classifier', 'provides', 'requires',
  446. 'obsoletes'):
  447. print('\n'.join(value))
  448. else:
  449. print(value)
  450. any_display_options = True
  451. return any_display_options
  452. def print_command_list(self, commands, header, max_length):
  453. """Print a subset of the list of all commands -- used by
  454. 'print_commands()'.
  455. """
  456. print(header + ":")
  457. for cmd in commands:
  458. cls = self.cmdclass.get(cmd) or get_command_class(cmd)
  459. description = getattr(cls, 'description',
  460. '(no description available)')
  461. print(" %-*s %s" % (max_length, cmd, description))
  462. def _get_command_groups(self):
  463. """Helper function to retrieve all the command class names divided
  464. into standard commands (listed in
  465. packaging.command.STANDARD_COMMANDS) and extra commands (given in
  466. self.cmdclass and not standard commands).
  467. """
  468. extra_commands = [cmd for cmd in self.cmdclass
  469. if cmd not in STANDARD_COMMANDS]
  470. return STANDARD_COMMANDS, extra_commands
  471. def print_commands(self):
  472. """Print out a help message listing all available commands with a
  473. description of each. The list is divided into standard commands
  474. (listed in packaging.command.STANDARD_COMMANDS) and extra commands
  475. (given in self.cmdclass and not standard commands). The
  476. descriptions come from the command class attribute
  477. 'description'.
  478. """
  479. std_commands, extra_commands = self._get_command_groups()
  480. max_length = 0
  481. for cmd in (std_commands + extra_commands):
  482. if len(cmd) > max_length:
  483. max_length = len(cmd)
  484. self.print_command_list(std_commands,
  485. "Standard commands",
  486. max_length)
  487. if extra_commands:
  488. print()
  489. self.print_command_list(extra_commands,
  490. "Extra commands",
  491. max_length)
  492. # -- Command class/object methods ----------------------------------
  493. def get_command_obj(self, command, create=True):
  494. """Return the command object for 'command'. Normally this object
  495. is cached on a previous call to 'get_command_obj()'; if no command
  496. object for 'command' is in the cache, then we either create and
  497. return it (if 'create' is true) or return None.
  498. """
  499. cmd_obj = self.command_obj.get(command)
  500. if not cmd_obj and create:
  501. logger.debug("Distribution.get_command_obj(): "
  502. "creating %r command object", command)
  503. cls = get_command_class(command)
  504. cmd_obj = self.command_obj[command] = cls(self)
  505. self.have_run[command] = 0
  506. # Set any options that were supplied in config files or on the
  507. # command line. (XXX support for error reporting is suboptimal
  508. # here: errors aren't reported until finalize_options is called,
  509. # which means we won't report the source of the error.)
  510. options = self.command_options.get(command)
  511. if options:
  512. self._set_command_options(cmd_obj, options)
  513. return cmd_obj
  514. def _set_command_options(self, command_obj, option_dict=None):
  515. """Set the options for 'command_obj' from 'option_dict'. Basically
  516. this means copying elements of a dictionary ('option_dict') to
  517. attributes of an instance ('command').
  518. 'command_obj' must be a Command instance. If 'option_dict' is not
  519. supplied, uses the standard option dictionary for this command
  520. (from 'self.command_options').
  521. """
  522. command_name = command_obj.get_command_name()
  523. if option_dict is None:
  524. option_dict = self.get_option_dict(command_name)
  525. logger.debug(" setting options for %r command:", command_name)
  526. for option, (source, value) in option_dict.items():
  527. logger.debug(" %s = %s (from %s)", option, value, source)
  528. try:
  529. bool_opts = [x.replace('-', '_')
  530. for x in command_obj.boolean_options]
  531. except AttributeError:
  532. bool_opts = []
  533. try:
  534. neg_opt = command_obj.negative_opt
  535. except AttributeError:
  536. neg_opt = {}
  537. try:
  538. is_string = isinstance(value, str)
  539. if option in neg_opt and is_string:
  540. setattr(command_obj, neg_opt[option], not strtobool(value))
  541. elif option in bool_opts and is_string:
  542. setattr(command_obj, option, strtobool(value))
  543. elif hasattr(command_obj, option):
  544. setattr(command_obj, option, value)
  545. else:
  546. raise PackagingOptionError(
  547. "error in %s: command %r has no such option %r" %
  548. (source, command_name, option))
  549. except ValueError as msg:
  550. raise PackagingOptionError(msg)
  551. def reinitialize_command(self, command, reinit_subcommands=False):
  552. """Reinitializes a command to the state it was in when first
  553. returned by 'get_command_obj()': i.e., initialized but not yet
  554. finalized. This provides the opportunity to sneak option
  555. values in programmatically, overriding or supplementing
  556. user-supplied values from the config files and command line.
  557. You'll have to re-finalize the command object (by calling
  558. 'finalize_options()' or 'ensure_finalized()') before using it for
  559. real.
  560. 'command' should be a command name (string) or command object. If
  561. 'reinit_subcommands' is true, also reinitializes the command's
  562. sub-commands, as declared by the 'sub_commands' class attribute (if
  563. it has one). See the "install_dist" command for an example. Only
  564. reinitializes the sub-commands that actually matter, i.e. those
  565. whose test predicate return true.
  566. Returns the reinitialized command object. It will be the same
  567. object as the one stored in the self.command_obj attribute.
  568. """
  569. if not isinstance(command, Command):
  570. command_name = command
  571. command = self.get_command_obj(command_name)
  572. else:
  573. command_name = command.get_command_name()
  574. if not command.finalized:
  575. return command
  576. command.initialize_options()
  577. self.have_run[command_name] = 0
  578. command.finalized = False
  579. self._set_command_options(command)
  580. if reinit_subcommands:
  581. for sub in command.get_sub_commands():
  582. self.reinitialize_command(sub, reinit_subcommands)
  583. return command
  584. # -- Methods that operate on the Distribution ----------------------
  585. def run_commands(self):
  586. """Run each command that was seen on the setup script command line.
  587. Uses the list of commands found and cache of command objects
  588. created by 'get_command_obj()'.
  589. """
  590. for cmd in self.commands:
  591. self.run_command(cmd)
  592. # -- Methods that operate on its Commands --------------------------
  593. def run_command(self, command, options=None):
  594. """Do whatever it takes to run a command (including nothing at all,
  595. if the command has already been run). Specifically: if we have
  596. already created and run the command named by 'command', return
  597. silently without doing anything. If the command named by 'command'
  598. doesn't even have a command object yet, create one. Then invoke
  599. 'run()' on that command object (or an existing one).
  600. """
  601. # Already been here, done that? then return silently.
  602. if self.have_run.get(command):
  603. return
  604. if options is not None:
  605. self.command_options[command] = options
  606. cmd_obj = self.get_command_obj(command)
  607. cmd_obj.ensure_finalized()
  608. self.run_command_hooks(cmd_obj, 'pre_hook')
  609. logger.info("running %s", command)
  610. cmd_obj.run()
  611. self.run_command_hooks(cmd_obj, 'post_hook')
  612. self.have_run[command] = 1
  613. def run_command_hooks(self, cmd_obj, hook_kind):
  614. """Run hooks registered for that command and phase.
  615. *cmd_obj* is a finalized command object; *hook_kind* is either
  616. 'pre_hook' or 'post_hook'.
  617. """
  618. if hook_kind not in ('pre_hook', 'post_hook'):
  619. raise ValueError('invalid hook kind: %r' % hook_kind)
  620. hooks = getattr(cmd_obj, hook_kind, None)
  621. if hooks is None:
  622. return
  623. for hook in hooks.values():
  624. if isinstance(hook, str):
  625. try:
  626. hook_obj = resolve_name(hook)
  627. except ImportError as e:
  628. raise PackagingModuleError(e)
  629. else:
  630. hook_obj = hook
  631. if not callable(hook_obj):
  632. raise PackagingOptionError('hook %r is not callable' % hook)
  633. logger.info('running %s %s for command %s',
  634. hook_kind, hook, cmd_obj.get_command_name())
  635. hook_obj(cmd_obj)
  636. # -- Distribution query methods ------------------------------------
  637. def has_pure_modules(self):
  638. return len(self.packages or self.py_modules or []) > 0
  639. def has_ext_modules(self):
  640. return self.ext_modules and len(self.ext_modules) > 0
  641. def has_c_libraries(self):
  642. return self.libraries and len(self.libraries) > 0
  643. def has_modules(self):
  644. return self.has_pure_modules() or self.has_ext_modules()
  645. def has_headers(self):
  646. return self.headers and len(self.headers) > 0
  647. def has_scripts(self):
  648. return self.scripts and len(self.scripts) > 0
  649. def has_data_files(self):
  650. return self.data_files and len(self.data_files) > 0
  651. def is_pure(self):
  652. return (self.has_pure_modules() and
  653. not self.has_ext_modules() and
  654. not self.has_c_libraries())