summaryrefslogtreecommitdiff
path: root/Lib/distutils
diff options
context:
space:
mode:
authorEzio Melotti <ezio.melotti@gmail.com>2015-09-06 21:44:45 +0300
committerEzio Melotti <ezio.melotti@gmail.com>2015-09-06 21:44:45 +0300
commitf352202977fc53197bd38198b1ac26ed4008a9ba (patch)
tree34246df426e6f7d82794886be98c613903a5655e /Lib/distutils
parentd68070857ae58758849446f5ae162ff3bffb7d6e (diff)
parent2936930f6c8fc1d8992b680181c30f417d74b7c2 (diff)
downloadcpython-f352202977fc53197bd38198b1ac26ed4008a9ba.tar.gz
#23144: merge with 3.4.
Diffstat (limited to 'Lib/distutils')
-rw-r--r--Lib/distutils/_msvccompiler.py494
-rw-r--r--Lib/distutils/archive_util.py21
-rw-r--r--Lib/distutils/ccompiler.py2
-rw-r--r--Lib/distutils/command/bdist.py3
-rw-r--r--Lib/distutils/command/bdist_dumb.py3
-rw-r--r--Lib/distutils/command/bdist_wininst.py36
-rw-r--r--Lib/distutils/command/build.py9
-rw-r--r--Lib/distutils/command/build_ext.py107
-rw-r--r--Lib/distutils/command/build_py.py4
-rw-r--r--Lib/distutils/command/install.py2
-rw-r--r--Lib/distutils/command/install_lib.py16
-rw-r--r--Lib/distutils/command/upload.py40
-rw-r--r--Lib/distutils/command/wininst-14.0-amd64.exebin0 -> 136192 bytes
-rw-r--r--Lib/distutils/command/wininst-14.0.exebin0 -> 129024 bytes
-rw-r--r--Lib/distutils/core.py2
-rw-r--r--Lib/distutils/dist.py69
-rw-r--r--Lib/distutils/extension.py8
-rw-r--r--Lib/distutils/msvc9compiler.py3
-rw-r--r--Lib/distutils/msvccompiler.py3
-rw-r--r--Lib/distutils/spawn.py3
-rw-r--r--Lib/distutils/sysconfig.py27
-rw-r--r--Lib/distutils/tests/test_archive_util.py154
-rw-r--r--Lib/distutils/tests/test_bdist.py2
-rw-r--r--Lib/distutils/tests/test_build_ext.py56
-rw-r--r--Lib/distutils/tests/test_build_py.py4
-rw-r--r--Lib/distutils/tests/test_install.py2
-rw-r--r--Lib/distutils/tests/test_install_lib.py13
-rw-r--r--Lib/distutils/tests/test_msvccompiler.py39
-rw-r--r--Lib/distutils/util.py9
-rw-r--r--Lib/distutils/version.py6
30 files changed, 887 insertions, 250 deletions
diff --git a/Lib/distutils/_msvccompiler.py b/Lib/distutils/_msvccompiler.py
new file mode 100644
index 0000000000..b344616e60
--- /dev/null
+++ b/Lib/distutils/_msvccompiler.py
@@ -0,0 +1,494 @@
+"""distutils._msvccompiler
+
+Contains MSVCCompiler, an implementation of the abstract CCompiler class
+for Microsoft Visual Studio 2015.
+
+The module is compatible with VS 2015 and later. You can find legacy support
+for older versions in distutils.msvc9compiler and distutils.msvccompiler.
+"""
+
+# Written by Perry Stoll
+# hacked by Robin Becker and Thomas Heller to do a better job of
+# finding DevStudio (through the registry)
+# ported to VS 2005 and VS 2008 by Christian Heimes
+# ported to VS 2015 by Steve Dower
+
+import os
+import subprocess
+
+from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
+ CompileError, LibError, LinkError
+from distutils.ccompiler import CCompiler, gen_lib_options
+from distutils import log
+from distutils.util import get_platform
+
+import winreg
+from itertools import count
+
+def _find_vcvarsall():
+ with winreg.OpenKeyEx(
+ winreg.HKEY_LOCAL_MACHINE,
+ r"Software\Microsoft\VisualStudio\SxS\VC7",
+ access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
+ ) as key:
+ if not key:
+ log.debug("Visual C++ is not registered")
+ return None
+
+ best_version = 0
+ best_dir = None
+ for i in count():
+ try:
+ v, vc_dir, vt = winreg.EnumValue(key, i)
+ except OSError:
+ break
+ if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
+ try:
+ version = int(float(v))
+ except (ValueError, TypeError):
+ continue
+ if version >= 14 and version > best_version:
+ best_version, best_dir = version, vc_dir
+ if not best_version:
+ log.debug("No suitable Visual C++ version found")
+ return None
+
+ vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
+ if not os.path.isfile(vcvarsall):
+ log.debug("%s cannot be found", vcvarsall)
+ return None
+
+ return vcvarsall
+
+def _get_vc_env(plat_spec):
+ if os.getenv("DISTUTILS_USE_SDK"):
+ return {
+ key.lower(): value
+ for key, value in os.environ.items()
+ }
+
+ vcvarsall = _find_vcvarsall()
+ if not vcvarsall:
+ raise DistutilsPlatformError("Unable to find vcvarsall.bat")
+
+ try:
+ out = subprocess.check_output(
+ '"{}" {} && set'.format(vcvarsall, plat_spec),
+ shell=True,
+ stderr=subprocess.STDOUT,
+ universal_newlines=True,
+ )
+ except subprocess.CalledProcessError as exc:
+ log.error(exc.output)
+ raise DistutilsPlatformError("Error executing {}"
+ .format(exc.cmd))
+
+ return {
+ key.lower(): value
+ for key, _, value in
+ (line.partition('=') for line in out.splitlines())
+ if key and value
+ }
+
+def _find_exe(exe, paths=None):
+ """Return path to an MSVC executable program.
+
+ Tries to find the program in several places: first, one of the
+ MSVC program search paths from the registry; next, the directories
+ in the PATH environment variable. If any of those work, return an
+ absolute path that is known to exist. If none of them work, just
+ return the original program name, 'exe'.
+ """
+ if not paths:
+ paths = os.getenv('path').split(os.pathsep)
+ for p in paths:
+ fn = os.path.join(os.path.abspath(p), exe)
+ if os.path.isfile(fn):
+ return fn
+ return exe
+
+# A map keyed by get_platform() return values to values accepted by
+# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
+# the param to cross-compile on x86 targetting amd64.)
+PLAT_TO_VCVARS = {
+ 'win32' : 'x86',
+ 'win-amd64' : 'amd64',
+}
+
+class MSVCCompiler(CCompiler) :
+ """Concrete class that implements an interface to Microsoft Visual C++,
+ as defined by the CCompiler abstract class."""
+
+ compiler_type = 'msvc'
+
+ # Just set this so CCompiler's constructor doesn't barf. We currently
+ # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+ # as it really isn't necessary for this sort of single-compiler class.
+ # Would be nice to have a consistent interface with UnixCCompiler,
+ # though, so it's worth thinking about.
+ executables = {}
+
+ # Private class data (need to distinguish C from C++ source for compiler)
+ _c_extensions = ['.c']
+ _cpp_extensions = ['.cc', '.cpp', '.cxx']
+ _rc_extensions = ['.rc']
+ _mc_extensions = ['.mc']
+
+ # Needed for the filename generation methods provided by the
+ # base class, CCompiler.
+ src_extensions = (_c_extensions + _cpp_extensions +
+ _rc_extensions + _mc_extensions)
+ res_extension = '.res'
+ obj_extension = '.obj'
+ static_lib_extension = '.lib'
+ shared_lib_extension = '.dll'
+ static_lib_format = shared_lib_format = '%s%s'
+ exe_extension = '.exe'
+
+
+ def __init__(self, verbose=0, dry_run=0, force=0):
+ CCompiler.__init__ (self, verbose, dry_run, force)
+ # target platform (.plat_name is consistent with 'bdist')
+ self.plat_name = None
+ self.initialized = False
+
+ def initialize(self, plat_name=None):
+ # multi-init means we would need to check platform same each time...
+ assert not self.initialized, "don't init multiple times"
+ if plat_name is None:
+ plat_name = get_platform()
+ # sanity check for platforms to prevent obscure errors later.
+ if plat_name not in PLAT_TO_VCVARS:
+ raise DistutilsPlatformError("--plat-name must be one of {}"
+ .format(tuple(PLAT_TO_VCVARS)))
+
+ # On x86, 'vcvarsall.bat amd64' creates an env that doesn't work;
+ # to cross compile, you use 'x86_amd64'.
+ # On AMD64, 'vcvarsall.bat amd64' is a native build env; to cross
+ # compile use 'x86' (ie, it runs the x86 compiler directly)
+ if plat_name == get_platform() or plat_name == 'win32':
+ # native build or cross-compile to win32
+ plat_spec = PLAT_TO_VCVARS[plat_name]
+ else:
+ # cross compile from win32 -> some 64bit
+ plat_spec = '{}_{}'.format(
+ PLAT_TO_VCVARS[get_platform()],
+ PLAT_TO_VCVARS[plat_name]
+ )
+
+ vc_env = _get_vc_env(plat_spec)
+ if not vc_env:
+ raise DistutilsPlatformError("Unable to find a compatible "
+ "Visual Studio installation.")
+
+ self._paths = vc_env.get('path', '')
+ paths = self._paths.split(os.pathsep)
+ self.cc = _find_exe("cl.exe", paths)
+ self.linker = _find_exe("link.exe", paths)
+ self.lib = _find_exe("lib.exe", paths)
+ self.rc = _find_exe("rc.exe", paths) # resource compiler
+ self.mc = _find_exe("mc.exe", paths) # message compiler
+ self.mt = _find_exe("mt.exe", paths) # message compiler
+
+ for dir in vc_env.get('include', '').split(os.pathsep):
+ if dir:
+ self.add_include_dir(dir)
+
+ for dir in vc_env.get('lib', '').split(os.pathsep):
+ if dir:
+ self.add_library_dir(dir)
+
+ self.preprocess_options = None
+ # Use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib
+ # later to dynamically link to ucrtbase but not vcruntime.
+ self.compile_options = [
+ '/nologo', '/Ox', '/MT', '/W3', '/GL', '/DNDEBUG'
+ ]
+ self.compile_options_debug = [
+ '/nologo', '/Od', '/MTd', '/Zi', '/W3', '/D_DEBUG'
+ ]
+
+ ldflags = [
+ '/nologo', '/INCREMENTAL:NO', '/LTCG', '/nodefaultlib:libucrt.lib', 'ucrt.lib',
+ ]
+ ldflags_debug = [
+ '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL', '/nodefaultlib:libucrtd.lib', 'ucrtd.lib',
+ ]
+
+ self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
+ self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
+ self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
+ self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
+ self.ldflags_static = [*ldflags]
+ self.ldflags_static_debug = [*ldflags_debug]
+
+ self._ldflags = {
+ (CCompiler.EXECUTABLE, None): self.ldflags_exe,
+ (CCompiler.EXECUTABLE, False): self.ldflags_exe,
+ (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
+ (CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
+ (CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
+ (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
+ (CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
+ (CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
+ (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
+ }
+
+ self.initialized = True
+
+ # -- Worker methods ------------------------------------------------
+
+ def object_filenames(self,
+ source_filenames,
+ strip_dir=0,
+ output_dir=''):
+ ext_map = {
+ **{ext: self.obj_extension for ext in self.src_extensions},
+ **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions},
+ }
+
+ output_dir = output_dir or ''
+
+ def make_out_path(p):
+ base, ext = os.path.splitext(p)
+ if strip_dir:
+ base = os.path.basename(base)
+ else:
+ _, base = os.path.splitdrive(base)
+ if base.startswith((os.path.sep, os.path.altsep)):
+ base = base[1:]
+ try:
+ # XXX: This may produce absurdly long paths. We should check
+ # the length of the result and trim base until we fit within
+ # 260 characters.
+ return os.path.join(output_dir, base + ext_map[ext])
+ except LookupError:
+ # Better to raise an exception instead of silently continuing
+ # and later complain about sources and targets having
+ # different lengths
+ raise CompileError("Don't know how to compile {}".format(p))
+
+ return list(map(make_out_path, source_filenames))
+
+
+ def compile(self, sources,
+ output_dir=None, macros=None, include_dirs=None, debug=0,
+ extra_preargs=None, extra_postargs=None, depends=None):
+
+ if not self.initialized:
+ self.initialize()
+ compile_info = self._setup_compile(output_dir, macros, include_dirs,
+ sources, depends, extra_postargs)
+ macros, objects, extra_postargs, pp_opts, build = compile_info
+
+ compile_opts = extra_preargs or []
+ compile_opts.append('/c')
+ if debug:
+ compile_opts.extend(self.compile_options_debug)
+ else:
+ compile_opts.extend(self.compile_options)
+
+
+ add_cpp_opts = False
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ if debug:
+ # pass the full pathname to MSVC in debug mode,
+ # this allows the debugger to find the source file
+ # without asking the user to browse for it
+ src = os.path.abspath(src)
+
+ if ext in self._c_extensions:
+ input_opt = "/Tc" + src
+ elif ext in self._cpp_extensions:
+ input_opt = "/Tp" + src
+ add_cpp_opts = True
+ elif ext in self._rc_extensions:
+ # compile .RC to .RES file
+ input_opt = src
+ output_opt = "/fo" + obj
+ try:
+ self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+ continue
+ elif ext in self._mc_extensions:
+ # Compile .MC to .RC file to .RES file.
+ # * '-h dir' specifies the directory for the
+ # generated include file
+ # * '-r dir' specifies the target directory of the
+ # generated RC file and the binary message resource
+ # it includes
+ #
+ # For now (since there are no options to change this),
+ # we use the source-directory for the include file and
+ # the build directory for the RC file and message
+ # resources. This works at least for win32all.
+ h_dir = os.path.dirname(src)
+ rc_dir = os.path.dirname(obj)
+ try:
+ # first compile .MC to .RC and .H file
+ self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
+ base, _ = os.path.splitext(os.path.basename (src))
+ rc_file = os.path.join(rc_dir, base + '.rc')
+ # then compile .RC to .RES file
+ self.spawn([self.rc, "/fo" + obj, rc_file])
+
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+ continue
+ else:
+ # how to handle this file?
+ raise CompileError("Don't know how to compile {} to {}"
+ .format(src, obj))
+
+ args = [self.cc] + compile_opts + pp_opts
+ if add_cpp_opts:
+ args.append('/EHsc')
+ args.append(input_opt)
+ args.append("/Fo" + obj)
+ args.extend(extra_postargs)
+
+ try:
+ self.spawn(args)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ return objects
+
+
+ def create_static_lib(self,
+ objects,
+ output_libname,
+ output_dir=None,
+ debug=0,
+ target_lang=None):
+
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ output_filename = self.library_filename(output_libname,
+ output_dir=output_dir)
+
+ if self._need_link(objects, output_filename):
+ lib_args = objects + ['/OUT:' + output_filename]
+ if debug:
+ pass # XXX what goes here?
+ try:
+ log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
+ self.spawn([self.lib] + lib_args)
+ except DistutilsExecError as msg:
+ raise LibError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+
+ def link(self,
+ target_desc,
+ objects,
+ output_filename,
+ output_dir=None,
+ libraries=None,
+ library_dirs=None,
+ runtime_library_dirs=None,
+ export_symbols=None,
+ debug=0,
+ extra_preargs=None,
+ extra_postargs=None,
+ build_temp=None,
+ target_lang=None):
+
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ fixed_args = self._fix_lib_args(libraries, library_dirs,
+ runtime_library_dirs)
+ libraries, library_dirs, runtime_library_dirs = fixed_args
+
+ if runtime_library_dirs:
+ self.warn("I don't know what to do with 'runtime_library_dirs': "
+ + str(runtime_library_dirs))
+
+ lib_opts = gen_lib_options(self,
+ library_dirs, runtime_library_dirs,
+ libraries)
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+ ldflags = self._ldflags[target_desc, debug]
+
+ export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
+
+ ld_args = (ldflags + lib_opts + export_opts +
+ objects + ['/OUT:' + output_filename])
+
+ # The MSVC linker generates .lib and .exp files, which cannot be
+ # suppressed by any linker switches. The .lib files may even be
+ # needed! Make sure they are generated in the temporary build
+ # directory. Since they have different names for debug and release
+ # builds, they can go into the same directory.
+ build_temp = os.path.dirname(objects[0])
+ if export_symbols is not None:
+ (dll_name, dll_ext) = os.path.splitext(
+ os.path.basename(output_filename))
+ implib_file = os.path.join(
+ build_temp,
+ self.library_filename(dll_name))
+ ld_args.append ('/IMPLIB:' + implib_file)
+
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+
+ self.mkpath(os.path.dirname(output_filename))
+ try:
+ log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
+ self.spawn([self.linker] + ld_args)
+ except DistutilsExecError as msg:
+ raise LinkError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+ def spawn(self, cmd):
+ old_path = os.getenv('path')
+ try:
+ os.environ['path'] = self._paths
+ return super().spawn(cmd)
+ finally:
+ os.environ['path'] = old_path
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
+ def library_dir_option(self, dir):
+ return "/LIBPATH:" + dir
+
+ def runtime_library_dir_option(self, dir):
+ raise DistutilsPlatformError(
+ "don't know how to set runtime library search path for MSVC")
+
+ def library_option(self, lib):
+ return self.library_filename(lib)
+
+ def find_library_file(self, dirs, lib, debug=0):
+ # Prefer a debugging library if found (and requested), but deal
+ # with it if we don't have one.
+ if debug:
+ try_names = [lib + "_d", lib]
+ else:
+ try_names = [lib]
+ for dir in dirs:
+ for name in try_names:
+ libfile = os.path.join(dir, self.library_filename(name))
+ if os.path.isfile(libfile):
+ return libfile
+ else:
+ # Oops, didn't find it in *any* of 'dirs'
+ return None
diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py
index 4470bb0222..bed1384900 100644
--- a/Lib/distutils/archive_util.py
+++ b/Lib/distutils/archive_util.py
@@ -57,26 +57,28 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
"""Create a (possibly compressed) tar file from all the files under
'base_dir'.
- 'compress' must be "gzip" (the default), "compress", "bzip2", or None.
- (compress will be deprecated in Python 3.2)
+ 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or
+ None. ("compress" will be deprecated in Python 3.2)
'owner' and 'group' can be used to define an owner and a group for the
archive that is being built. If not provided, the current owner and group
will be used.
The output tar file will be named 'base_dir' + ".tar", possibly plus
- the appropriate compression extension (".gz", ".bz2" or ".Z").
+ the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z").
Returns the output filename.
"""
- tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''}
- compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'}
+ tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '',
+ 'compress': ''}
+ compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz',
+ 'compress': '.Z'}
# flags for compression program, each element of list will be an argument
if compress is not None and compress not in compress_ext.keys():
raise ValueError(
- "bad value for 'compress': must be None, 'gzip', 'bzip2' "
- "or 'compress'")
+ "bad value for 'compress': must be None, 'gzip', 'bzip2', "
+ "'xz' or 'compress'")
archive_name = base_name + '.tar'
if compress != 'compress':
@@ -177,6 +179,7 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
ARCHIVE_FORMATS = {
'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
+ 'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"),
'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
'zip': (make_zipfile, [],"ZIP file")
@@ -197,8 +200,8 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
"""Create an archive file (eg. zip or tar).
'base_name' is the name of the file to create, minus any format-specific
- extension; 'format' is the archive format: one of "zip", "tar", "ztar",
- or "gztar".
+ extension; 'format' is the archive format: one of "zip", "tar", "gztar",
+ "bztar", "xztar", or "ztar".
'root_dir' is a directory that will be the root directory of the
archive; ie. we typically chdir into 'root_dir' before creating the
diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py
index 911e84dd3b..b7df394ecc 100644
--- a/Lib/distutils/ccompiler.py
+++ b/Lib/distutils/ccompiler.py
@@ -959,7 +959,7 @@ def get_default_compiler(osname=None, platform=None):
# is assumed to be in the 'distutils' package.)
compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler',
"standard UNIX-style compiler"),
- 'msvc': ('msvccompiler', 'MSVCCompiler',
+ 'msvc': ('_msvccompiler', 'MSVCCompiler',
"Microsoft Visual C++"),
'cygwin': ('cygwinccompiler', 'CygwinCCompiler',
"Cygwin port of GNU C Compiler for Win32"),
diff --git a/Lib/distutils/command/bdist.py b/Lib/distutils/command/bdist.py
index 6814a1c382..014871d280 100644
--- a/Lib/distutils/command/bdist.py
+++ b/Lib/distutils/command/bdist.py
@@ -61,13 +61,14 @@ class bdist(Command):
'nt': 'zip'}
# Establish the preferred order (for the --help-formats option).
- format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar',
+ format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar',
'wininst', 'zip', 'msi']
# And the real information.
format_command = {'rpm': ('bdist_rpm', "RPM distribution"),
'gztar': ('bdist_dumb', "gzip'ed tar file"),
'bztar': ('bdist_dumb', "bzip2'ed tar file"),
+ 'xztar': ('bdist_dumb', "xz'ed tar file"),
'ztar': ('bdist_dumb', "compressed tar file"),
'tar': ('bdist_dumb', "tar file"),
'wininst': ('bdist_wininst',
diff --git a/Lib/distutils/command/bdist_dumb.py b/Lib/distutils/command/bdist_dumb.py
index 4405d12c05..f1bfb24923 100644
--- a/Lib/distutils/command/bdist_dumb.py
+++ b/Lib/distutils/command/bdist_dumb.py
@@ -22,7 +22,8 @@ class bdist_dumb(Command):
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('format=', 'f',
- "archive format to create (tar, ztar, gztar, zip)"),
+ "archive format to create (tar, gztar, bztar, xztar, "
+ "ztar, zip)"),
('keep-temp', 'k',
"keep the pseudo-installation tree around after " +
"creating the distribution archive"),
diff --git a/Lib/distutils/command/bdist_wininst.py b/Lib/distutils/command/bdist_wininst.py
index 959a8bf62e..0c0e2c1a26 100644
--- a/Lib/distutils/command/bdist_wininst.py
+++ b/Lib/distutils/command/bdist_wininst.py
@@ -303,7 +303,6 @@ class bdist_wininst(Command):
return installer_name
def get_exe_bytes(self):
- from distutils.msvccompiler import get_build_version
# If a target-version other than the current version has been
# specified, then using the MSVC version from *this* build is no good.
# Without actually finding and executing the target version and parsing
@@ -313,20 +312,33 @@ class bdist_wininst(Command):
# We can then execute this program to obtain any info we need, such
# as the real sys.version string for the build.
cur_version = get_python_version()
- if self.target_version and self.target_version != cur_version:
- # If the target version is *later* than us, then we assume they
- # use what we use
- # string compares seem wrong, but are what sysconfig.py itself uses
- if self.target_version > cur_version:
- bv = get_build_version()
+
+ # If the target version is *later* than us, then we assume they
+ # use what we use
+ # string compares seem wrong, but are what sysconfig.py itself uses
+ if self.target_version and self.target_version < cur_version:
+ if self.target_version < "2.4":
+ bv = 6.0
+ elif self.target_version == "2.4":
+ bv = 7.1
+ elif self.target_version == "2.5":
+ bv = 8.0
+ elif self.target_version <= "3.2":
+ bv = 9.0
+ elif self.target_version <= "3.4":
+ bv = 10.0
else:
- if self.target_version < "2.4":
- bv = 6.0
- else:
- bv = 7.1
+ bv = 14.0
else:
# for current version - use authoritative check.
- bv = get_build_version()
+ try:
+ from msvcrt import CRT_ASSEMBLY_VERSION
+ except ImportError:
+ # cross-building, so assume the latest version
+ bv = 14.0
+ else:
+ bv = float('.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2]))
+
# wininst-x.y.exe is in the same directory as this file
directory = os.path.dirname(__file__)
diff --git a/Lib/distutils/command/build.py b/Lib/distutils/command/build.py
index cfc15cf0dd..337dd0bfc1 100644
--- a/Lib/distutils/command/build.py
+++ b/Lib/distutils/command/build.py
@@ -36,6 +36,8 @@ class build(Command):
"(default: %s)" % get_platform()),
('compiler=', 'c',
"specify the compiler type"),
+ ('parallel=', 'j',
+ "number of parallel build jobs"),
('debug', 'g',
"compile extensions and libraries with debugging information"),
('force', 'f',
@@ -65,6 +67,7 @@ class build(Command):
self.debug = None
self.force = 0
self.executable = None
+ self.parallel = None
def finalize_options(self):
if self.plat_name is None:
@@ -116,6 +119,12 @@ class build(Command):
if self.executable is None:
self.executable = os.path.normpath(sys.executable)
+ if isinstance(self.parallel, str):
+ try:
+ self.parallel = int(self.parallel)
+ except ValueError:
+ raise DistutilsOptionError("parallel should be an integer")
+
def run(self):
# Run all relevant sub-commands. This will be some subset of:
# - build_py - pure Python modules
diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ext.py
index acbe648036..d4cb11e6db 100644
--- a/Lib/distutils/command/build_ext.py
+++ b/Lib/distutils/command/build_ext.py
@@ -4,7 +4,10 @@ Implements the Distutils 'build_ext' command, for building extension
modules (currently limited to C extensions, should accommodate C++
extensions ASAP)."""
-import sys, os, re
+import contextlib
+import os
+import re
+import sys
from distutils.core import Command
from distutils.errors import *
from distutils.sysconfig import customize_compiler, get_python_version
@@ -16,10 +19,6 @@ from distutils import log
from site import USER_BASE
-if os.name == 'nt':
- from distutils.msvccompiler import get_build_version
- MSVC_VERSION = int(get_build_version())
-
# An extension name is just a dot-separated list of Python NAMEs (ie.
# the same as a fully-qualified module name).
extension_name_re = re.compile \
@@ -85,6 +84,8 @@ class build_ext(Command):
"forcibly build everything (ignore file timestamps)"),
('compiler=', 'c',
"specify the compiler type"),
+ ('parallel=', 'j',
+ "number of parallel build jobs"),
('swig-cpp', None,
"make SWIG create C++ files (default is C)"),
('swig-opts=', None,
@@ -124,6 +125,7 @@ class build_ext(Command):
self.swig_cpp = None
self.swig_opts = None
self.user = None
+ self.parallel = None
def finalize_options(self):
from distutils import sysconfig
@@ -134,6 +136,7 @@ class build_ext(Command):
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'),
+ ('parallel', 'parallel'),
('plat_name', 'plat_name'),
)
@@ -199,27 +202,17 @@ class build_ext(Command):
_sys_home = getattr(sys, '_home', None)
if _sys_home:
self.library_dirs.append(_sys_home)
- if MSVC_VERSION >= 9:
- # Use the .lib files for the correct architecture
- if self.plat_name == 'win32':
- suffix = ''
- else:
- # win-amd64 or win-ia64
- suffix = self.plat_name[4:]
- new_lib = os.path.join(sys.exec_prefix, 'PCbuild')
- if suffix:
- new_lib = os.path.join(new_lib, suffix)
- self.library_dirs.append(new_lib)
-
- elif MSVC_VERSION == 8:
- self.library_dirs.append(os.path.join(sys.exec_prefix,
- 'PC', 'VS8.0'))
- elif MSVC_VERSION == 7:
- self.library_dirs.append(os.path.join(sys.exec_prefix,
- 'PC', 'VS7.1'))
+
+ # Use the .lib files for the correct architecture
+ if self.plat_name == 'win32':
+ suffix = 'win32'
else:
- self.library_dirs.append(os.path.join(sys.exec_prefix,
- 'PC', 'VC6'))
+ # win-amd64 or win-ia64
+ suffix = self.plat_name[4:]
+ new_lib = os.path.join(sys.exec_prefix, 'PCbuild')
+ if suffix:
+ new_lib = os.path.join(new_lib, suffix)
+ self.library_dirs.append(new_lib)
# for extensions under Cygwin and AtheOS Python's library directory must be
# appended to library_dirs
@@ -274,6 +267,12 @@ class build_ext(Command):
self.library_dirs.append(user_lib)
self.rpath.append(user_lib)
+ if isinstance(self.parallel, str):
+ try:
+ self.parallel = int(self.parallel)
+ except ValueError:
+ raise DistutilsOptionError("parallel should be an integer")
+
def run(self):
from distutils.ccompiler import new_compiler
@@ -442,15 +441,45 @@ class build_ext(Command):
def build_extensions(self):
# First, sanity-check the 'extensions' list
self.check_extensions_list(self.extensions)
+ if self.parallel:
+ self._build_extensions_parallel()
+ else:
+ self._build_extensions_serial()
+
+ def _build_extensions_parallel(self):
+ workers = self.parallel
+ if self.parallel is True:
+ workers = os.cpu_count() # may return None
+ try:
+ from concurrent.futures import ThreadPoolExecutor
+ except ImportError:
+ workers = None
+
+ if workers is None:
+ self._build_extensions_serial()
+ return
+
+ with ThreadPoolExecutor(max_workers=workers) as executor:
+ futures = [executor.submit(self.build_extension, ext)
+ for ext in self.extensions]
+ for ext, fut in zip(self.extensions, futures):
+ with self._filter_build_errors(ext):
+ fut.result()
+ def _build_extensions_serial(self):
for ext in self.extensions:
- try:
+ with self._filter_build_errors(ext):
self.build_extension(ext)
- except (CCompilerError, DistutilsError, CompileError) as e:
- if not ext.optional:
- raise
- self.warn('building extension "%s" failed: %s' %
- (ext.name, e))
+
+ @contextlib.contextmanager
+ def _filter_build_errors(self, ext):
+ try:
+ yield
+ except (CCompilerError, DistutilsError, CompileError) as e:
+ if not ext.optional:
+ raise
+ self.warn('building extension "%s" failed: %s' %
+ (ext.name, e))
def build_extension(self, ext):
sources = ext.sources
@@ -502,15 +531,8 @@ class build_ext(Command):
extra_postargs=extra_args,
depends=ext.depends)
- # XXX -- this is a Vile HACK!
- #
- # The setup.py script for Python on Unix needs to be able to
- # get this list so it can perform all the clean up needed to
- # avoid keeping object files around when cleaning out a failed
- # build of an extension module. Since Distutils does not
- # track dependencies, we have to get rid of intermediates to
- # ensure all the intermediates will be properly re-built.
- #
+ # XXX outdated variable, kept here in case third-part code
+ # needs it.
self._built_objects = objects[:]
# Now link the object files together into a "shared object" --
@@ -655,10 +677,7 @@ class build_ext(Command):
"""
from distutils.sysconfig import get_config_var
ext_path = ext_name.split('.')
- # extensions in debug_mode are named 'module_d.pyd' under windows
ext_suffix = get_config_var('EXT_SUFFIX')
- if os.name == 'nt' and self.debug:
- return os.path.join(*ext_path) + '_d' + ext_suffix
return os.path.join(*ext_path) + ext_suffix
def get_export_symbols(self, ext):
@@ -683,7 +702,7 @@ class build_ext(Command):
# to need it mentioned explicitly, though, so that's what we do.
# Append '_d' to the python import library on debug builds.
if sys.platform == "win32":
- from distutils.msvccompiler import MSVCCompiler
+ from distutils._msvccompiler import MSVCCompiler
if not isinstance(self.compiler, MSVCCompiler):
template = "python%d%d"
if self.debug:
diff --git a/Lib/distutils/command/build_py.py b/Lib/distutils/command/build_py.py
index 9100b96a2a..cf0ca57c32 100644
--- a/Lib/distutils/command/build_py.py
+++ b/Lib/distutils/command/build_py.py
@@ -314,10 +314,10 @@ class build_py (Command):
if include_bytecode:
if self.compile:
outputs.append(importlib.util.cache_from_source(
- filename, debug_override=True))
+ filename, optimization=''))
if self.optimize > 0:
outputs.append(importlib.util.cache_from_source(
- filename, debug_override=False))
+ filename, optimization=self.optimize))
outputs += [
os.path.join(build_dir, filename)
diff --git a/Lib/distutils/command/install.py b/Lib/distutils/command/install.py
index d768dc5463..67db007a02 100644
--- a/Lib/distutils/command/install.py
+++ b/Lib/distutils/command/install.py
@@ -51,7 +51,7 @@ if HAS_USER_SITE:
'purelib': '$usersite',
'platlib': '$usersite',
'headers': '$userbase/Python$py_version_nodot/Include/$dist_name',
- 'scripts': '$userbase/Scripts',
+ 'scripts': '$userbase/Python$py_version_nodot/Scripts',
'data' : '$userbase',
}
diff --git a/Lib/distutils/command/install_lib.py b/Lib/distutils/command/install_lib.py
index 215813ba97..6154cf0943 100644
--- a/Lib/distutils/command/install_lib.py
+++ b/Lib/distutils/command/install_lib.py
@@ -22,15 +22,15 @@ class install_lib(Command):
# possible scenarios:
# 1) no compilation at all (--no-compile --no-optimize)
# 2) compile .pyc only (--compile --no-optimize; default)
- # 3) compile .pyc and "level 1" .pyo (--compile --optimize)
- # 4) compile "level 1" .pyo only (--no-compile --optimize)
- # 5) compile .pyc and "level 2" .pyo (--compile --optimize-more)
- # 6) compile "level 2" .pyo only (--no-compile --optimize-more)
+ # 3) compile .pyc and "opt-1" .pyc (--compile --optimize)
+ # 4) compile "opt-1" .pyc only (--no-compile --optimize)
+ # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more)
+ # 6) compile "opt-2" .pyc only (--no-compile --optimize-more)
#
- # The UI for this is two option, 'compile' and 'optimize'.
+ # The UI for this is two options, 'compile' and 'optimize'.
# 'compile' is strictly boolean, and only decides whether to
# generate .pyc files. 'optimize' is three-way (0, 1, or 2), and
- # decides both whether to generate .pyo files and what level of
+ # decides both whether to generate .pyc files and what level of
# optimization to use.
user_options = [
@@ -166,10 +166,10 @@ class install_lib(Command):
continue
if self.compile:
bytecode_files.append(importlib.util.cache_from_source(
- py_file, debug_override=True))
+ py_file, optimization=''))
if self.optimize > 0:
bytecode_files.append(importlib.util.cache_from_source(
- py_file, debug_override=False))
+ py_file, optimization=self.optimize))
return bytecode_files
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
index 1a96e2221e..1c4fc48a12 100644
--- a/Lib/distutils/command/upload.py
+++ b/Lib/distutils/command/upload.py
@@ -1,11 +1,14 @@
-"""distutils.command.upload
+"""
+distutils.command.upload
-Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
+Implements the Distutils 'upload' subcommand (upload package to a package
+index).
+"""
-import sys
-import os, io
-import socket
+import os
+import io
import platform
+import hashlib
from base64 import standard_b64encode
from urllib.request import urlopen, Request, HTTPError
from urllib.parse import urlparse
@@ -14,12 +17,6 @@ from distutils.core import PyPIRCCommand
from distutils.spawn import spawn
from distutils import log
-# this keeps compatibility for 2.3 and 2.4
-if sys.version < "2.5":
- from md5 import md5
-else:
- from hashlib import md5
-
class upload(PyPIRCCommand):
description = "upload binary package to PyPI"
@@ -60,7 +57,8 @@ class upload(PyPIRCCommand):
def run(self):
if not self.distribution.dist_files:
- raise DistutilsOptionError("No dist file created in earlier command")
+ msg = "No dist file created in earlier command"
+ raise DistutilsOptionError(msg)
for command, pyversion, filename in self.distribution.dist_files:
self.upload_file(command, pyversion, filename)
@@ -103,10 +101,10 @@ class upload(PyPIRCCommand):
'content': (os.path.basename(filename),content),
'filetype': command,
'pyversion': pyversion,
- 'md5_digest': md5(content).hexdigest(),
+ 'md5_digest': hashlib.md5(content).hexdigest(),
# additional meta-data
- 'metadata_version' : '1.0',
+ 'metadata_version': '1.0',
'summary': meta.get_description(),
'home_page': meta.get_url(),
'author': meta.get_contact(),
@@ -149,7 +147,7 @@ class upload(PyPIRCCommand):
for key, value in data.items():
title = '\r\nContent-Disposition: form-data; name="%s"' % key
# handle multiple entries for the same name
- if type(value) != type([]):
+ if not isinstance(value, list):
value = [value]
for value in value:
if type(value) is tuple:
@@ -166,13 +164,15 @@ class upload(PyPIRCCommand):
body.write(end_boundary)
body = body.getvalue()
- self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO)
+ msg = "Submitting %s to %s" % (filename, self.repository)
+ self.announce(msg, log.INFO)
# build the Request
- headers = {'Content-type':
- 'multipart/form-data; boundary=%s' % boundary,
- 'Content-length': str(len(body)),
- 'Authorization': auth}
+ headers = {
+ 'Content-type': 'multipart/form-data; boundary=%s' % boundary,
+ 'Content-length': str(len(body)),
+ 'Authorization': auth,
+ }
request = Request(self.repository, data=body,
headers=headers)
diff --git a/Lib/distutils/command/wininst-14.0-amd64.exe b/Lib/distutils/command/wininst-14.0-amd64.exe
new file mode 100644
index 0000000000..7a5e78d52b
--- /dev/null
+++ b/Lib/distutils/command/wininst-14.0-amd64.exe
Binary files differ
diff --git a/Lib/distutils/command/wininst-14.0.exe b/Lib/distutils/command/wininst-14.0.exe
new file mode 100644
index 0000000000..cc43296b67
--- /dev/null
+++ b/Lib/distutils/command/wininst-14.0.exe
Binary files differ
diff --git a/Lib/distutils/core.py b/Lib/distutils/core.py
index 2bfe66aa2f..f05b34b295 100644
--- a/Lib/distutils/core.py
+++ b/Lib/distutils/core.py
@@ -221,8 +221,6 @@ def run_setup (script_name, script_args=None, stop_after="run"):
# Hmm, should we do something if exiting with a non-zero code
# (ie. error)?
pass
- except:
- raise
if _setup_distribution is None:
raise RuntimeError(("'distutils.core.setup()' was never called -- "
diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py
index 7eb04bc3f5..ffb33ff645 100644
--- a/Lib/distutils/dist.py
+++ b/Lib/distutils/dist.py
@@ -4,7 +4,9 @@ Provides the Distribution class, which represents the module distribution
being built/installed/distributed.
"""
-import sys, os, re
+import sys
+import os
+import re
from email import message_from_file
try:
@@ -22,7 +24,7 @@ from distutils.debug import DEBUG
# the same as a Python NAME -- I don't allow leading underscores. The fact
# that they're very similar is no coincidence; the default naming scheme is
# to look for a Python module named after the command.
-command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
+command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
class Distribution:
@@ -39,7 +41,6 @@ class Distribution:
See the code for 'setup()', in core.py, for details.
"""
-
# 'global_options' describes the command-line options that may be
# supplied to the setup script prior to any actual commands.
# Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of
@@ -48,12 +49,13 @@ class Distribution:
# don't want to pollute the commands with too many options that they
# have minimal control over.
# The fourth entry for verbose means that it can be repeated.
- global_options = [('verbose', 'v', "run verbosely (default)", 1),
- ('quiet', 'q', "run quietly (turns verbosity off)"),
- ('dry-run', 'n', "don't actually do anything"),
- ('help', 'h', "show detailed help message"),
- ('no-user-cfg', None,
- 'ignore pydistutils.cfg in your home directory'),
+ global_options = [
+ ('verbose', 'v', "run verbosely (default)", 1),
+ ('quiet', 'q', "run quietly (turns verbosity off)"),
+ ('dry-run', 'n', "don't actually do anything"),
+ ('help', 'h', "show detailed help message"),
+ ('no-user-cfg', None,
+ 'ignore pydistutils.cfg in your home directory'),
]
# 'common_usage' is a short (2-3 line) string describing the common
@@ -115,10 +117,9 @@ Common commands: (see '--help-commands' for more)
# negative options are options that exclude other options
negative_opt = {'quiet': 'verbose'}
-
# -- Creation/initialization methods -------------------------------
- def __init__ (self, attrs=None):
+ def __init__(self, attrs=None):
"""Construct a new Distribution instance: initialize all the
attributes of a Distribution, and then use 'attrs' (a dictionary
mapping attribute names to values) to assign some of those
@@ -532,15 +533,15 @@ Common commands: (see '--help-commands' for more)
# to be sure that the basic "command" interface is implemented.
if not issubclass(cmd_class, Command):
raise DistutilsClassError(
- "command class %s must subclass Command" % cmd_class)
+ "command class %s must subclass Command" % cmd_class)
# Also make sure that the command object provides a list of its
# known options.
if not (hasattr(cmd_class, 'user_options') and
isinstance(cmd_class.user_options, list)):
- raise DistutilsClassError(("command class %s must provide " +
- "'user_options' attribute (a list of tuples)") % \
- cmd_class)
+ msg = ("command class %s must provide "
+ "'user_options' attribute (a list of tuples)")
+ raise DistutilsClassError(msg % cmd_class)
# If the command class has a list of negative alias options,
# merge it in with the global negative aliases.
@@ -552,12 +553,11 @@ Common commands: (see '--help-commands' for more)
# Check for help_options in command class. They have a different
# format (tuple of four) so we need to preprocess them here.
if (hasattr(cmd_class, 'help_options') and
- isinstance(cmd_class.help_options, list)):
+ isinstance(cmd_class.help_options, list)):
help_options = fix_help_options(cmd_class.help_options)
else:
help_options = []
-
# All commands support the global options too, just by adding
# in 'global_options'.
parser.set_option_table(self.global_options +
@@ -570,7 +570,7 @@ Common commands: (see '--help-commands' for more)
return
if (hasattr(cmd_class, 'help_options') and
- isinstance(cmd_class.help_options, list)):
+ isinstance(cmd_class.help_options, list)):
help_option_found=0
for (help_option, short, desc, func) in cmd_class.help_options:
if hasattr(opts, parser.get_attr_name(help_option)):
@@ -647,7 +647,7 @@ Common commands: (see '--help-commands' for more)
else:
klass = self.get_command_class(command)
if (hasattr(klass, 'help_options') and
- isinstance(klass.help_options, list)):
+ isinstance(klass.help_options, list)):
parser.set_option_table(klass.user_options +
fix_help_options(klass.help_options))
else:
@@ -814,7 +814,7 @@ Common commands: (see '--help-commands' for more)
klass_name = command
try:
- __import__ (module_name)
+ __import__(module_name)
module = sys.modules[module_name]
except ImportError:
continue
@@ -823,8 +823,8 @@ Common commands: (see '--help-commands' for more)
klass = getattr(module, klass_name)
except AttributeError:
raise DistutilsModuleError(
- "invalid command '%s' (no class '%s' in module '%s')"
- % (command, klass_name, module_name))
+ "invalid command '%s' (no class '%s' in module '%s')"
+ % (command, klass_name, module_name))
self.cmdclass[command] = klass
return klass
@@ -840,7 +840,7 @@ Common commands: (see '--help-commands' for more)
cmd_obj = self.command_obj.get(command)
if not cmd_obj and create:
if DEBUG:
- self.announce("Distribution.get_command_obj(): " \
+ self.announce("Distribution.get_command_obj(): "
"creating '%s' command object" % command)
klass = self.get_command_class(command)
@@ -897,8 +897,8 @@ Common commands: (see '--help-commands' for more)
setattr(command_obj, option, value)
else:
raise DistutilsOptionError(
- "error in %s: command '%s' has no such option '%s'"
- % (source, command_name, option))
+ "error in %s: command '%s' has no such option '%s'"
+ % (source, command_name, option))
except ValueError as msg:
raise DistutilsOptionError(msg)
@@ -974,7 +974,6 @@ Common commands: (see '--help-commands' for more)
cmd_obj.run()
self.have_run[command] = 1
-
# -- Distribution query methods ------------------------------------
def has_pure_modules(self):
@@ -1112,17 +1111,17 @@ class DistributionMetadata:
"""
version = '1.0'
if (self.provides or self.requires or self.obsoletes or
- self.classifiers or self.download_url):
+ self.classifiers or self.download_url):
version = '1.1'
file.write('Metadata-Version: %s\n' % version)
- file.write('Name: %s\n' % self.get_name() )
- file.write('Version: %s\n' % self.get_version() )
- file.write('Summary: %s\n' % self.get_description() )
- file.write('Home-page: %s\n' % self.get_url() )
- file.write('Author: %s\n' % self.get_contact() )
- file.write('Author-email: %s\n' % self.get_contact_email() )
- file.write('License: %s\n' % self.get_license() )
+ file.write('Name: %s\n' % self.get_name())
+ file.write('Version: %s\n' % self.get_version())
+ file.write('Summary: %s\n' % self.get_description())
+ file.write('Home-page: %s\n' % self.get_url())
+ file.write('Author: %s\n' % self.get_contact())
+ file.write('Author-email: %s\n' % self.get_contact_email())
+ file.write('License: %s\n' % self.get_license())
if self.download_url:
file.write('Download-URL: %s\n' % self.download_url)
@@ -1131,7 +1130,7 @@ class DistributionMetadata:
keywords = ','.join(self.get_keywords())
if keywords:
- file.write('Keywords: %s\n' % keywords )
+ file.write('Keywords: %s\n' % keywords)
self._write_list(file, 'Platform', self.get_platforms())
self._write_list(file, 'Classifier', self.get_classifiers())
diff --git a/Lib/distutils/extension.py b/Lib/distutils/extension.py
index a93655af2c..7efbb74f89 100644
--- a/Lib/distutils/extension.py
+++ b/Lib/distutils/extension.py
@@ -131,6 +131,14 @@ class Extension:
msg = "Unknown Extension options: %s" % options
warnings.warn(msg)
+ def __repr__(self):
+ return '<%s.%s(%r) at %#x>' % (
+ self.__class__.__module__,
+ self.__class__.__qualname__,
+ self.name,
+ id(self))
+
+
def read_setup_file(filename):
"""Reads a Setup file and returns Extension instances."""
from distutils.sysconfig import (parse_makefile, expand_makefile_vars,
diff --git a/Lib/distutils/msvc9compiler.py b/Lib/distutils/msvc9compiler.py
index a5a5010053..da4b21d22a 100644
--- a/Lib/distutils/msvc9compiler.py
+++ b/Lib/distutils/msvc9compiler.py
@@ -179,6 +179,9 @@ def get_build_version():
i = i + len(prefix)
s, rest = sys.version[i:].split(" ", 1)
majorVersion = int(s[:-2]) - 6
+ if majorVersion >= 13:
+ # v13 was skipped and should be v14
+ majorVersion += 1
minorVersion = int(s[2:3]) / 10.0
# I don't think paths are affected by minor version in version 6
if majorVersion == 6:
diff --git a/Lib/distutils/msvccompiler.py b/Lib/distutils/msvccompiler.py
index 8116656961..1048cd4159 100644
--- a/Lib/distutils/msvccompiler.py
+++ b/Lib/distutils/msvccompiler.py
@@ -157,6 +157,9 @@ def get_build_version():
i = i + len(prefix)
s, rest = sys.version[i:].split(" ", 1)
majorVersion = int(s[:-2]) - 6
+ if majorVersion >= 13:
+ # v13 was skipped and should be v14
+ majorVersion += 1
minorVersion = int(s[2:3]) / 10.0
# I don't think paths are affected by minor version in version 6
if majorVersion == 6:
diff --git a/Lib/distutils/spawn.py b/Lib/distutils/spawn.py
index 22e87e8fdb..5dd415a283 100644
--- a/Lib/distutils/spawn.py
+++ b/Lib/distutils/spawn.py
@@ -137,9 +137,6 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0):
try:
pid, status = os.waitpid(pid, 0)
except OSError as exc:
- import errno
- if exc.errno == errno.EINTR:
- continue
if not DEBUG:
cmd = executable
raise DistutilsExecError(
diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py
index a1452fe167..573724ddd7 100644
--- a/Lib/distutils/sysconfig.py
+++ b/Lib/distutils/sysconfig.py
@@ -9,6 +9,7 @@ Written by: Fred L. Drake, Jr.
Email: <fdrake@acm.org>
"""
+import _imp
import os
import re
import sys
@@ -22,23 +23,15 @@ BASE_PREFIX = os.path.normpath(sys.base_prefix)
BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
# Path to the base directory of the project. On Windows the binary may
-# live in project/PCBuild9. If we're dealing with an x64 Windows build,
-# it'll live in project/PCbuild/amd64.
+# live in project/PCBuild/win32 or project/PCBuild/amd64.
# set for cross builds
if "_PYTHON_PROJECT_BASE" in os.environ:
project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"])
else:
project_base = os.path.dirname(os.path.abspath(sys.executable))
-if os.name == "nt" and "pcbuild" in project_base[-8:].lower():
- project_base = os.path.abspath(os.path.join(project_base, os.path.pardir))
-# PC/VS7.1
-if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower():
- project_base = os.path.abspath(os.path.join(project_base, os.path.pardir,
- os.path.pardir))
-# PC/AMD64
-if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower():
- project_base = os.path.abspath(os.path.join(project_base, os.path.pardir,
- os.path.pardir))
+if (os.name == 'nt' and
+ project_base.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))):
+ project_base = os.path.dirname(os.path.dirname(project_base))
# python_build: (Boolean) if true, we're either building Python or
# building an extension with an un-installed Python, so we use
@@ -51,11 +44,9 @@ def _is_python_source_dir(d):
return True
return False
_sys_home = getattr(sys, '_home', None)
-if _sys_home and os.name == 'nt' and \
- _sys_home.lower().endswith(('pcbuild', 'pcbuild\\amd64')):
- _sys_home = os.path.dirname(_sys_home)
- if _sys_home.endswith('pcbuild'): # must be amd64
- _sys_home = os.path.dirname(_sys_home)
+if (_sys_home and os.name == 'nt' and
+ _sys_home.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))):
+ _sys_home = os.path.dirname(os.path.dirname(_sys_home))
def _python_build():
if _sys_home:
return _is_python_source_dir(_sys_home)
@@ -468,7 +459,7 @@ def _init_nt():
# XXX hmmm.. a normal install puts include files here
g['INCLUDEPY'] = get_python_inc(plat_specific=0)
- g['EXT_SUFFIX'] = '.pyd'
+ g['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
g['EXE'] = ".exe"
g['VERSION'] = get_python_version().replace(".", "")
g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py
index 2d72af4aa6..02fa1e27a4 100644
--- a/Lib/distutils/tests/test_archive_util.py
+++ b/Lib/distutils/tests/test_archive_util.py
@@ -13,7 +13,7 @@ from distutils.archive_util import (check_archive_formats, make_tarball,
ARCHIVE_FORMATS)
from distutils.spawn import find_executable, spawn
from distutils.tests import support
-from test.support import check_warnings, run_unittest, patch
+from test.support import check_warnings, run_unittest, patch, change_cwd
try:
import grp
@@ -34,6 +34,16 @@ try:
except ImportError:
ZLIB_SUPPORT = False
+try:
+ import bz2
+except ImportError:
+ bz2 = None
+
+try:
+ import lzma
+except ImportError:
+ lzma = None
+
def can_fs_encode(filename):
"""
Return True if the filename can be saved in the file system.
@@ -52,19 +62,36 @@ class ArchiveUtilTestCase(support.TempdirManager,
unittest.TestCase):
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
- def test_make_tarball(self):
- self._make_tarball('archive')
+ def test_make_tarball(self, name='archive'):
+ # creating something to tar
+ tmpdir = self._create_files()
+ self._make_tarball(tmpdir, name, '.tar.gz')
+ # trying an uncompressed one
+ self._make_tarball(tmpdir, name, '.tar', compress=None)
@unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
+ def test_make_tarball_gzip(self):
+ tmpdir = self._create_files()
+ self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip')
+
+ @unittest.skipUnless(bz2, 'Need bz2 support to run')
+ def test_make_tarball_bzip2(self):
+ tmpdir = self._create_files()
+ self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2')
+
+ @unittest.skipUnless(lzma, 'Need lzma support to run')
+ def test_make_tarball_xz(self):
+ tmpdir = self._create_files()
+ self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz')
+
@unittest.skipUnless(can_fs_encode('årchiv'),
'File system cannot handle this filename')
def test_make_tarball_latin1(self):
"""
Mirror test_make_tarball, except filename contains latin characters.
"""
- self._make_tarball('årchiv') # note this isn't a real word
+ self.test_make_tarball('årchiv') # note this isn't a real word
- @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
@unittest.skipUnless(can_fs_encode('のアーカイブ'),
'File system cannot handle this filename')
def test_make_tarball_extended(self):
@@ -72,16 +99,9 @@ class ArchiveUtilTestCase(support.TempdirManager,
Mirror test_make_tarball, except filename contains extended
characters outside the latin charset.
"""
- self._make_tarball('のアーカイブ') # japanese for archive
-
- def _make_tarball(self, target_name):
- # creating something to tar
- tmpdir = self.mkdtemp()
- self.write_file([tmpdir, 'file1'], 'xxx')
- self.write_file([tmpdir, 'file2'], 'xxx')
- os.mkdir(os.path.join(tmpdir, 'sub'))
- self.write_file([tmpdir, 'sub', 'file3'], 'xxx')
+ self.test_make_tarball('のアーカイブ') # japanese for archive
+ def _make_tarball(self, tmpdir, target_name, suffix, **kwargs):
tmpdir2 = self.mkdtemp()
unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
"source and target should be on same drive")
@@ -89,27 +109,13 @@ class ArchiveUtilTestCase(support.TempdirManager,
base_name = os.path.join(tmpdir2, target_name)
# working with relative paths to avoid tar warnings
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- make_tarball(splitdrive(base_name)[1], '.')
- finally:
- os.chdir(old_dir)
+ with change_cwd(tmpdir):
+ make_tarball(splitdrive(base_name)[1], 'dist', **kwargs)
# check if the compressed tarball was created
- tarball = base_name + '.tar.gz'
- self.assertTrue(os.path.exists(tarball))
-
- # trying an uncompressed one
- base_name = os.path.join(tmpdir2, target_name)
- old_dir = os.getcwd()
- os.chdir(tmpdir)
- try:
- make_tarball(splitdrive(base_name)[1], '.', compress=None)
- finally:
- os.chdir(old_dir)
- tarball = base_name + '.tar'
+ tarball = base_name + suffix
self.assertTrue(os.path.exists(tarball))
+ self.assertEqual(self._tarinfo(tarball), self._created_files)
def _tarinfo(self, path):
tar = tarfile.open(path)
@@ -120,6 +126,9 @@ class ArchiveUtilTestCase(support.TempdirManager,
finally:
tar.close()
+ _created_files = ('dist', 'dist/file1', 'dist/file2',
+ 'dist/sub', 'dist/sub/file3', 'dist/sub2')
+
def _create_files(self):
# creating something to tar
tmpdir = self.mkdtemp()
@@ -130,15 +139,15 @@ class ArchiveUtilTestCase(support.TempdirManager,
os.mkdir(os.path.join(dist, 'sub'))
self.write_file([dist, 'sub', 'file3'], 'xxx')
os.mkdir(os.path.join(dist, 'sub2'))
- tmpdir2 = self.mkdtemp()
- base_name = os.path.join(tmpdir2, 'archive')
- return tmpdir, tmpdir2, base_name
+ return tmpdir
@unittest.skipUnless(find_executable('tar') and find_executable('gzip')
and ZLIB_SUPPORT,
'Need the tar, gzip and zlib command to run')
def test_tarfile_vs_tar(self):
- tmpdir, tmpdir2, base_name = self._create_files()
+ tmpdir = self._create_files()
+ tmpdir2 = self.mkdtemp()
+ base_name = os.path.join(tmpdir2, 'archive')
old_dir = os.getcwd()
os.chdir(tmpdir)
try:
@@ -164,7 +173,8 @@ class ArchiveUtilTestCase(support.TempdirManager,
self.assertTrue(os.path.exists(tarball2))
# let's compare both tarballs
- self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2))
+ self.assertEqual(self._tarinfo(tarball), self._created_files)
+ self.assertEqual(self._tarinfo(tarball2), self._created_files)
# trying an uncompressed one
base_name = os.path.join(tmpdir2, 'archive')
@@ -191,7 +201,8 @@ class ArchiveUtilTestCase(support.TempdirManager,
@unittest.skipUnless(find_executable('compress'),
'The compress program is required')
def test_compress_deprecated(self):
- tmpdir, tmpdir2, base_name = self._create_files()
+ tmpdir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
# using compress and testing the PendingDeprecationWarning
old_dir = os.getcwd()
@@ -224,17 +235,17 @@ class ArchiveUtilTestCase(support.TempdirManager,
'Need zip and zlib support to run')
def test_make_zipfile(self):
# creating something to tar
- tmpdir = self.mkdtemp()
- self.write_file([tmpdir, 'file1'], 'xxx')
- self.write_file([tmpdir, 'file2'], 'xxx')
-
- tmpdir2 = self.mkdtemp()
- base_name = os.path.join(tmpdir2, 'archive')
- make_zipfile(base_name, tmpdir)
+ tmpdir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
+ with change_cwd(tmpdir):
+ make_zipfile(base_name, 'dist')
# check if the compressed tarball was created
tarball = base_name + '.zip'
self.assertTrue(os.path.exists(tarball))
+ with zipfile.ZipFile(tarball) as zf:
+ self.assertEqual(sorted(zf.namelist()),
+ ['dist/file1', 'dist/file2', 'dist/sub/file3'])
@unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
def test_make_zipfile_no_zlib(self):
@@ -250,18 +261,24 @@ class ArchiveUtilTestCase(support.TempdirManager,
patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile)
# create something to tar and compress
- tmpdir, tmpdir2, base_name = self._create_files()
- make_zipfile(base_name, tmpdir)
+ tmpdir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
+ with change_cwd(tmpdir):
+ make_zipfile(base_name, 'dist')
tarball = base_name + '.zip'
self.assertEqual(called,
[((tarball, "w"), {'compression': zipfile.ZIP_STORED})])
self.assertTrue(os.path.exists(tarball))
+ with zipfile.ZipFile(tarball) as zf:
+ self.assertEqual(sorted(zf.namelist()),
+ ['dist/file1', 'dist/file2', 'dist/sub/file3'])
def test_check_archive_formats(self):
self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']),
'xxx')
- self.assertEqual(check_archive_formats(['gztar', 'zip']), None)
+ self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar',
+ 'ztar', 'tar', 'zip']))
def test_make_archive(self):
tmpdir = self.mkdtemp()
@@ -282,6 +299,41 @@ class ArchiveUtilTestCase(support.TempdirManager,
finally:
del ARCHIVE_FORMATS['xxx']
+ def test_make_archive_tar(self):
+ base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp() , 'archive')
+ res = make_archive(base_name, 'tar', base_dir, 'dist')
+ self.assertTrue(os.path.exists(res))
+ self.assertEqual(os.path.basename(res), 'archive.tar')
+ self.assertEqual(self._tarinfo(res), self._created_files)
+
+ @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run')
+ def test_make_archive_gztar(self):
+ base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp() , 'archive')
+ res = make_archive(base_name, 'gztar', base_dir, 'dist')
+ self.assertTrue(os.path.exists(res))
+ self.assertEqual(os.path.basename(res), 'archive.tar.gz')
+ self.assertEqual(self._tarinfo(res), self._created_files)
+
+ @unittest.skipUnless(bz2, 'Need bz2 support to run')
+ def test_make_archive_bztar(self):
+ base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp() , 'archive')
+ res = make_archive(base_name, 'bztar', base_dir, 'dist')
+ self.assertTrue(os.path.exists(res))
+ self.assertEqual(os.path.basename(res), 'archive.tar.bz2')
+ self.assertEqual(self._tarinfo(res), self._created_files)
+
+ @unittest.skipUnless(lzma, 'Need xz support to run')
+ def test_make_archive_xztar(self):
+ base_dir = self._create_files()
+ base_name = os.path.join(self.mkdtemp() , 'archive')
+ res = make_archive(base_name, 'xztar', base_dir, 'dist')
+ self.assertTrue(os.path.exists(res))
+ self.assertEqual(os.path.basename(res), 'archive.tar.xz')
+ self.assertEqual(self._tarinfo(res), self._created_files)
+
def test_make_archive_owner_group(self):
# testing make_archive with owner and group, with various combinations
# this works even if there's not gid/uid support
@@ -291,7 +343,8 @@ class ArchiveUtilTestCase(support.TempdirManager,
else:
group = owner = 'root'
- base_dir, root_dir, base_name = self._create_files()
+ base_dir = self._create_files()
+ root_dir = self.mkdtemp()
base_name = os.path.join(self.mkdtemp() , 'archive')
res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner,
group=group)
@@ -311,7 +364,8 @@ class ArchiveUtilTestCase(support.TempdirManager,
@unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib")
@unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
def test_tarfile_root_owner(self):
- tmpdir, tmpdir2, base_name = self._create_files()
+ tmpdir = self._create_files()
+ base_name = os.path.join(self.mkdtemp(), 'archive')
old_dir = os.getcwd()
os.chdir(tmpdir)
group = grp.getgrgid(0)[0]
diff --git a/Lib/distutils/tests/test_bdist.py b/Lib/distutils/tests/test_bdist.py
index 503a6e857d..f762f5d987 100644
--- a/Lib/distutils/tests/test_bdist.py
+++ b/Lib/distutils/tests/test_bdist.py
@@ -21,7 +21,7 @@ class BuildTestCase(support.TempdirManager,
# what formats does bdist offer?
formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar',
- 'wininst', 'zip', 'ztar']
+ 'wininst', 'xztar', 'zip', 'ztar']
found = sorted(cmd.format_command)
self.assertEqual(found, formats)
diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py
index b9f407f401..366ffbec9f 100644
--- a/Lib/distutils/tests/test_build_ext.py
+++ b/Lib/distutils/tests/test_build_ext.py
@@ -37,6 +37,9 @@ class BuildExtTestCase(TempdirManager,
from distutils.command import build_ext
build_ext.USER_BASE = site.USER_BASE
+ def build_ext(self, *args, **kwargs):
+ return build_ext(*args, **kwargs)
+
def test_build_ext(self):
global ALREADY_TESTED
copy_xxmodule_c(self.tmp_dir)
@@ -44,7 +47,7 @@ class BuildExtTestCase(TempdirManager,
xx_ext = Extension('xx', [xx_c])
dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
dist.package_dir = self.tmp_dir
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
fixup_build_ext(cmd)
cmd.build_lib = self.tmp_dir
cmd.build_temp = self.tmp_dir
@@ -91,7 +94,7 @@ class BuildExtTestCase(TempdirManager,
def test_solaris_enable_shared(self):
dist = Distribution({'name': 'xx'})
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
old = sys.platform
sys.platform = 'sunos' # fooling finalize_options
@@ -113,7 +116,7 @@ class BuildExtTestCase(TempdirManager,
def test_user_site(self):
import site
dist = Distribution({'name': 'xx'})
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
# making sure the user option is there
options = [name for name, short, lable in
@@ -144,14 +147,14 @@ class BuildExtTestCase(TempdirManager,
# with the optional argument.
modules = [Extension('foo', ['xxx'], optional=False)]
dist = Distribution({'name': 'xx', 'ext_modules': modules})
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.ensure_finalized()
self.assertRaises((UnknownFileError, CompileError),
cmd.run) # should raise an error
modules = [Extension('foo', ['xxx'], optional=True)]
dist = Distribution({'name': 'xx', 'ext_modules': modules})
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.ensure_finalized()
cmd.run() # should pass
@@ -160,7 +163,7 @@ class BuildExtTestCase(TempdirManager,
# etc.) are in the include search path.
modules = [Extension('foo', ['xxx'], optional=False)]
dist = Distribution({'name': 'xx', 'ext_modules': modules})
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.finalize_options()
from distutils import sysconfig
@@ -172,14 +175,14 @@ class BuildExtTestCase(TempdirManager,
# make sure cmd.libraries is turned into a list
# if it's a string
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.libraries = 'my_lib, other_lib lastlib'
cmd.finalize_options()
self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib'])
# make sure cmd.library_dirs is turned into a list
# if it's a string
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep
cmd.finalize_options()
self.assertIn('my_lib_dir', cmd.library_dirs)
@@ -187,7 +190,7 @@ class BuildExtTestCase(TempdirManager,
# make sure rpath is turned into a list
# if it's a string
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.rpath = 'one%stwo' % os.pathsep
cmd.finalize_options()
self.assertEqual(cmd.rpath, ['one', 'two'])
@@ -196,32 +199,32 @@ class BuildExtTestCase(TempdirManager,
# make sure define is turned into 2-tuples
# strings if they are ','-separated strings
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.define = 'one,two'
cmd.finalize_options()
self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
# make sure undef is turned into a list of
# strings if they are ','-separated strings
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.undef = 'one,two'
cmd.finalize_options()
self.assertEqual(cmd.undef, ['one', 'two'])
# make sure swig_opts is turned into a list
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.swig_opts = None
cmd.finalize_options()
self.assertEqual(cmd.swig_opts, [])
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.swig_opts = '1 2'
cmd.finalize_options()
self.assertEqual(cmd.swig_opts, ['1', '2'])
def test_check_extensions_list(self):
dist = Distribution()
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.finalize_options()
#'extensions' option must be a list of Extension instances
@@ -270,7 +273,7 @@ class BuildExtTestCase(TempdirManager,
def test_get_source_files(self):
modules = [Extension('foo', ['xxx'], optional=False)]
dist = Distribution({'name': 'xx', 'ext_modules': modules})
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.ensure_finalized()
self.assertEqual(cmd.get_source_files(), ['xxx'])
@@ -279,7 +282,7 @@ class BuildExtTestCase(TempdirManager,
# should not be overriden by a compiler instance
# when the command is run
dist = Distribution()
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.compiler = 'unix'
cmd.ensure_finalized()
cmd.run()
@@ -292,7 +295,7 @@ class BuildExtTestCase(TempdirManager,
ext = Extension('foo', [c_file], optional=False)
dist = Distribution({'name': 'xx',
'ext_modules': [ext]})
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
fixup_build_ext(cmd)
cmd.ensure_finalized()
self.assertEqual(len(cmd.get_outputs()), 1)
@@ -355,7 +358,7 @@ class BuildExtTestCase(TempdirManager,
#etree_ext = Extension('lxml.etree', [etree_c])
#dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
dist = Distribution()
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.inplace = 1
cmd.distribution.package_dir = {'': 'src'}
cmd.distribution.packages = ['lxml', 'lxml.html']
@@ -462,7 +465,7 @@ class BuildExtTestCase(TempdirManager,
'ext_modules': [deptarget_ext]
})
dist.package_dir = self.tmp_dir
- cmd = build_ext(dist)
+ cmd = self.build_ext(dist)
cmd.build_lib = self.tmp_dir
cmd.build_temp = self.tmp_dir
@@ -481,8 +484,19 @@ class BuildExtTestCase(TempdirManager,
self.fail("Wrong deployment target during compilation")
+class ParallelBuildExtTestCase(BuildExtTestCase):
+
+ def build_ext(self, *args, **kwargs):
+ build_ext = super().build_ext(*args, **kwargs)
+ build_ext.parallel = True
+ return build_ext
+
+
def test_suite():
- return unittest.makeSuite(BuildExtTestCase)
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(BuildExtTestCase))
+ suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase))
+ return suite
if __name__ == '__main__':
- support.run_unittest(test_suite())
+ support.run_unittest(__name__)
diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py
index c8f6b89919..18283dc722 100644
--- a/Lib/distutils/tests/test_build_py.py
+++ b/Lib/distutils/tests/test_build_py.py
@@ -120,8 +120,8 @@ class BuildPyTestCase(support.TempdirManager,
found = os.listdir(cmd.build_lib)
self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
- self.assertEqual(sorted(found),
- ['boiledeggs.%s.pyo' % sys.implementation.cache_tag])
+ expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag)
+ self.assertEqual(sorted(found), [expect])
def test_dir_in_package_data(self):
"""
diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py
index 18e1e57505..9313330e2b 100644
--- a/Lib/distutils/tests/test_install.py
+++ b/Lib/distutils/tests/test_install.py
@@ -20,8 +20,6 @@ from distutils.tests import support
def _make_ext_name(modname):
- if os.name == 'nt' and sys.executable.endswith('_d.exe'):
- modname += '_d'
return modname + sysconfig.get_config_var('EXT_SUFFIX')
diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py
index 40dd1a95a6..5378aa8249 100644
--- a/Lib/distutils/tests/test_install_lib.py
+++ b/Lib/distutils/tests/test_install_lib.py
@@ -44,12 +44,11 @@ class InstallLibTestCase(support.TempdirManager,
f = os.path.join(project_dir, 'foo.py')
self.write_file(f, '# python file')
cmd.byte_compile([f])
- pyc_file = importlib.util.cache_from_source('foo.py',
- debug_override=True)
- pyo_file = importlib.util.cache_from_source('foo.py',
- debug_override=False)
+ pyc_file = importlib.util.cache_from_source('foo.py', optimization='')
+ pyc_opt_file = importlib.util.cache_from_source('foo.py',
+ optimization=cmd.optimize)
self.assertTrue(os.path.exists(pyc_file))
- self.assertTrue(os.path.exists(pyo_file))
+ self.assertTrue(os.path.exists(pyc_opt_file))
def test_get_outputs(self):
project_dir, dist = self.create_dist()
@@ -66,8 +65,8 @@ class InstallLibTestCase(support.TempdirManager,
cmd.distribution.packages = ['spam']
cmd.distribution.script_name = 'setup.py'
- # get_outputs should return 4 elements: spam/__init__.py, .pyc and
- # .pyo, foo.import-tag-abiflags.so / foo.pyd
+ # get_outputs should return 4 elements: spam/__init__.py and .pyc,
+ # foo.import-tag-abiflags.so / foo.pyd
outputs = cmd.get_outputs()
self.assertEqual(len(outputs), 4, outputs)
diff --git a/Lib/distutils/tests/test_msvccompiler.py b/Lib/distutils/tests/test_msvccompiler.py
new file mode 100644
index 0000000000..1f8890792e
--- /dev/null
+++ b/Lib/distutils/tests/test_msvccompiler.py
@@ -0,0 +1,39 @@
+"""Tests for distutils._msvccompiler."""
+import sys
+import unittest
+import os
+
+from distutils.errors import DistutilsPlatformError
+from distutils.tests import support
+from test.support import run_unittest
+
+
+SKIP_MESSAGE = (None if sys.platform == "win32" else
+ "These tests are only for win32")
+
+@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE)
+class msvccompilerTestCase(support.TempdirManager,
+ unittest.TestCase):
+
+ def test_no_compiler(self):
+ # makes sure query_vcvarsall raises
+ # a DistutilsPlatformError if the compiler
+ # is not found
+ from distutils._msvccompiler import _get_vc_env
+ def _find_vcvarsall():
+ return None
+
+ import distutils._msvccompiler as _msvccompiler
+ old_find_vcvarsall = _msvccompiler._find_vcvarsall
+ _msvccompiler._find_vcvarsall = _find_vcvarsall
+ try:
+ self.assertRaises(DistutilsPlatformError, _get_vc_env,
+ 'wont find this version')
+ finally:
+ _msvccompiler._find_vcvarsall = old_find_vcvarsall
+
+def test_suite():
+ return unittest.makeSuite(msvccompilerTestCase)
+
+if __name__ == "__main__":
+ run_unittest(test_suite())
diff --git a/Lib/distutils/util.py b/Lib/distutils/util.py
index 5adcac5a95..e423325de4 100644
--- a/Lib/distutils/util.py
+++ b/Lib/distutils/util.py
@@ -322,11 +322,11 @@ def byte_compile (py_files,
prefix=None, base_dir=None,
verbose=1, dry_run=0,
direct=None):
- """Byte-compile a collection of Python source files to either .pyc
- or .pyo files in a __pycache__ subdirectory. 'py_files' is a list
+ """Byte-compile a collection of Python source files to .pyc
+ files in a __pycache__ subdirectory. 'py_files' is a list
of files to compile; any files that don't end in ".py" are silently
skipped. 'optimize' must be one of the following:
- 0 - don't optimize (generate .pyc)
+ 0 - don't optimize
1 - normal optimization (like "python -O")
2 - extra optimization (like "python -OO")
If 'force' is true, all files are recompiled regardless of
@@ -438,8 +438,9 @@ byte_compile(files, optimize=%r, force=%r,
# cfile - byte-compiled file
# dfile - purported source filename (same as 'file' by default)
if optimize >= 0:
+ opt = '' if optimize == 0 else optimize
cfile = importlib.util.cache_from_source(
- file, debug_override=not optimize)
+ file, optimization=opt)
else:
cfile = importlib.util.cache_from_source(file)
dfile = file
diff --git a/Lib/distutils/version.py b/Lib/distutils/version.py
index ebcab84e4e..af14cc1348 100644
--- a/Lib/distutils/version.py
+++ b/Lib/distutils/version.py
@@ -48,12 +48,6 @@ class Version:
return c
return c == 0
- def __ne__(self, other):
- c = self._cmp(other)
- if c is NotImplemented:
- return c
- return c != 0
-
def __lt__(self, other):
c = self._cmp(other)
if c is NotImplemented: