codekingpro/portable-devtools
114k
1"""distutils.core2 3The only module that needs to be imported to use the Distutils; provides4the 'setup' function (which is to be called from the setup script). Also5indirectly provides the Distribution and Command classes, although they are6really defined in distutils.dist and distutils.cmd.7"""8 9import os10import sys11import tokenize12 13from .cmd import Command14from .config import PyPIRCCommand15from .debug import DEBUG16 17# Mainly import these so setup scripts can "from distutils.core import" them.18from .dist import Distribution19from .errors import (20 CCompilerError,21 DistutilsArgError,22 DistutilsError,23 DistutilsSetupError,24)25from .extension import Extension26 27__all__ = ['Distribution', 'Command', 'PyPIRCCommand', 'Extension', 'setup']28 29# This is a barebones help message generated displayed when the user30# runs the setup script with no arguments at all. More useful help31# is generated with various --help options: global help, list commands,32# and per-command help.33USAGE = """\34usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]35 or: %(script)s --help [cmd1 cmd2 ...]36 or: %(script)s --help-commands37 or: %(script)s cmd --help38"""39 40 41def gen_usage(script_name):42 script = os.path.basename(script_name)43 return USAGE % locals()44 45 46# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'.47_setup_stop_after = None48_setup_distribution = None49 50# Legal keyword arguments for the setup() function51setup_keywords = (52 'distclass',53 'script_name',54 'script_args',55 'options',56 'name',57 'version',58 'author',59 'author_email',60 'maintainer',61 'maintainer_email',62 'url',63 'license',64 'description',65 'long_description',66 'keywords',67 'platforms',68 'classifiers',69 'download_url',70 'requires',71 'provides',72 'obsoletes',73)74 75# Legal keyword arguments for the Extension constructor76extension_keywords = (77 'name',78 'sources',79 'include_dirs',80 'define_macros',81 'undef_macros',82 'library_dirs',83 'libraries',84 'runtime_library_dirs',85 'extra_objects',86 'extra_compile_args',87 'extra_link_args',88 'swig_opts',89 'export_symbols',90 'depends',91 'language',92)93 94 95def setup(**attrs): # noqa: C90196 """The gateway to the Distutils: do everything your setup script needs97 to do, in a highly flexible and user-driven way. Briefly: create a98 Distribution instance; find and parse config files; parse the command99 line; run each Distutils command found there, customized by the options100 supplied to 'setup()' (as keyword arguments), in config files, and on101 the command line.102 103 The Distribution instance might be an instance of a class supplied via104 the 'distclass' keyword argument to 'setup'; if no such class is105 supplied, then the Distribution class (in dist.py) is instantiated.106 All other arguments to 'setup' (except for 'cmdclass') are used to set107 attributes of the Distribution instance.108 109 The 'cmdclass' argument, if supplied, is a dictionary mapping command110 names to command classes. Each command encountered on the command line111 will be turned into a command class, which is in turn instantiated; any112 class found in 'cmdclass' is used in place of the default, which is113 (for command 'foo_bar') class 'foo_bar' in module114 'distutils.command.foo_bar'. The command class must provide a115 'user_options' attribute which is a list of option specifiers for116 'distutils.fancy_getopt'. Any command-line options between the current117 and the next command are used to set attributes of the current command118 object.119 120 When the entire command-line has been successfully parsed, calls the121 'run()' method on each command object in turn. This method will be122 driven entirely by the Distribution object (which each command object123 has a reference to, thanks to its constructor), and the124 command-specific options that became attributes of each command125 object.126 """127 128 global _setup_stop_after, _setup_distribution129 130 # Determine the distribution class -- either caller-supplied or131 # our Distribution (see below).132 klass = attrs.get('distclass')133 if klass:134 attrs.pop('distclass')135 else:136 klass = Distribution137 138 if 'script_name' not in attrs:139 attrs['script_name'] = os.path.basename(sys.argv[0])140 if 'script_args' not in attrs:141 attrs['script_args'] = sys.argv[1:]142 143 # Create the Distribution instance, using the remaining arguments144 # (ie. everything except distclass) to initialize it145 try:146 _setup_distribution = dist = klass(attrs)147 except DistutilsSetupError as msg:148 if 'name' not in attrs:149 raise SystemExit("error in setup command: %s" % msg)150 else:151 raise SystemExit("error in {} setup command: {}".format(attrs['name'], msg))152 153 if _setup_stop_after == "init":154 return dist155 156 # Find and parse the config file(s): they will override options from157 # the setup script, but be overridden by the command line.158 dist.parse_config_files()159 160 if DEBUG:161 print("options (after parsing config files):")162 dist.dump_option_dicts()163 164 if _setup_stop_after == "config":165 return dist166 167 # Parse the command line and override config files; any168 # command-line errors are the end user's fault, so turn them into169 # SystemExit to suppress tracebacks.170 try:171 ok = dist.parse_command_line()172 except DistutilsArgError as msg:173 raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg)174 175 if DEBUG:176 print("options (after parsing command line):")177 dist.dump_option_dicts()178 179 if _setup_stop_after == "commandline":180 return dist181 182 # And finally, run all the commands found on the command line.183 if ok:184 return run_commands(dist)185 186 return dist187 188 189# setup ()190 191 192def run_commands(dist):193 """Given a Distribution object run all the commands,194 raising ``SystemExit`` errors in the case of failure.195 196 This function assumes that either ``sys.argv`` or ``dist.script_args``197 is already set accordingly.198 """199 try:200 dist.run_commands()201 except KeyboardInterrupt:202 raise SystemExit("interrupted")203 except OSError as exc:204 if DEBUG:205 sys.stderr.write(f"error: {exc}\n")206 raise207 else:208 raise SystemExit(f"error: {exc}")209 210 except (DistutilsError, CCompilerError) as msg:211 if DEBUG:212 raise213 else:214 raise SystemExit("error: " + str(msg))215 216 return dist217 218 219def run_setup(script_name, script_args=None, stop_after="run"):220 """Run a setup script in a somewhat controlled environment, and221 return the Distribution instance that drives things. This is useful222 if you need to find out the distribution meta-data (passed as223 keyword args from 'script' to 'setup()', or the contents of the224 config files or command-line.225 226 'script_name' is a file that will be read and run with 'exec()';227 'sys.argv[0]' will be replaced with 'script' for the duration of the228 call. 'script_args' is a list of strings; if supplied,229 'sys.argv[1:]' will be replaced by 'script_args' for the duration of230 the call.231 232 'stop_after' tells 'setup()' when to stop processing; possible233 values:234 init235 stop after the Distribution instance has been created and236 populated with the keyword arguments to 'setup()'237 config238 stop after config files have been parsed (and their data239 stored in the Distribution instance)240 commandline241 stop after the command-line ('sys.argv[1:]' or 'script_args')242 have been parsed (and the data stored in the Distribution)243 run [default]244 stop after all commands have been run (the same as if 'setup()'245 had been called in the usual way246 247 Returns the Distribution instance, which provides all information248 used to drive the Distutils.249 """250 if stop_after not in ('init', 'config', 'commandline', 'run'):251 raise ValueError(f"invalid value for 'stop_after': {stop_after!r}")252 253 global _setup_stop_after, _setup_distribution254 _setup_stop_after = stop_after255 256 save_argv = sys.argv.copy()257 g = {'__file__': script_name, '__name__': '__main__'}258 try:259 try:260 sys.argv[0] = script_name261 if script_args is not None:262 sys.argv[1:] = script_args263 # tokenize.open supports automatic encoding detection264 with tokenize.open(script_name) as f:265 code = f.read().replace(r'\r\n', r'\n')266 exec(code, g)267 finally:268 sys.argv = save_argv269 _setup_stop_after = None270 except SystemExit:271 # Hmm, should we do something if exiting with a non-zero code272 # (ie. error)?273 pass274 275 if _setup_distribution is None:276 raise RuntimeError(277 (278 "'distutils.core.setup()' was never called -- "279 "perhaps '%s' is not a Distutils setup script?"280 )281 % script_name282 )283 284 # I wonder if the setup script's namespace -- g and l -- would be of285 # any interest to callers?286 # print "_setup_distribution:", _setup_distribution287 return _setup_distribution288 289 290# run_setup ()291 