Change venv

This commit is contained in:
Ambulance Clerc
2023-05-31 08:31:22 +02:00
parent fb6f579089
commit fdbb52c96f
466 changed files with 25899 additions and 64721 deletions

View File

@@ -3,29 +3,23 @@
Package containing implementation of all the standard Distutils
commands."""
__all__ = ['build',
'build_py',
'build_ext',
'build_clib',
'build_scripts',
'clean',
'install',
'install_lib',
'install_headers',
'install_scripts',
'install_data',
'sdist',
'register',
'bdist',
'bdist_dumb',
'bdist_rpm',
'bdist_wininst',
'check',
'upload',
# These two are reserved for future use:
#'bdist_sdux',
#'bdist_pkgtool',
# Note:
# bdist_packager is not included because it only provides
# an abstract base class
]
__all__ = [ # noqa: F822
'build',
'build_py',
'build_ext',
'build_clib',
'build_scripts',
'clean',
'install',
'install_lib',
'install_headers',
'install_scripts',
'install_data',
'sdist',
'register',
'bdist',
'bdist_dumb',
'bdist_rpm',
'check',
'upload',
]

View File

@@ -4,79 +4,93 @@ Implements the Distutils 'bdist' command (create a built [binary]
distribution)."""
import os
import warnings
from distutils.core import Command
from distutils.errors import *
from distutils.errors import DistutilsPlatformError, DistutilsOptionError
from distutils.util import get_platform
def show_formats():
"""Print list of available formats (arguments to "--format" option).
"""
"""Print list of available formats (arguments to "--format" option)."""
from distutils.fancy_getopt import FancyGetopt
formats = []
for format in bdist.format_commands:
formats.append(("formats=" + format, None,
bdist.format_command[format][1]))
formats.append(("formats=" + format, None, bdist.format_commands[format][1]))
pretty_printer = FancyGetopt(formats)
pretty_printer.print_help("List of available distribution formats:")
class ListCompat(dict):
# adapter to allow for Setuptools compatibility in format_commands
def append(self, item):
warnings.warn(
"""format_commands is now a dict. append is deprecated.""",
DeprecationWarning,
stacklevel=2,
)
class bdist(Command):
description = "create a built (binary) distribution"
user_options = [('bdist-base=', 'b',
"temporary directory for creating built distributions"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('formats=', None,
"formats for distribution (comma-separated list)"),
('dist-dir=', 'd',
"directory to put final built distributions in "
"[default: dist]"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('owner=', 'u',
"Owner name used when creating a tar file"
" [default: current user]"),
('group=', 'g',
"Group name used when creating a tar file"
" [default: current group]"),
]
user_options = [
('bdist-base=', 'b', "temporary directory for creating built distributions"),
(
'plat-name=',
'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform(),
),
('formats=', None, "formats for distribution (comma-separated list)"),
(
'dist-dir=',
'd',
"directory to put final built distributions in " "[default: dist]",
),
('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
(
'owner=',
'u',
"Owner name used when creating a tar file" " [default: current user]",
),
(
'group=',
'g',
"Group name used when creating a tar file" " [default: current group]",
),
]
boolean_options = ['skip-build']
help_options = [
('help-formats', None,
"lists available distribution formats", show_formats),
]
('help-formats', None, "lists available distribution formats", show_formats),
]
# The following commands do not take a format option from bdist
no_format_option = ('bdist_rpm',)
# This won't do in reality: will need to distinguish RPM-ish Linux,
# Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
default_format = {'posix': 'gztar',
'nt': 'zip'}
default_format = {'posix': 'gztar', 'nt': 'zip'}
# Establish the preferred order (for the --help-formats option).
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',
"Windows executable installer"),
'zip': ('bdist_dumb', "ZIP file"),
'msi': ('bdist_msi', "Microsoft Installer")
}
# Define commands in preferred order for the --help-formats option
format_commands = ListCompat(
{
'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"),
'zip': ('bdist_dumb', "ZIP file"),
}
)
# for compatibility until consumers only reference format_commands
format_command = format_commands
def initialize_options(self):
self.bdist_base = None
@@ -100,8 +114,7 @@ class bdist(Command):
# "build/bdist.<plat>/dumb", "build/bdist.<plat>/rpm", etc.)
if self.bdist_base is None:
build_base = self.get_finalized_command('build').build_base
self.bdist_base = os.path.join(build_base,
'bdist.' + self.plat_name)
self.bdist_base = os.path.join(build_base, 'bdist.' + self.plat_name)
self.ensure_string_list('formats')
if self.formats is None:
@@ -109,8 +122,9 @@ class bdist(Command):
self.formats = [self.default_format[os.name]]
except KeyError:
raise DistutilsPlatformError(
"don't know how to create built distributions "
"on platform %s" % os.name)
"don't know how to create built distributions "
"on platform %s" % os.name
)
if self.dist_dir is None:
self.dist_dir = "dist"
@@ -120,7 +134,7 @@ class bdist(Command):
commands = []
for format in self.formats:
try:
commands.append(self.format_command[format][0])
commands.append(self.format_commands[format][0])
except KeyError:
raise DistutilsOptionError("invalid format '%s'" % format)
@@ -138,6 +152,6 @@ class bdist(Command):
# If we're going to need to run this command again, tell it to
# keep its temporary files around so subsequent runs go faster.
if cmd_name in commands[i+1:]:
if cmd_name in commands[i + 1 :]:
sub_cmd.keep_temp = 1
self.run_command(cmd_name)

View File

@@ -8,44 +8,56 @@ import os
from distutils.core import Command
from distutils.util import get_platform
from distutils.dir_util import remove_tree, ensure_relative
from distutils.errors import *
from distutils.errors import DistutilsPlatformError
from distutils.sysconfig import get_python_version
from distutils import log
class bdist_dumb(Command):
description = "create a \"dumb\" built distribution"
user_options = [('bdist-dir=', 'd',
"temporary directory for creating the distribution"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('format=', 'f',
"archive format to create (tar, gztar, bztar, xztar, "
"ztar, zip)"),
('keep-temp', 'k',
"keep the pseudo-installation tree around after " +
"creating the distribution archive"),
('dist-dir=', 'd',
"directory to put final built distributions in"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('relative', None,
"build the archive using relative paths "
"(default: false)"),
('owner=', 'u',
"Owner name used when creating a tar file"
" [default: current user]"),
('group=', 'g',
"Group name used when creating a tar file"
" [default: current group]"),
]
user_options = [
('bdist-dir=', 'd', "temporary directory for creating the distribution"),
(
'plat-name=',
'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform(),
),
(
'format=',
'f',
"archive format to create (tar, gztar, bztar, xztar, " "ztar, zip)",
),
(
'keep-temp',
'k',
"keep the pseudo-installation tree around after "
+ "creating the distribution archive",
),
('dist-dir=', 'd', "directory to put final built distributions in"),
('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
(
'relative',
None,
"build the archive using relative paths " "(default: false)",
),
(
'owner=',
'u',
"Owner name used when creating a tar file" " [default: current user]",
),
(
'group=',
'g',
"Group name used when creating a tar file" " [default: current group]",
),
]
boolean_options = ['keep-temp', 'skip-build', 'relative']
default_format = { 'posix': 'gztar',
'nt': 'zip' }
default_format = {'posix': 'gztar', 'nt': 'zip'}
def initialize_options(self):
self.bdist_dir = None
@@ -68,13 +80,16 @@ class bdist_dumb(Command):
self.format = self.default_format[os.name]
except KeyError:
raise DistutilsPlatformError(
"don't know how to create dumb built distributions "
"on platform %s" % os.name)
"don't know how to create dumb built distributions "
"on platform %s" % os.name
)
self.set_undefined_options('bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
('skip_build', 'skip_build'))
self.set_undefined_options(
'bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
('skip_build', 'skip_build'),
)
def run(self):
if not self.skip_build:
@@ -90,34 +105,40 @@ class bdist_dumb(Command):
# And make an archive relative to the root of the
# pseudo-installation tree.
archive_basename = "%s.%s" % (self.distribution.get_fullname(),
self.plat_name)
archive_basename = "{}.{}".format(
self.distribution.get_fullname(), self.plat_name
)
pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
if not self.relative:
archive_root = self.bdist_dir
else:
if (self.distribution.has_ext_modules() and
(install.install_base != install.install_platbase)):
if self.distribution.has_ext_modules() and (
install.install_base != install.install_platbase
):
raise DistutilsPlatformError(
"can't make a dumb built distribution where "
"base and platbase are different (%s, %s)"
% (repr(install.install_base),
repr(install.install_platbase)))
"can't make a dumb built distribution where "
"base and platbase are different (%s, %s)"
% (repr(install.install_base), repr(install.install_platbase))
)
else:
archive_root = os.path.join(self.bdist_dir,
ensure_relative(install.install_base))
archive_root = os.path.join(
self.bdist_dir, ensure_relative(install.install_base)
)
# Make the archive
filename = self.make_archive(pseudoinstall_root,
self.format, root_dir=archive_root,
owner=self.owner, group=self.group)
filename = self.make_archive(
pseudoinstall_root,
self.format,
root_dir=archive_root,
owner=self.owner,
group=self.group,
)
if self.distribution.has_ext_modules():
pyversion = get_python_version()
else:
pyversion = 'any'
self.distribution.dist_files.append(('bdist_dumb', pyversion,
filename))
self.distribution.dist_files.append(('bdist_dumb', pyversion, filename))
if not self.keep_temp:
remove_tree(self.bdist_dir, dry_run=self.dry_run)

View File

@@ -1,749 +0,0 @@
# Copyright (C) 2005, 2006 Martin von Löwis
# Licensed to PSF under a Contributor Agreement.
# The bdist_wininst command proper
# based on bdist_wininst
"""
Implements the bdist_msi command.
"""
import os
import sys
import warnings
from distutils.core import Command
from distutils.dir_util import remove_tree
from distutils.sysconfig import get_python_version
from distutils.version import StrictVersion
from distutils.errors import DistutilsOptionError
from distutils.util import get_platform
from distutils import log
import msilib
from msilib import schema, sequence, text
from msilib import Directory, Feature, Dialog, add_data
class PyDialog(Dialog):
"""Dialog class with a fixed layout: controls at the top, then a ruler,
then a list of buttons: back, next, cancel. Optionally a bitmap at the
left."""
def __init__(self, *args, **kw):
"""Dialog(database, name, x, y, w, h, attributes, title, first,
default, cancel, bitmap=true)"""
Dialog.__init__(self, *args)
ruler = self.h - 36
bmwidth = 152*ruler/328
#if kw.get("bitmap", True):
# self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin")
self.line("BottomLine", 0, ruler, self.w, 0)
def title(self, title):
"Set the title text of the dialog at the top."
# name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
# text, in VerdanaBold10
self.text("Title", 15, 10, 320, 60, 0x30003,
r"{\VerdanaBold10}%s" % title)
def back(self, title, next, name = "Back", active = 1):
"""Add a back button with a given title, the tab-next button,
its name in the Control table, possibly initially disabled.
Return the button, so that events can be associated"""
if active:
flags = 3 # Visible|Enabled
else:
flags = 1 # Visible
return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next)
def cancel(self, title, next, name = "Cancel", active = 1):
"""Add a cancel button with a given title, the tab-next button,
its name in the Control table, possibly initially disabled.
Return the button, so that events can be associated"""
if active:
flags = 3 # Visible|Enabled
else:
flags = 1 # Visible
return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next)
def next(self, title, next, name = "Next", active = 1):
"""Add a Next button with a given title, the tab-next button,
its name in the Control table, possibly initially disabled.
Return the button, so that events can be associated"""
if active:
flags = 3 # Visible|Enabled
else:
flags = 1 # Visible
return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next)
def xbutton(self, name, title, next, xpos):
"""Add a button with a given title, the tab-next button,
its name in the Control table, giving its x position; the
y-position is aligned with the other buttons.
Return the button, so that events can be associated"""
return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
class bdist_msi(Command):
description = "create a Microsoft Installer (.msi) binary distribution"
user_options = [('bdist-dir=', None,
"temporary directory for creating the distribution"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('keep-temp', 'k',
"keep the pseudo-installation tree around after " +
"creating the distribution archive"),
('target-version=', None,
"require a specific python version" +
" on the target system"),
('no-target-compile', 'c',
"do not compile .py to .pyc on the target system"),
('no-target-optimize', 'o',
"do not compile .py to .pyo (optimized) "
"on the target system"),
('dist-dir=', 'd',
"directory to put final built distributions in"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('install-script=', None,
"basename of installation script to be run after "
"installation or before deinstallation"),
('pre-install-script=', None,
"Fully qualified filename of a script to be run before "
"any files are installed. This script need not be in the "
"distribution"),
]
boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
'skip-build']
all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4',
'2.5', '2.6', '2.7', '2.8', '2.9',
'3.0', '3.1', '3.2', '3.3', '3.4',
'3.5', '3.6', '3.7', '3.8', '3.9']
other_version = 'X'
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
warnings.warn("bdist_msi command is deprecated since Python 3.9, "
"use bdist_wheel (wheel packages) instead",
DeprecationWarning, 2)
def initialize_options(self):
self.bdist_dir = None
self.plat_name = None
self.keep_temp = 0
self.no_target_compile = 0
self.no_target_optimize = 0
self.target_version = None
self.dist_dir = None
self.skip_build = None
self.install_script = None
self.pre_install_script = None
self.versions = None
def finalize_options(self):
self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
if self.bdist_dir is None:
bdist_base = self.get_finalized_command('bdist').bdist_base
self.bdist_dir = os.path.join(bdist_base, 'msi')
short_version = get_python_version()
if (not self.target_version) and self.distribution.has_ext_modules():
self.target_version = short_version
if self.target_version:
self.versions = [self.target_version]
if not self.skip_build and self.distribution.has_ext_modules()\
and self.target_version != short_version:
raise DistutilsOptionError(
"target version can only be %s, or the '--skip-build'"
" option must be specified" % (short_version,))
else:
self.versions = list(self.all_versions)
self.set_undefined_options('bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
)
if self.pre_install_script:
raise DistutilsOptionError(
"the pre-install-script feature is not yet implemented")
if self.install_script:
for script in self.distribution.scripts:
if self.install_script == os.path.basename(script):
break
else:
raise DistutilsOptionError(
"install_script '%s' not found in scripts"
% self.install_script)
self.install_script_key = None
def run(self):
if not self.skip_build:
self.run_command('build')
install = self.reinitialize_command('install', reinit_subcommands=1)
install.prefix = self.bdist_dir
install.skip_build = self.skip_build
install.warn_dir = 0
install_lib = self.reinitialize_command('install_lib')
# we do not want to include pyc or pyo files
install_lib.compile = 0
install_lib.optimize = 0
if self.distribution.has_ext_modules():
# If we are building an installer for a Python version other
# than the one we are currently running, then we need to ensure
# our build_lib reflects the other Python version rather than ours.
# Note that for target_version!=sys.version, we must have skipped the
# build step, so there is no issue with enforcing the build of this
# version.
target_version = self.target_version
if not target_version:
assert self.skip_build, "Should have already checked this"
target_version = '%d.%d' % sys.version_info[:2]
plat_specifier = ".%s-%s" % (self.plat_name, target_version)
build = self.get_finalized_command('build')
build.build_lib = os.path.join(build.build_base,
'lib' + plat_specifier)
log.info("installing to %s", self.bdist_dir)
install.ensure_finalized()
# avoid warning of 'install_lib' about installing
# into a directory not in sys.path
sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
install.run()
del sys.path[0]
self.mkpath(self.dist_dir)
fullname = self.distribution.get_fullname()
installer_name = self.get_installer_filename(fullname)
installer_name = os.path.abspath(installer_name)
if os.path.exists(installer_name): os.unlink(installer_name)
metadata = self.distribution.metadata
author = metadata.author
if not author:
author = metadata.maintainer
if not author:
author = "UNKNOWN"
version = metadata.get_version()
# ProductVersion must be strictly numeric
# XXX need to deal with prerelease versions
sversion = "%d.%d.%d" % StrictVersion(version).version
# Prefix ProductName with Python x.y, so that
# it sorts together with the other Python packages
# in Add-Remove-Programs (APR)
fullname = self.distribution.get_fullname()
if self.target_version:
product_name = "Python %s %s" % (self.target_version, fullname)
else:
product_name = "Python %s" % (fullname)
self.db = msilib.init_database(installer_name, schema,
product_name, msilib.gen_uuid(),
sversion, author)
msilib.add_tables(self.db, sequence)
props = [('DistVersion', version)]
email = metadata.author_email or metadata.maintainer_email
if email:
props.append(("ARPCONTACT", email))
if metadata.url:
props.append(("ARPURLINFOABOUT", metadata.url))
if props:
add_data(self.db, 'Property', props)
self.add_find_python()
self.add_files()
self.add_scripts()
self.add_ui()
self.db.Commit()
if hasattr(self.distribution, 'dist_files'):
tup = 'bdist_msi', self.target_version or 'any', fullname
self.distribution.dist_files.append(tup)
if not self.keep_temp:
remove_tree(self.bdist_dir, dry_run=self.dry_run)
def add_files(self):
db = self.db
cab = msilib.CAB("distfiles")
rootdir = os.path.abspath(self.bdist_dir)
root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir")
f = Feature(db, "Python", "Python", "Everything",
0, 1, directory="TARGETDIR")
items = [(f, root, '')]
for version in self.versions + [self.other_version]:
target = "TARGETDIR" + version
name = default = "Python" + version
desc = "Everything"
if version is self.other_version:
title = "Python from another location"
level = 2
else:
title = "Python %s from registry" % version
level = 1
f = Feature(db, name, title, desc, 1, level, directory=target)
dir = Directory(db, cab, root, rootdir, target, default)
items.append((f, dir, version))
db.Commit()
seen = {}
for feature, dir, version in items:
todo = [dir]
while todo:
dir = todo.pop()
for file in os.listdir(dir.absolute):
afile = os.path.join(dir.absolute, file)
if os.path.isdir(afile):
short = "%s|%s" % (dir.make_short(file), file)
default = file + version
newdir = Directory(db, cab, dir, file, default, short)
todo.append(newdir)
else:
if not dir.component:
dir.start_component(dir.logical, feature, 0)
if afile not in seen:
key = seen[afile] = dir.add_file(file)
if file==self.install_script:
if self.install_script_key:
raise DistutilsOptionError(
"Multiple files with name %s" % file)
self.install_script_key = '[#%s]' % key
else:
key = seen[afile]
add_data(self.db, "DuplicateFile",
[(key + version, dir.component, key, None, dir.logical)])
db.Commit()
cab.commit(db)
def add_find_python(self):
"""Adds code to the installer to compute the location of Python.
Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the
registry for each version of Python.
Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined,
else from PYTHON.MACHINE.X.Y.
Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe"""
start = 402
for ver in self.versions:
install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver
machine_reg = "python.machine." + ver
user_reg = "python.user." + ver
machine_prop = "PYTHON.MACHINE." + ver
user_prop = "PYTHON.USER." + ver
machine_action = "PythonFromMachine" + ver
user_action = "PythonFromUser" + ver
exe_action = "PythonExe" + ver
target_dir_prop = "TARGETDIR" + ver
exe_prop = "PYTHON" + ver
if msilib.Win64:
# type: msidbLocatorTypeRawValue + msidbLocatorType64bit
Type = 2+16
else:
Type = 2
add_data(self.db, "RegLocator",
[(machine_reg, 2, install_path, None, Type),
(user_reg, 1, install_path, None, Type)])
add_data(self.db, "AppSearch",
[(machine_prop, machine_reg),
(user_prop, user_reg)])
add_data(self.db, "CustomAction",
[(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"),
(user_action, 51+256, target_dir_prop, "[" + user_prop + "]"),
(exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"),
])
add_data(self.db, "InstallExecuteSequence",
[(machine_action, machine_prop, start),
(user_action, user_prop, start + 1),
(exe_action, None, start + 2),
])
add_data(self.db, "InstallUISequence",
[(machine_action, machine_prop, start),
(user_action, user_prop, start + 1),
(exe_action, None, start + 2),
])
add_data(self.db, "Condition",
[("Python" + ver, 0, "NOT TARGETDIR" + ver)])
start += 4
assert start < 500
def add_scripts(self):
if self.install_script:
start = 6800
for ver in self.versions + [self.other_version]:
install_action = "install_script." + ver
exe_prop = "PYTHON" + ver
add_data(self.db, "CustomAction",
[(install_action, 50, exe_prop, self.install_script_key)])
add_data(self.db, "InstallExecuteSequence",
[(install_action, "&Python%s=3" % ver, start)])
start += 1
# XXX pre-install scripts are currently refused in finalize_options()
# but if this feature is completed, it will also need to add
# entries for each version as the above code does
if self.pre_install_script:
scriptfn = os.path.join(self.bdist_dir, "preinstall.bat")
with open(scriptfn, "w") as f:
# The batch file will be executed with [PYTHON], so that %1
# is the path to the Python interpreter; %0 will be the path
# of the batch file.
# rem ="""
# %1 %0
# exit
# """
# <actual script>
f.write('rem ="""\n%1 %0\nexit\n"""\n')
with open(self.pre_install_script) as fin:
f.write(fin.read())
add_data(self.db, "Binary",
[("PreInstall", msilib.Binary(scriptfn))
])
add_data(self.db, "CustomAction",
[("PreInstall", 2, "PreInstall", None)
])
add_data(self.db, "InstallExecuteSequence",
[("PreInstall", "NOT Installed", 450)])
def add_ui(self):
db = self.db
x = y = 50
w = 370
h = 300
title = "[ProductName] Setup"
# see "Dialog Style Bits"
modal = 3 # visible | modal
modeless = 1 # visible
track_disk_space = 32
# UI customization properties
add_data(db, "Property",
# See "DefaultUIFont Property"
[("DefaultUIFont", "DlgFont8"),
# See "ErrorDialog Style Bit"
("ErrorDialog", "ErrorDlg"),
("Progress1", "Install"), # modified in maintenance type dlg
("Progress2", "installs"),
("MaintenanceForm_Action", "Repair"),
# possible values: ALL, JUSTME
("WhichUsers", "ALL")
])
# Fonts, see "TextStyle Table"
add_data(db, "TextStyle",
[("DlgFont8", "Tahoma", 9, None, 0),
("DlgFontBold8", "Tahoma", 8, None, 1), #bold
("VerdanaBold10", "Verdana", 10, None, 1),
("VerdanaRed9", "Verdana", 9, 255, 0),
])
# UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
# Numbers indicate sequence; see sequence.py for how these action integrate
add_data(db, "InstallUISequence",
[("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141),
# In the user interface, assume all-users installation if privileged.
("SelectFeaturesDlg", "Not Installed", 1230),
# XXX no support for resume installations yet
#("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250),
("ProgressDlg", None, 1280)])
add_data(db, 'ActionText', text.ActionText)
add_data(db, 'UIText', text.UIText)
#####################################################################
# Standard dialogs: FatalError, UserExit, ExitDialog
fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title,
"Finish", "Finish", "Finish")
fatal.title("[ProductName] Installer ended prematurely")
fatal.back("< Back", "Finish", active = 0)
fatal.cancel("Cancel", "Back", active = 0)
fatal.text("Description1", 15, 70, 320, 80, 0x30003,
"[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.")
fatal.text("Description2", 15, 155, 320, 20, 0x30003,
"Click the Finish button to exit the Installer.")
c=fatal.next("Finish", "Cancel", name="Finish")
c.event("EndDialog", "Exit")
user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title,
"Finish", "Finish", "Finish")
user_exit.title("[ProductName] Installer was interrupted")
user_exit.back("< Back", "Finish", active = 0)
user_exit.cancel("Cancel", "Back", active = 0)
user_exit.text("Description1", 15, 70, 320, 80, 0x30003,
"[ProductName] setup was interrupted. Your system has not been modified. "
"To install this program at a later time, please run the installation again.")
user_exit.text("Description2", 15, 155, 320, 20, 0x30003,
"Click the Finish button to exit the Installer.")
c = user_exit.next("Finish", "Cancel", name="Finish")
c.event("EndDialog", "Exit")
exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title,
"Finish", "Finish", "Finish")
exit_dialog.title("Completing the [ProductName] Installer")
exit_dialog.back("< Back", "Finish", active = 0)
exit_dialog.cancel("Cancel", "Back", active = 0)
exit_dialog.text("Description", 15, 235, 320, 20, 0x30003,
"Click the Finish button to exit the Installer.")
c = exit_dialog.next("Finish", "Cancel", name="Finish")
c.event("EndDialog", "Return")
#####################################################################
# Required dialog: FilesInUse, ErrorDlg
inuse = PyDialog(db, "FilesInUse",
x, y, w, h,
19, # KeepModeless|Modal|Visible
title,
"Retry", "Retry", "Retry", bitmap=False)
inuse.text("Title", 15, 6, 200, 15, 0x30003,
r"{\DlgFontBold8}Files in Use")
inuse.text("Description", 20, 23, 280, 20, 0x30003,
"Some files that need to be updated are currently in use.")
inuse.text("Text", 20, 55, 330, 50, 3,
"The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.")
inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess",
None, None, None)
c=inuse.back("Exit", "Ignore", name="Exit")
c.event("EndDialog", "Exit")
c=inuse.next("Ignore", "Retry", name="Ignore")
c.event("EndDialog", "Ignore")
c=inuse.cancel("Retry", "Exit", name="Retry")
c.event("EndDialog","Retry")
# See "Error Dialog". See "ICE20" for the required names of the controls.
error = Dialog(db, "ErrorDlg",
50, 10, 330, 101,
65543, # Error|Minimize|Modal|Visible
title,
"ErrorText", None, None)
error.text("ErrorText", 50,9,280,48,3, "")
#error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None)
error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo")
error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes")
error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort")
error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel")
error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore")
error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk")
error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry")
#####################################################################
# Global "Query Cancel" dialog
cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title,
"No", "No", "No")
cancel.text("Text", 48, 15, 194, 30, 3,
"Are you sure you want to cancel [ProductName] installation?")
#cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
# "py.ico", None, None)
c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
c.event("EndDialog", "Exit")
c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
c.event("EndDialog", "Return")
#####################################################################
# Global "Wait for costing" dialog
costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title,
"Return", "Return", "Return")
costing.text("Text", 48, 15, 194, 30, 3,
"Please wait while the installer finishes determining your disk space requirements.")
c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
c.event("EndDialog", "Exit")
#####################################################################
# Preparation dialog: no user input except cancellation
prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title,
"Cancel", "Cancel", "Cancel")
prep.text("Description", 15, 70, 320, 40, 0x30003,
"Please wait while the Installer prepares to guide you through the installation.")
prep.title("Welcome to the [ProductName] Installer")
c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...")
c.mapping("ActionText", "Text")
c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None)
c.mapping("ActionData", "Text")
prep.back("Back", None, active=0)
prep.next("Next", None, active=0)
c=prep.cancel("Cancel", None)
c.event("SpawnDialog", "CancelDlg")
#####################################################################
# Feature (Python directory) selection
seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title,
"Next", "Next", "Cancel")
seldlg.title("Select Python Installations")
seldlg.text("Hint", 15, 30, 300, 20, 3,
"Select the Python locations where %s should be installed."
% self.distribution.get_fullname())
seldlg.back("< Back", None, active=0)
c = seldlg.next("Next >", "Cancel")
order = 1
c.event("[TARGETDIR]", "[SourceDir]", ordering=order)
for version in self.versions + [self.other_version]:
order += 1
c.event("[TARGETDIR]", "[TARGETDIR%s]" % version,
"FEATURE_SELECTED AND &Python%s=3" % version,
ordering=order)
c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1)
c.event("EndDialog", "Return", ordering=order + 2)
c = seldlg.cancel("Cancel", "Features")
c.event("SpawnDialog", "CancelDlg")
c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3,
"FEATURE", None, "PathEdit", None)
c.event("[FEATURE_SELECTED]", "1")
ver = self.other_version
install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver
dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver
c = seldlg.text("Other", 15, 200, 300, 15, 3,
"Provide an alternate Python location")
c.condition("Enable", install_other_cond)
c.condition("Show", install_other_cond)
c.condition("Disable", dont_install_other_cond)
c.condition("Hide", dont_install_other_cond)
c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1,
"TARGETDIR" + ver, None, "Next", None)
c.condition("Enable", install_other_cond)
c.condition("Show", install_other_cond)
c.condition("Disable", dont_install_other_cond)
c.condition("Hide", dont_install_other_cond)
#####################################################################
# Disk cost
cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title,
"OK", "OK", "OK", bitmap=False)
cost.text("Title", 15, 6, 200, 15, 0x30003,
r"{\DlgFontBold8}Disk Space Requirements")
cost.text("Description", 20, 20, 280, 20, 0x30003,
"The disk space required for the installation of the selected features.")
cost.text("Text", 20, 53, 330, 60, 3,
"The highlighted volumes (if any) do not have enough disk space "
"available for the currently selected features. You can either "
"remove some files from the highlighted volumes, or choose to "
"install less features onto local drive(s), or select different "
"destination drive(s).")
cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223,
None, "{120}{70}{70}{70}{70}", None, None)
cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
#####################################################################
# WhichUsers Dialog. Only available on NT, and for privileged users.
# This must be run before FindRelatedProducts, because that will
# take into account whether the previous installation was per-user
# or per-machine. We currently don't support going back to this
# dialog after "Next" was selected; to support this, we would need to
# find how to reset the ALLUSERS property, and how to re-run
# FindRelatedProducts.
# On Windows9x, the ALLUSERS property is ignored on the command line
# and in the Property table, but installer fails according to the documentation
# if a dialog attempts to set ALLUSERS.
whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title,
"AdminInstall", "Next", "Cancel")
whichusers.title("Select whether to install [ProductName] for all users of this computer.")
# A radio group with two options: allusers, justme
g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3,
"WhichUsers", "", "Next")
g.add("ALL", 0, 5, 150, 20, "Install for all users")
g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
whichusers.back("Back", None, active=0)
c = whichusers.next("Next >", "Cancel")
c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
c.event("EndDialog", "Return", ordering = 2)
c = whichusers.cancel("Cancel", "AdminInstall")
c.event("SpawnDialog", "CancelDlg")
#####################################################################
# Installation Progress dialog (modeless)
progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title,
"Cancel", "Cancel", "Cancel", bitmap=False)
progress.text("Title", 20, 15, 200, 15, 0x30003,
r"{\DlgFontBold8}[Progress1] [ProductName]")
progress.text("Text", 35, 65, 300, 30, 3,
"Please wait while the Installer [Progress2] [ProductName]. "
"This may take several minutes.")
progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...")
c.mapping("ActionText", "Text")
#c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
#c.mapping("ActionData", "Text")
c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537,
None, "Progress done", None, None)
c.mapping("SetProgress", "Progress")
progress.back("< Back", "Next", active=False)
progress.next("Next >", "Cancel", active=False)
progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
###################################################################
# Maintenance type: repair/uninstall
maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title,
"Next", "Next", "Cancel")
maint.title("Welcome to the [ProductName] Setup Wizard")
maint.text("BodyText", 15, 63, 330, 42, 3,
"Select whether you want to repair or remove [ProductName].")
g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3,
"MaintenanceForm_Action", "", "Next")
#g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
maint.back("< Back", None, active=False)
c=maint.next("Finish", "Cancel")
# Change installation: Change progress dialog to "Change", then ask
# for feature selection
#c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
#c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
# Reinstall: Change progress dialog to "Repair", then invoke reinstall
# Also set list of reinstalled features to "ALL"
c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
# Uninstall: Change progress to "Remove", then invoke uninstall
# Also set list of removed features to "ALL"
c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
# Close dialog when maintenance action scheduled
c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
#c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21)
maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
def get_installer_filename(self, fullname):
# Factored out to allow overriding in subclasses
if self.target_version:
base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name,
self.target_version)
else:
base_name = "%s.%s.msi" % (fullname, self.plat_name)
installer_name = os.path.join(self.dist_dir, base_name)
return installer_name

View File

@@ -3,134 +3,153 @@
Implements the Distutils 'bdist_rpm' command (create RPM source and binary
distributions)."""
import subprocess, sys, os
import subprocess
import sys
import os
from distutils.core import Command
from distutils.debug import DEBUG
from distutils.file_util import write_file
from distutils.errors import *
from distutils.errors import (
DistutilsOptionError,
DistutilsPlatformError,
DistutilsFileError,
DistutilsExecError,
)
from distutils.sysconfig import get_python_version
from distutils import log
class bdist_rpm(Command):
description = "create an RPM distribution"
user_options = [
('bdist-base=', None,
"base directory for creating built distributions"),
('rpm-base=', None,
"base directory for creating RPMs (defaults to \"rpm\" under "
"--bdist-base; must be specified for RPM 2)"),
('dist-dir=', 'd',
"directory to put final RPM files in "
"(and .spec files if --spec-only)"),
('python=', None,
"path to Python interpreter to hard-code in the .spec file "
"(default: \"python\")"),
('fix-python', None,
"hard-code the exact path to the current Python interpreter in "
"the .spec file"),
('spec-only', None,
"only regenerate spec file"),
('source-only', None,
"only generate source RPM"),
('binary-only', None,
"only generate binary RPM"),
('use-bzip2', None,
"use bzip2 instead of gzip to create source distribution"),
('bdist-base=', None, "base directory for creating built distributions"),
(
'rpm-base=',
None,
"base directory for creating RPMs (defaults to \"rpm\" under "
"--bdist-base; must be specified for RPM 2)",
),
(
'dist-dir=',
'd',
"directory to put final RPM files in " "(and .spec files if --spec-only)",
),
(
'python=',
None,
"path to Python interpreter to hard-code in the .spec file "
"(default: \"python\")",
),
(
'fix-python',
None,
"hard-code the exact path to the current Python interpreter in "
"the .spec file",
),
('spec-only', None, "only regenerate spec file"),
('source-only', None, "only generate source RPM"),
('binary-only', None, "only generate binary RPM"),
('use-bzip2', None, "use bzip2 instead of gzip to create source distribution"),
# More meta-data: too RPM-specific to put in the setup script,
# but needs to go in the .spec file -- so we make these options
# to "bdist_rpm". The idea is that packagers would put this
# info in setup.cfg, although they are of course free to
# supply it on the command line.
('distribution-name=', None,
"name of the (Linux) distribution to which this "
"RPM applies (*not* the name of the module distribution!)"),
('group=', None,
"package classification [default: \"Development/Libraries\"]"),
('release=', None,
"RPM release number"),
('serial=', None,
"RPM serial number"),
('vendor=', None,
"RPM \"vendor\" (eg. \"Joe Blow <joe@example.com>\") "
"[default: maintainer or author from setup script]"),
('packager=', None,
"RPM packager (eg. \"Jane Doe <jane@example.net>\") "
"[default: vendor]"),
('doc-files=', None,
"list of documentation files (space or comma-separated)"),
('changelog=', None,
"RPM changelog"),
('icon=', None,
"name of icon file"),
('provides=', None,
"capabilities provided by this package"),
('requires=', None,
"capabilities required by this package"),
('conflicts=', None,
"capabilities which conflict with this package"),
('build-requires=', None,
"capabilities required to build this package"),
('obsoletes=', None,
"capabilities made obsolete by this package"),
('no-autoreq', None,
"do not automatically calculate dependencies"),
(
'distribution-name=',
None,
"name of the (Linux) distribution to which this "
"RPM applies (*not* the name of the module distribution!)",
),
('group=', None, "package classification [default: \"Development/Libraries\"]"),
('release=', None, "RPM release number"),
('serial=', None, "RPM serial number"),
(
'vendor=',
None,
"RPM \"vendor\" (eg. \"Joe Blow <joe@example.com>\") "
"[default: maintainer or author from setup script]",
),
(
'packager=',
None,
"RPM packager (eg. \"Jane Doe <jane@example.net>\") " "[default: vendor]",
),
('doc-files=', None, "list of documentation files (space or comma-separated)"),
('changelog=', None, "RPM changelog"),
('icon=', None, "name of icon file"),
('provides=', None, "capabilities provided by this package"),
('requires=', None, "capabilities required by this package"),
('conflicts=', None, "capabilities which conflict with this package"),
('build-requires=', None, "capabilities required to build this package"),
('obsoletes=', None, "capabilities made obsolete by this package"),
('no-autoreq', None, "do not automatically calculate dependencies"),
# Actions to take when building RPM
('keep-temp', 'k',
"don't clean up RPM build directory"),
('no-keep-temp', None,
"clean up RPM build directory [default]"),
('use-rpm-opt-flags', None,
"compile with RPM_OPT_FLAGS when building from source RPM"),
('no-rpm-opt-flags', None,
"do not pass any RPM CFLAGS to compiler"),
('rpm3-mode', None,
"RPM 3 compatibility mode (default)"),
('rpm2-mode', None,
"RPM 2 compatibility mode"),
('keep-temp', 'k', "don't clean up RPM build directory"),
('no-keep-temp', None, "clean up RPM build directory [default]"),
(
'use-rpm-opt-flags',
None,
"compile with RPM_OPT_FLAGS when building from source RPM",
),
('no-rpm-opt-flags', None, "do not pass any RPM CFLAGS to compiler"),
('rpm3-mode', None, "RPM 3 compatibility mode (default)"),
('rpm2-mode', None, "RPM 2 compatibility mode"),
# Add the hooks necessary for specifying custom scripts
('prep-script=', None,
"Specify a script for the PREP phase of RPM building"),
('build-script=', None,
"Specify a script for the BUILD phase of RPM building"),
('pre-install=', None,
"Specify a script for the pre-INSTALL phase of RPM building"),
('install-script=', None,
"Specify a script for the INSTALL phase of RPM building"),
('post-install=', None,
"Specify a script for the post-INSTALL phase of RPM building"),
('pre-uninstall=', None,
"Specify a script for the pre-UNINSTALL phase of RPM building"),
('post-uninstall=', None,
"Specify a script for the post-UNINSTALL phase of RPM building"),
('clean-script=', None,
"Specify a script for the CLEAN phase of RPM building"),
('verify-script=', None,
"Specify a script for the VERIFY phase of the RPM build"),
('prep-script=', None, "Specify a script for the PREP phase of RPM building"),
('build-script=', None, "Specify a script for the BUILD phase of RPM building"),
(
'pre-install=',
None,
"Specify a script for the pre-INSTALL phase of RPM building",
),
(
'install-script=',
None,
"Specify a script for the INSTALL phase of RPM building",
),
(
'post-install=',
None,
"Specify a script for the post-INSTALL phase of RPM building",
),
(
'pre-uninstall=',
None,
"Specify a script for the pre-UNINSTALL phase of RPM building",
),
(
'post-uninstall=',
None,
"Specify a script for the post-UNINSTALL phase of RPM building",
),
('clean-script=', None, "Specify a script for the CLEAN phase of RPM building"),
(
'verify-script=',
None,
"Specify a script for the VERIFY phase of the RPM build",
),
# Allow a packager to explicitly force an architecture
('force-arch=', None,
"Force an architecture onto the RPM build process"),
('force-arch=', None, "Force an architecture onto the RPM build process"),
('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"),
]
('quiet', 'q',
"Run the INSTALL phase of RPM building in quiet mode"),
]
boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode',
'no-autoreq', 'quiet']
negative_opt = {'no-keep-temp': 'keep-temp',
'no-rpm-opt-flags': 'use-rpm-opt-flags',
'rpm2-mode': 'rpm3-mode'}
boolean_options = [
'keep-temp',
'use-rpm-opt-flags',
'rpm3-mode',
'no-autoreq',
'quiet',
]
negative_opt = {
'no-keep-temp': 'keep-temp',
'no-rpm-opt-flags': 'use-rpm-opt-flags',
'rpm2-mode': 'rpm3-mode',
}
def initialize_options(self):
self.bdist_base = None
@@ -181,8 +200,7 @@ class bdist_rpm(Command):
self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
if self.rpm_base is None:
if not self.rpm3_mode:
raise DistutilsOptionError(
"you must specify --rpm-base in RPM 2 mode")
raise DistutilsOptionError("you must specify --rpm-base in RPM 2 mode")
self.rpm_base = os.path.join(self.bdist_base, "rpm")
if self.python is None:
@@ -192,14 +210,17 @@ class bdist_rpm(Command):
self.python = "python3"
elif self.fix_python:
raise DistutilsOptionError(
"--python and --fix-python are mutually exclusive options")
"--python and --fix-python are mutually exclusive options"
)
if os.name != 'posix':
raise DistutilsPlatformError("don't know how to create RPM "
"distributions on platform %s" % os.name)
raise DistutilsPlatformError(
"don't know how to create RPM " "distributions on platform %s" % os.name
)
if self.binary_only and self.source_only:
raise DistutilsOptionError(
"cannot supply both '--source-only' and '--binary-only'")
"cannot supply both '--source-only' and '--binary-only'"
)
# don't pass CFLAGS to pure python distributions
if not self.distribution.has_ext_modules():
@@ -210,9 +231,11 @@ class bdist_rpm(Command):
def finalize_package_data(self):
self.ensure_string('group', "Development/Libraries")
self.ensure_string('vendor',
"%s <%s>" % (self.distribution.get_contact(),
self.distribution.get_contact_email()))
self.ensure_string(
'vendor',
"%s <%s>"
% (self.distribution.get_contact(), self.distribution.get_contact_email()),
)
self.ensure_string('packager')
self.ensure_string_list('doc_files')
if isinstance(self.doc_files, list):
@@ -221,12 +244,12 @@ class bdist_rpm(Command):
self.doc_files.append(readme)
self.ensure_string('release', "1")
self.ensure_string('serial') # should it be an int?
self.ensure_string('serial') # should it be an int?
self.ensure_string('distribution_name')
self.ensure_string('changelog')
# Format changelog correctly
# Format changelog correctly
self.changelog = self._format_changelog(self.changelog)
self.ensure_filename('icon')
@@ -253,7 +276,7 @@ class bdist_rpm(Command):
self.ensure_string('force_arch')
def run(self):
def run(self): # noqa: C901
if DEBUG:
print("before _get_package_data():")
print("vendor =", self.vendor)
@@ -274,14 +297,12 @@ class bdist_rpm(Command):
# Spec file goes into 'dist_dir' if '--spec-only specified',
# build/rpm.<plat> otherwise.
spec_path = os.path.join(spec_dir,
"%s.spec" % self.distribution.get_name())
self.execute(write_file,
(spec_path,
self._make_spec_file()),
"writing '%s'" % spec_path)
spec_path = os.path.join(spec_dir, "%s.spec" % self.distribution.get_name())
self.execute(
write_file, (spec_path, self._make_spec_file()), "writing '%s'" % spec_path
)
if self.spec_only: # stop if requested
if self.spec_only: # stop if requested
return
# Make a source distribution and copy to SOURCES directory with
@@ -303,14 +324,13 @@ class bdist_rpm(Command):
if os.path.exists(self.icon):
self.copy_file(self.icon, source_dir)
else:
raise DistutilsFileError(
"icon file '%s' does not exist" % self.icon)
raise DistutilsFileError("icon file '%s' does not exist" % self.icon)
# build package
log.info("building RPMs")
rpm_cmd = ['rpmbuild']
if self.source_only: # what kind of RPMs?
if self.source_only: # what kind of RPMs?
rpm_cmd.append('-bs')
elif self.binary_only:
rpm_cmd.append('-bb')
@@ -318,8 +338,7 @@ class bdist_rpm(Command):
rpm_cmd.append('-ba')
rpm_cmd.extend(['--define', '__python %s' % self.python])
if self.rpm3_mode:
rpm_cmd.extend(['--define',
'_topdir %s' % os.path.abspath(self.rpm_base)])
rpm_cmd.extend(['--define', '_topdir %s' % os.path.abspath(self.rpm_base)])
if not self.keep_temp:
rpm_cmd.append('--clean')
@@ -334,8 +353,11 @@ class bdist_rpm(Command):
nvr_string = "%{name}-%{version}-%{release}"
src_rpm = nvr_string + ".src.rpm"
non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm"
q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % (
src_rpm, non_src_rpm, spec_path)
q_cmd = r"rpm -q --qf '{} {}\n' --specfile '{}'".format(
src_rpm,
non_src_rpm,
spec_path,
)
out = os.popen(q_cmd)
try:
@@ -345,12 +367,12 @@ class bdist_rpm(Command):
line = out.readline()
if not line:
break
l = line.strip().split()
assert(len(l) == 2)
binary_rpms.append(l[1])
ell = line.strip().split()
assert len(ell) == 2
binary_rpms.append(ell[1])
# The source rpm is named after the first entry in the spec file
if source_rpm is None:
source_rpm = l[0]
source_rpm = ell[0]
status = out.close()
if status:
@@ -369,38 +391,37 @@ class bdist_rpm(Command):
if not self.binary_only:
srpm = os.path.join(rpm_dir['SRPMS'], source_rpm)
assert(os.path.exists(srpm))
assert os.path.exists(srpm)
self.move_file(srpm, self.dist_dir)
filename = os.path.join(self.dist_dir, source_rpm)
self.distribution.dist_files.append(
('bdist_rpm', pyversion, filename))
self.distribution.dist_files.append(('bdist_rpm', pyversion, filename))
if not self.source_only:
for rpm in binary_rpms:
rpm = os.path.join(rpm_dir['RPMS'], rpm)
if os.path.exists(rpm):
self.move_file(rpm, self.dist_dir)
filename = os.path.join(self.dist_dir,
os.path.basename(rpm))
filename = os.path.join(self.dist_dir, os.path.basename(rpm))
self.distribution.dist_files.append(
('bdist_rpm', pyversion, filename))
('bdist_rpm', pyversion, filename)
)
def _dist_path(self, path):
return os.path.join(self.dist_dir, os.path.basename(path))
def _make_spec_file(self):
def _make_spec_file(self): # noqa: C901
"""Generate the text of an RPM spec file and return it as a
list of strings (one per line).
"""
# definitions and headers
spec_file = [
'%define name ' + self.distribution.get_name(),
'%define version ' + self.distribution.get_version().replace('-','_'),
'%define version ' + self.distribution.get_version().replace('-', '_'),
'%define unmangled_version ' + self.distribution.get_version(),
'%define release ' + self.release.replace('-','_'),
'%define release ' + self.release.replace('-', '_'),
'',
'Summary: ' + self.distribution.get_description(),
]
'Summary: ' + (self.distribution.get_description() or "UNKNOWN"),
]
# Workaround for #14443 which affects some RPM based systems such as
# RHEL6 (and probably derivatives)
@@ -408,8 +429,9 @@ class bdist_rpm(Command):
# Generate a potential replacement value for __os_install_post (whilst
# normalizing the whitespace to simplify the test for whether the
# invocation of brp-python-bytecompile passes in __python):
vendor_hook = '\n'.join([' %s \\' % line.strip()
for line in vendor_hook.splitlines()])
vendor_hook = '\n'.join(
[' %s \\' % line.strip() for line in vendor_hook.splitlines()]
)
problem = "brp-python-bytecompile \\\n"
fixed = "brp-python-bytecompile %{__python} \\\n"
fixed_hook = vendor_hook.replace(problem, fixed)
@@ -420,14 +442,17 @@ class bdist_rpm(Command):
# put locale summaries into spec file
# XXX not supported for now (hard to put a dictionary
# in a config file -- arg!)
#for locale in self.summaries.keys():
# for locale in self.summaries.keys():
# spec_file.append('Summary(%s): %s' % (locale,
# self.summaries[locale]))
spec_file.extend([
'Name: %{name}',
'Version: %{version}',
'Release: %{release}',])
spec_file.extend(
[
'Name: %{name}',
'Version: %{version}',
'Release: %{release}',
]
)
# XXX yuck! this filename is available from the "sdist" command,
# but only after it has run: and we create the spec file before
@@ -437,42 +462,44 @@ class bdist_rpm(Command):
else:
spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz')
spec_file.extend([
'License: ' + self.distribution.get_license(),
'Group: ' + self.group,
'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
'Prefix: %{_prefix}', ])
spec_file.extend(
[
'License: ' + (self.distribution.get_license() or "UNKNOWN"),
'Group: ' + self.group,
'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
'Prefix: %{_prefix}',
]
)
if not self.force_arch:
# noarch if no extension modules
if not self.distribution.has_ext_modules():
spec_file.append('BuildArch: noarch')
else:
spec_file.append( 'BuildArch: %s' % self.force_arch )
spec_file.append('BuildArch: %s' % self.force_arch)
for field in ('Vendor',
'Packager',
'Provides',
'Requires',
'Conflicts',
'Obsoletes',
):
for field in (
'Vendor',
'Packager',
'Provides',
'Requires',
'Conflicts',
'Obsoletes',
):
val = getattr(self, field.lower())
if isinstance(val, list):
spec_file.append('%s: %s' % (field, ' '.join(val)))
spec_file.append('{}: {}'.format(field, ' '.join(val)))
elif val is not None:
spec_file.append('%s: %s' % (field, val))
spec_file.append('{}: {}'.format(field, val))
if self.distribution.get_url() != 'UNKNOWN':
if self.distribution.get_url():
spec_file.append('Url: ' + self.distribution.get_url())
if self.distribution_name:
spec_file.append('Distribution: ' + self.distribution_name)
if self.build_requires:
spec_file.append('BuildRequires: ' +
' '.join(self.build_requires))
spec_file.append('BuildRequires: ' + ' '.join(self.build_requires))
if self.icon:
spec_file.append('Icon: ' + os.path.basename(self.icon))
@@ -480,16 +507,18 @@ class bdist_rpm(Command):
if self.no_autoreq:
spec_file.append('AutoReq: 0')
spec_file.extend([
'',
'%description',
self.distribution.get_long_description()
])
spec_file.extend(
[
'',
'%description',
self.distribution.get_long_description() or "",
]
)
# put locale descriptions into spec file
# XXX again, suppressed because config file syntax doesn't
# easily support this ;-(
#for locale in self.descriptions.keys():
# for locale in self.descriptions.keys():
# spec_file.extend([
# '',
# '%description -l ' + locale,
@@ -498,7 +527,7 @@ class bdist_rpm(Command):
# rpm scripts
# figure out default build script
def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0]))
def_setup_call = "{} {}".format(self.python, os.path.basename(sys.argv[0]))
def_build = "%s build" % def_setup_call
if self.use_rpm_opt_flags:
def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
@@ -509,8 +538,9 @@ class bdist_rpm(Command):
# that we open and interpolate into the spec file, but the defaults
# are just text that we drop in as-is. Hmmm.
install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT '
'--record=INSTALLED_FILES') % def_setup_call
install_cmd = (
'%s install -O1 --root=$RPM_BUILD_ROOT ' '--record=INSTALLED_FILES'
) % def_setup_call
script_options = [
('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"),
@@ -529,37 +559,43 @@ class bdist_rpm(Command):
# use 'default' as contents of script
val = getattr(self, attr)
if val or default:
spec_file.extend([
'',
'%' + rpm_opt,])
spec_file.extend(
[
'',
'%' + rpm_opt,
]
)
if val:
with open(val) as f:
spec_file.extend(f.read().split('\n'))
else:
spec_file.append(default)
# files section
spec_file.extend([
'',
'%files -f INSTALLED_FILES',
'%defattr(-,root,root)',
])
spec_file.extend(
[
'',
'%files -f INSTALLED_FILES',
'%defattr(-,root,root)',
]
)
if self.doc_files:
spec_file.append('%doc ' + ' '.join(self.doc_files))
if self.changelog:
spec_file.extend([
'',
'%changelog',])
spec_file.extend(
[
'',
'%changelog',
]
)
spec_file.extend(self.changelog)
return spec_file
def _format_changelog(self, changelog):
"""Format the changelog correctly and convert it to a list of strings
"""
"""Format the changelog correctly and convert it to a list of strings"""
if not changelog:
return changelog
new_changelog = []

View File

@@ -1,377 +0,0 @@
"""distutils.command.bdist_wininst
Implements the Distutils 'bdist_wininst' command: create a windows installer
exe-program."""
import os
import sys
import warnings
from distutils.core import Command
from distutils.util import get_platform
from distutils.dir_util import remove_tree
from distutils.errors import *
from distutils.sysconfig import get_python_version
from distutils import log
class bdist_wininst(Command):
description = "create an executable installer for MS Windows"
user_options = [('bdist-dir=', None,
"temporary directory for creating the distribution"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('keep-temp', 'k',
"keep the pseudo-installation tree around after " +
"creating the distribution archive"),
('target-version=', None,
"require a specific python version" +
" on the target system"),
('no-target-compile', 'c',
"do not compile .py to .pyc on the target system"),
('no-target-optimize', 'o',
"do not compile .py to .pyo (optimized) "
"on the target system"),
('dist-dir=', 'd',
"directory to put final built distributions in"),
('bitmap=', 'b',
"bitmap to use for the installer instead of python-powered logo"),
('title=', 't',
"title to display on the installer background instead of default"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('install-script=', None,
"basename of installation script to be run after "
"installation or before deinstallation"),
('pre-install-script=', None,
"Fully qualified filename of a script to be run before "
"any files are installed. This script need not be in the "
"distribution"),
('user-access-control=', None,
"specify Vista's UAC handling - 'none'/default=no "
"handling, 'auto'=use UAC if target Python installed for "
"all users, 'force'=always use UAC"),
]
boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
'skip-build']
# bpo-10945: bdist_wininst requires mbcs encoding only available on Windows
_unsupported = (sys.platform != "win32")
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
warnings.warn("bdist_wininst command is deprecated since Python 3.8, "
"use bdist_wheel (wheel packages) instead",
DeprecationWarning, 2)
def initialize_options(self):
self.bdist_dir = None
self.plat_name = None
self.keep_temp = 0
self.no_target_compile = 0
self.no_target_optimize = 0
self.target_version = None
self.dist_dir = None
self.bitmap = None
self.title = None
self.skip_build = None
self.install_script = None
self.pre_install_script = None
self.user_access_control = None
def finalize_options(self):
self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
if self.bdist_dir is None:
if self.skip_build and self.plat_name:
# If build is skipped and plat_name is overridden, bdist will
# not see the correct 'plat_name' - so set that up manually.
bdist = self.distribution.get_command_obj('bdist')
bdist.plat_name = self.plat_name
# next the command will be initialized using that name
bdist_base = self.get_finalized_command('bdist').bdist_base
self.bdist_dir = os.path.join(bdist_base, 'wininst')
if not self.target_version:
self.target_version = ""
if not self.skip_build and self.distribution.has_ext_modules():
short_version = get_python_version()
if self.target_version and self.target_version != short_version:
raise DistutilsOptionError(
"target version can only be %s, or the '--skip-build'" \
" option must be specified" % (short_version,))
self.target_version = short_version
self.set_undefined_options('bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
)
if self.install_script:
for script in self.distribution.scripts:
if self.install_script == os.path.basename(script):
break
else:
raise DistutilsOptionError(
"install_script '%s' not found in scripts"
% self.install_script)
def run(self):
if (sys.platform != "win32" and
(self.distribution.has_ext_modules() or
self.distribution.has_c_libraries())):
raise DistutilsPlatformError \
("distribution contains extensions and/or C libraries; "
"must be compiled on a Windows 32 platform")
if not self.skip_build:
self.run_command('build')
install = self.reinitialize_command('install', reinit_subcommands=1)
install.root = self.bdist_dir
install.skip_build = self.skip_build
install.warn_dir = 0
install.plat_name = self.plat_name
install_lib = self.reinitialize_command('install_lib')
# we do not want to include pyc or pyo files
install_lib.compile = 0
install_lib.optimize = 0
if self.distribution.has_ext_modules():
# If we are building an installer for a Python version other
# than the one we are currently running, then we need to ensure
# our build_lib reflects the other Python version rather than ours.
# Note that for target_version!=sys.version, we must have skipped the
# build step, so there is no issue with enforcing the build of this
# version.
target_version = self.target_version
if not target_version:
assert self.skip_build, "Should have already checked this"
target_version = '%d.%d' % sys.version_info[:2]
plat_specifier = ".%s-%s" % (self.plat_name, target_version)
build = self.get_finalized_command('build')
build.build_lib = os.path.join(build.build_base,
'lib' + plat_specifier)
# Use a custom scheme for the zip-file, because we have to decide
# at installation time which scheme to use.
for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
value = key.upper()
if key == 'headers':
value = value + '/Include/$dist_name'
setattr(install,
'install_' + key,
value)
log.info("installing to %s", self.bdist_dir)
install.ensure_finalized()
# avoid warning of 'install_lib' about installing
# into a directory not in sys.path
sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
install.run()
del sys.path[0]
# And make an archive relative to the root of the
# pseudo-installation tree.
from tempfile import mktemp
archive_basename = mktemp()
fullname = self.distribution.get_fullname()
arcname = self.make_archive(archive_basename, "zip",
root_dir=self.bdist_dir)
# create an exe containing the zip-file
self.create_exe(arcname, fullname, self.bitmap)
if self.distribution.has_ext_modules():
pyversion = get_python_version()
else:
pyversion = 'any'
self.distribution.dist_files.append(('bdist_wininst', pyversion,
self.get_installer_filename(fullname)))
# remove the zip-file again
log.debug("removing temporary file '%s'", arcname)
os.remove(arcname)
if not self.keep_temp:
remove_tree(self.bdist_dir, dry_run=self.dry_run)
def get_inidata(self):
# Return data describing the installation.
lines = []
metadata = self.distribution.metadata
# Write the [metadata] section.
lines.append("[metadata]")
# 'info' will be displayed in the installer's dialog box,
# describing the items to be installed.
info = (metadata.long_description or '') + '\n'
# Escape newline characters
def escape(s):
return s.replace("\n", "\\n")
for name in ["author", "author_email", "description", "maintainer",
"maintainer_email", "name", "url", "version"]:
data = getattr(metadata, name, "")
if data:
info = info + ("\n %s: %s" % \
(name.capitalize(), escape(data)))
lines.append("%s=%s" % (name, escape(data)))
# The [setup] section contains entries controlling
# the installer runtime.
lines.append("\n[Setup]")
if self.install_script:
lines.append("install_script=%s" % self.install_script)
lines.append("info=%s" % escape(info))
lines.append("target_compile=%d" % (not self.no_target_compile))
lines.append("target_optimize=%d" % (not self.no_target_optimize))
if self.target_version:
lines.append("target_version=%s" % self.target_version)
if self.user_access_control:
lines.append("user_access_control=%s" % self.user_access_control)
title = self.title or self.distribution.get_fullname()
lines.append("title=%s" % escape(title))
import time
import distutils
build_info = "Built %s with distutils-%s" % \
(time.ctime(time.time()), distutils.__version__)
lines.append("build_info=%s" % build_info)
return "\n".join(lines)
def create_exe(self, arcname, fullname, bitmap=None):
import struct
self.mkpath(self.dist_dir)
cfgdata = self.get_inidata()
installer_name = self.get_installer_filename(fullname)
self.announce("creating %s" % installer_name)
if bitmap:
with open(bitmap, "rb") as f:
bitmapdata = f.read()
bitmaplen = len(bitmapdata)
else:
bitmaplen = 0
with open(installer_name, "wb") as file:
file.write(self.get_exe_bytes())
if bitmap:
file.write(bitmapdata)
# Convert cfgdata from unicode to ascii, mbcs encoded
if isinstance(cfgdata, str):
cfgdata = cfgdata.encode("mbcs")
# Append the pre-install script
cfgdata = cfgdata + b"\0"
if self.pre_install_script:
# We need to normalize newlines, so we open in text mode and
# convert back to bytes. "latin-1" simply avoids any possible
# failures.
with open(self.pre_install_script, "r",
encoding="latin-1") as script:
script_data = script.read().encode("latin-1")
cfgdata = cfgdata + script_data + b"\n\0"
else:
# empty pre-install script
cfgdata = cfgdata + b"\0"
file.write(cfgdata)
# The 'magic number' 0x1234567B is used to make sure that the
# binary layout of 'cfgdata' is what the wininst.exe binary
# expects. If the layout changes, increment that number, make
# the corresponding changes to the wininst.exe sources, and
# recompile them.
header = struct.pack("<iii",
0x1234567B, # tag
len(cfgdata), # length
bitmaplen, # number of bytes in bitmap
)
file.write(header)
with open(arcname, "rb") as f:
file.write(f.read())
def get_installer_filename(self, fullname):
# Factored out to allow overriding in subclasses
if self.target_version:
# if we create an installer for a specific python version,
# it's better to include this in the name
installer_name = os.path.join(self.dist_dir,
"%s.%s-py%s.exe" %
(fullname, self.plat_name, self.target_version))
else:
installer_name = os.path.join(self.dist_dir,
"%s.%s.exe" % (fullname, self.plat_name))
return installer_name
def get_exe_bytes(self):
# 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
# its sys.version, we just hard-code our knowledge of old versions.
# NOTE: Possible alternative is to allow "--target-version" to
# specify a Python executable rather than a simple version string.
# 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 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:
bv = '14.0'
else:
# for current version - use authoritative check.
try:
from msvcrt import CRT_ASSEMBLY_VERSION
except ImportError:
# cross-building, so assume the latest version
bv = '14.0'
else:
# as far as we know, CRT is binary compatible based on
# the first field, so assume 'x.0' until proven otherwise
major = CRT_ASSEMBLY_VERSION.partition('.')[0]
bv = major + '.0'
# wininst-x.y.exe is in the same directory as this file
directory = os.path.dirname(__file__)
# we must use a wininst-x.y.exe built with the same C compiler
# used for python. XXX What about mingw, borland, and so on?
# if plat_name starts with "win" but is not "win32"
# we want to strip "win" and leave the rest (e.g. -amd64)
# for all other cases, we don't want any suffix
if self.plat_name != 'win32' and self.plat_name[:3] == 'win':
sfix = self.plat_name[3:]
else:
sfix = ''
filename = os.path.join(directory, "wininst-%s%s.exe" % (bv, sfix))
f = open(filename, "rb")
try:
return f.read()
finally:
f.close()

View File

@@ -2,7 +2,8 @@
Implements the Distutils 'build' command."""
import sys, os
import sys
import os
from distutils.core import Command
from distutils.errors import DistutilsOptionError
from distutils.util import get_platform
@@ -10,6 +11,7 @@ from distutils.util import get_platform
def show_compilers():
from distutils.ccompiler import show_compilers
show_compilers()
@@ -18,40 +20,35 @@ class build(Command):
description = "build everything needed to install"
user_options = [
('build-base=', 'b',
"base directory for build library"),
('build-purelib=', None,
"build directory for platform-neutral distributions"),
('build-platlib=', None,
"build directory for platform-specific distributions"),
('build-lib=', None,
"build directory for all distribution (defaults to either " +
"build-purelib or build-platlib"),
('build-scripts=', None,
"build directory for scripts"),
('build-temp=', 't',
"temporary build directory"),
('plat-name=', 'p',
"platform name to build for, if supported "
"(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',
"forcibly build everything (ignore file timestamps)"),
('executable=', 'e',
"specify final destination interpreter path (build.py)"),
]
('build-base=', 'b', "base directory for build library"),
('build-purelib=', None, "build directory for platform-neutral distributions"),
('build-platlib=', None, "build directory for platform-specific distributions"),
(
'build-lib=',
None,
"build directory for all distribution (defaults to either "
+ "build-purelib or build-platlib",
),
('build-scripts=', None, "build directory for scripts"),
('build-temp=', 't', "temporary build directory"),
(
'plat-name=',
'p',
"platform name to build for, if supported "
"(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', "forcibly build everything (ignore file timestamps)"),
('executable=', 'e', "specify final destination interpreter path (build.py)"),
]
boolean_options = ['debug', 'force']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
('help-compiler', None, "list available compilers", show_compilers),
]
def initialize_options(self):
self.build_base = 'build'
@@ -69,7 +66,7 @@ class build(Command):
self.executable = None
self.parallel = None
def finalize_options(self):
def finalize_options(self): # noqa: C901
if self.plat_name is None:
self.plat_name = get_platform()
else:
@@ -78,10 +75,11 @@ class build(Command):
# other platforms.
if os.name != 'nt':
raise DistutilsOptionError(
"--plat-name only supported on Windows (try "
"using './configure --help' on your platform)")
"--plat-name only supported on Windows (try "
"using './configure --help' on your platform)"
)
plat_specifier = ".%s-%d.%d" % (self.plat_name, *sys.version_info[:2])
plat_specifier = ".{}-{}".format(self.plat_name, sys.implementation.cache_tag)
# Make it so Python 2.x and Python 2.x with --with-pydebug don't
# share the same build directories. Doing so confuses the build
@@ -95,8 +93,7 @@ class build(Command):
if self.build_purelib is None:
self.build_purelib = os.path.join(self.build_base, 'lib')
if self.build_platlib is None:
self.build_platlib = os.path.join(self.build_base,
'lib' + plat_specifier)
self.build_platlib = os.path.join(self.build_base, 'lib' + plat_specifier)
# 'build_lib' is the actual directory that we will use for this
# particular module distribution -- if user didn't supply it, pick
@@ -110,11 +107,11 @@ class build(Command):
# 'build_temp' -- temporary directory for compiler turds,
# "build/temp.<plat>"
if self.build_temp is None:
self.build_temp = os.path.join(self.build_base,
'temp' + plat_specifier)
self.build_temp = os.path.join(self.build_base, 'temp' + plat_specifier)
if self.build_scripts is None:
self.build_scripts = os.path.join(self.build_base,
'scripts-%d.%d' % sys.version_info[:2])
self.build_scripts = os.path.join(
self.build_base, 'scripts-%d.%d' % sys.version_info[:2]
)
if self.executable is None and sys.executable:
self.executable = os.path.normpath(sys.executable)
@@ -134,7 +131,6 @@ class build(Command):
for cmd_name in self.get_sub_commands():
self.run_command(cmd_name)
# -- Predicates for the sub-command list ---------------------------
def has_pure_modules(self):
@@ -149,9 +145,9 @@ class build(Command):
def has_scripts(self):
return self.distribution.has_scripts()
sub_commands = [('build_py', has_pure_modules),
('build_clib', has_c_libraries),
('build_ext', has_ext_modules),
('build_scripts', has_scripts),
]
sub_commands = [
('build_py', has_pure_modules),
('build_clib', has_c_libraries),
('build_ext', has_ext_modules),
('build_scripts', has_scripts),
]

View File

@@ -16,12 +16,14 @@ module."""
import os
from distutils.core import Command
from distutils.errors import *
from distutils.errors import DistutilsSetupError
from distutils.sysconfig import customize_compiler
from distutils import log
def show_compilers():
from distutils.ccompiler import show_compilers
show_compilers()
@@ -30,24 +32,18 @@ class build_clib(Command):
description = "build C/C++ libraries used by Python extensions"
user_options = [
('build-clib=', 'b',
"directory to build C/C++ libraries to"),
('build-temp=', 't',
"directory to put temporary build by-products"),
('debug', 'g',
"compile with debugging information"),
('force', 'f',
"forcibly build everything (ignore file timestamps)"),
('compiler=', 'c',
"specify the compiler type"),
]
('build-clib=', 'b', "directory to build C/C++ libraries to"),
('build-temp=', 't', "directory to put temporary build by-products"),
('debug', 'g', "compile with debugging information"),
('force', 'f', "forcibly build everything (ignore file timestamps)"),
('compiler=', 'c', "specify the compiler type"),
]
boolean_options = ['debug', 'force']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
('help-compiler', None, "list available compilers", show_compilers),
]
def initialize_options(self):
self.build_clib = None
@@ -64,19 +60,20 @@ class build_clib(Command):
self.force = 0
self.compiler = None
def finalize_options(self):
# This might be confusing: both build-clib and build-temp default
# to build-temp as defined by the "build" command. This is because
# I think that C libraries are really just temporary build
# by-products, at least from the point of view of building Python
# extensions -- but I want to keep my options open.
self.set_undefined_options('build',
('build_temp', 'build_clib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'))
self.set_undefined_options(
'build',
('build_temp', 'build_clib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'),
)
self.libraries = self.distribution.libraries
if self.libraries:
@@ -90,23 +87,23 @@ class build_clib(Command):
# XXX same as for build_ext -- what about 'self.define' and
# 'self.undef' ?
def run(self):
if not self.libraries:
return
# Yech -- this is cut 'n pasted from build_ext.py!
from distutils.ccompiler import new_compiler
self.compiler = new_compiler(compiler=self.compiler,
dry_run=self.dry_run,
force=self.force)
self.compiler = new_compiler(
compiler=self.compiler, dry_run=self.dry_run, force=self.force
)
customize_compiler(self.compiler)
if self.include_dirs is not None:
self.compiler.set_include_dirs(self.include_dirs)
if self.define is not None:
# 'define' option is a list of (name,value) tuples
for (name,value) in self.define:
for (name, value) in self.define:
self.compiler.define_macro(name, value)
if self.undef is not None:
for macro in self.undef:
@@ -114,7 +111,6 @@ class build_clib(Command):
self.build_libraries(self.libraries)
def check_library_list(self, libraries):
"""Ensure that the list of libraries is valid.
@@ -126,30 +122,31 @@ class build_clib(Command):
just returns otherwise.
"""
if not isinstance(libraries, list):
raise DistutilsSetupError(
"'libraries' option must be a list of tuples")
raise DistutilsSetupError("'libraries' option must be a list of tuples")
for lib in libraries:
if not isinstance(lib, tuple) and len(lib) != 2:
raise DistutilsSetupError(
"each element of 'libraries' must a 2-tuple")
raise DistutilsSetupError("each element of 'libraries' must a 2-tuple")
name, build_info = lib
if not isinstance(name, str):
raise DistutilsSetupError(
"first element of each tuple in 'libraries' "
"must be a string (the library name)")
"first element of each tuple in 'libraries' "
"must be a string (the library name)"
)
if '/' in name or (os.sep != '/' and os.sep in name):
raise DistutilsSetupError("bad library name '%s': "
"may not contain directory separators" % lib[0])
raise DistutilsSetupError(
"bad library name '%s': "
"may not contain directory separators" % lib[0]
)
if not isinstance(build_info, dict):
raise DistutilsSetupError(
"second element of each tuple in 'libraries' "
"must be a dictionary (build info)")
"second element of each tuple in 'libraries' "
"must be a dictionary (build info)"
)
def get_library_names(self):
# Assume the library list is valid -- 'check_library_list()' is
@@ -162,7 +159,6 @@ class build_clib(Command):
lib_names.append(lib_name)
return lib_names
def get_source_files(self):
self.check_library_list(self.libraries)
filenames = []
@@ -170,22 +166,23 @@ class build_clib(Command):
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name
)
filenames.extend(sources)
return filenames
def build_libraries(self, libraries):
for (lib_name, build_info) in libraries:
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name
)
sources = list(sources)
log.info("building '%s' library", lib_name)
@@ -195,15 +192,17 @@ class build_clib(Command):
# files in a temporary build directory.)
macros = build_info.get('macros')
include_dirs = build_info.get('include_dirs')
objects = self.compiler.compile(sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
debug=self.debug)
objects = self.compiler.compile(
sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
debug=self.debug,
)
# Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.)
self.compiler.create_static_lib(objects, lib_name,
output_dir=self.build_clib,
debug=self.debug)
self.compiler.create_static_lib(
objects, lib_name, output_dir=self.build_clib, debug=self.debug
)

View File

@@ -9,7 +9,14 @@ import os
import re
import sys
from distutils.core import Command
from distutils.errors import *
from distutils.errors import (
DistutilsOptionError,
DistutilsSetupError,
CCompilerError,
DistutilsError,
CompileError,
DistutilsPlatformError,
)
from distutils.sysconfig import customize_compiler, get_python_version
from distutils.sysconfig import get_config_h_filename
from distutils.dep_util import newer_group
@@ -22,12 +29,12 @@ from site import USER_BASE
# 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 \
(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
def show_compilers ():
def show_compilers():
from distutils.ccompiler import show_compilers
show_compilers()
@@ -55,54 +62,50 @@ class build_ext(Command):
sep_by = " (separated by '%s')" % os.pathsep
user_options = [
('build-lib=', 'b',
"directory for compiled extension modules"),
('build-temp=', 't',
"directory for temporary files (build by-products)"),
('plat-name=', 'p',
"platform name to cross-compile for, if supported "
"(default: %s)" % get_platform()),
('inplace', 'i',
"ignore build-lib and put compiled extensions into the source " +
"directory alongside your pure Python modules"),
('include-dirs=', 'I',
"list of directories to search for header files" + sep_by),
('define=', 'D',
"C preprocessor macros to define"),
('undef=', 'U',
"C preprocessor macros to undefine"),
('libraries=', 'l',
"external C libraries to link with"),
('library-dirs=', 'L',
"directories to search for external C libraries" + sep_by),
('rpath=', 'R',
"directories to search for shared C libraries at runtime"),
('link-objects=', 'O',
"extra explicit link objects to include in the link"),
('debug', 'g',
"compile/link with debugging information"),
('force', 'f',
"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,
"list of SWIG command line options"),
('swig=', None,
"path to the SWIG executable"),
('user', None,
"add user include, library and rpath")
]
('build-lib=', 'b', "directory for compiled extension modules"),
('build-temp=', 't', "directory for temporary files (build by-products)"),
(
'plat-name=',
'p',
"platform name to cross-compile for, if supported "
"(default: %s)" % get_platform(),
),
(
'inplace',
'i',
"ignore build-lib and put compiled extensions into the source "
+ "directory alongside your pure Python modules",
),
(
'include-dirs=',
'I',
"list of directories to search for header files" + sep_by,
),
('define=', 'D', "C preprocessor macros to define"),
('undef=', 'U', "C preprocessor macros to undefine"),
('libraries=', 'l', "external C libraries to link with"),
(
'library-dirs=',
'L',
"directories to search for external C libraries" + sep_by,
),
('rpath=', 'R', "directories to search for shared C libraries at runtime"),
('link-objects=', 'O', "extra explicit link objects to include in the link"),
('debug', 'g', "compile/link with debugging information"),
('force', 'f', "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, "list of SWIG command line options"),
('swig=', None, "path to the SWIG executable"),
('user', None, "add user include, library and rpath"),
]
boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
('help-compiler', None, "list available compilers", show_compilers),
]
def initialize_options(self):
self.extensions = None
@@ -128,18 +131,19 @@ class build_ext(Command):
self.user = None
self.parallel = None
def finalize_options(self):
def finalize_options(self): # noqa: C901
from distutils import sysconfig
self.set_undefined_options('build',
('build_lib', 'build_lib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'),
('parallel', 'parallel'),
('plat_name', 'plat_name'),
)
self.set_undefined_options(
'build',
('build_lib', 'build_lib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'),
('parallel', 'parallel'),
('plat_name', 'plat_name'),
)
if self.package is None:
self.package = self.distribution.ext_package
@@ -164,8 +168,7 @@ class build_ext(Command):
# any local include dirs take precedence.
self.include_dirs.extend(py_include.split(os.path.pathsep))
if plat_py_include != py_include:
self.include_dirs.extend(
plat_py_include.split(os.path.pathsep))
self.include_dirs.extend(plat_py_include.split(os.path.pathsep))
self.ensure_string_list('libraries')
self.ensure_string_list('link_objects')
@@ -202,9 +205,7 @@ class build_ext(Command):
# Append the source distribution include and library directories,
# this allows distutils on windows to work in the source tree
self.include_dirs.append(os.path.dirname(get_config_h_filename()))
_sys_home = getattr(sys, '_home', None)
if _sys_home:
self.library_dirs.append(_sys_home)
self.library_dirs.append(sys.base_exec_prefix)
# Use the .lib files for the correct architecture
if self.plat_name == 'win32':
@@ -222,9 +223,11 @@ class build_ext(Command):
if sys.platform[:6] == 'cygwin':
if not sysconfig.python_build:
# building third party extensions
self.library_dirs.append(os.path.join(sys.prefix, "lib",
"python" + get_python_version(),
"config"))
self.library_dirs.append(
os.path.join(
sys.prefix, "lib", "python" + get_python_version(), "config"
)
)
else:
# building python standard extensions
self.library_dirs.append('.')
@@ -232,7 +235,7 @@ class build_ext(Command):
# For building extensions with a shared Python library,
# Python's library directory must be appended to library_dirs
# See Issues: #1600860, #4366
if (sysconfig.get_config_var('Py_ENABLE_SHARED')):
if sysconfig.get_config_var('Py_ENABLE_SHARED'):
if not sysconfig.python_build:
# building third party extensions
self.library_dirs.append(sysconfig.get_config_var('LIBDIR'))
@@ -276,7 +279,7 @@ class build_ext(Command):
except ValueError:
raise DistutilsOptionError("parallel should be an integer")
def run(self):
def run(self): # noqa: C901
from distutils.ccompiler import new_compiler
# 'self.extensions', as supplied by setup.py, is a list of
@@ -304,10 +307,12 @@ class build_ext(Command):
# Setup the CCompiler object that we'll use to do all the
# compiling and linking
self.compiler = new_compiler(compiler=self.compiler,
verbose=self.verbose,
dry_run=self.dry_run,
force=self.force)
self.compiler = new_compiler(
compiler=self.compiler,
verbose=self.verbose,
dry_run=self.dry_run,
force=self.force,
)
customize_compiler(self.compiler)
# If we are cross-compiling, init the compiler now (if we are not
# cross-compiling, init would not hurt, but people may rely on
@@ -340,7 +345,7 @@ class build_ext(Command):
# Now actually compile and link everything.
self.build_extensions()
def check_extensions_list(self, extensions):
def check_extensions_list(self, extensions): # noqa: C901
"""Ensure that the list of extensions (presumably provided as a
command option 'extensions') is valid, i.e. it is a list of
Extension objects. We also support the old-style list of 2-tuples,
@@ -352,34 +357,40 @@ class build_ext(Command):
"""
if not isinstance(extensions, list):
raise DistutilsSetupError(
"'ext_modules' option must be a list of Extension instances")
"'ext_modules' option must be a list of Extension instances"
)
for i, ext in enumerate(extensions):
if isinstance(ext, Extension):
continue # OK! (assume type-checking done
# by Extension constructor)
continue # OK! (assume type-checking done
# by Extension constructor)
if not isinstance(ext, tuple) or len(ext) != 2:
raise DistutilsSetupError(
"each element of 'ext_modules' option must be an "
"Extension instance or 2-tuple")
"each element of 'ext_modules' option must be an "
"Extension instance or 2-tuple"
)
ext_name, build_info = ext
log.warn("old-style (ext_name, build_info) tuple found in "
"ext_modules for extension '%s' "
"-- please convert to Extension instance", ext_name)
log.warn(
"old-style (ext_name, build_info) tuple found in "
"ext_modules for extension '%s' "
"-- please convert to Extension instance",
ext_name,
)
if not (isinstance(ext_name, str) and
extension_name_re.match(ext_name)):
if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)):
raise DistutilsSetupError(
"first element of each tuple in 'ext_modules' "
"must be the extension name (a string)")
"first element of each tuple in 'ext_modules' "
"must be the extension name (a string)"
)
if not isinstance(build_info, dict):
raise DistutilsSetupError(
"second element of each tuple in 'ext_modules' "
"must be a dictionary (build info)")
"second element of each tuple in 'ext_modules' "
"must be a dictionary (build info)"
)
# OK, the (ext_name, build_info) dict is type-safe: convert it
# to an Extension instance.
@@ -387,9 +398,14 @@ class build_ext(Command):
# Easy stuff: one-to-one mapping from dict elements to
# instance attributes.
for key in ('include_dirs', 'library_dirs', 'libraries',
'extra_objects', 'extra_compile_args',
'extra_link_args'):
for key in (
'include_dirs',
'library_dirs',
'libraries',
'extra_objects',
'extra_compile_args',
'extra_link_args',
):
val = build_info.get(key)
if val is not None:
setattr(ext, key, val)
@@ -397,8 +413,7 @@ class build_ext(Command):
# Medium-easy stuff: same syntax/semantics, different names.
ext.runtime_library_dirs = build_info.get('rpath')
if 'def_file' in build_info:
log.warn("'def_file' element of build info dict "
"no longer supported")
log.warn("'def_file' element of build info dict " "no longer supported")
# Non-trivial stuff: 'macros' split into 'define_macros'
# and 'undef_macros'.
@@ -409,8 +424,9 @@ class build_ext(Command):
for macro in macros:
if not (isinstance(macro, tuple) and len(macro) in (1, 2)):
raise DistutilsSetupError(
"'macros' element of build info dict "
"must be 1- or 2-tuple")
"'macros' element of build info dict "
"must be 1- or 2-tuple"
)
if len(macro) == 1:
ext.undef_macros.append(macro[0])
elif len(macro) == 2:
@@ -463,8 +479,9 @@ class build_ext(Command):
return
with ThreadPoolExecutor(max_workers=workers) as executor:
futures = [executor.submit(self.build_extension, ext)
for ext in self.extensions]
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()
@@ -481,16 +498,16 @@ class build_ext(Command):
except (CCompilerError, DistutilsError, CompileError) as e:
if not ext.optional:
raise
self.warn('building extension "%s" failed: %s' %
(ext.name, e))
self.warn('building extension "{}" failed: {}'.format(ext.name, e))
def build_extension(self, ext):
sources = ext.sources
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'ext_modules' option (extension '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % ext.name)
"in 'ext_modules' option (extension '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % ext.name
)
# sort to make the resulting .so file build reproducible
sources = sorted(sources)
@@ -527,13 +544,15 @@ class build_ext(Command):
for undef in ext.undef_macros:
macros.append((undef,))
objects = self.compiler.compile(sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=ext.include_dirs,
debug=self.debug,
extra_postargs=extra_args,
depends=ext.depends)
objects = self.compiler.compile(
sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=ext.include_dirs,
debug=self.debug,
extra_postargs=extra_args,
depends=ext.depends,
)
# XXX outdated variable, kept here in case third-part code
# needs it.
@@ -550,7 +569,8 @@ class build_ext(Command):
language = ext.language or self.compiler.detect_language(sources)
self.compiler.link_shared_object(
objects, ext_path,
objects,
ext_path,
libraries=self.get_libraries(ext),
library_dirs=ext.library_dirs,
runtime_library_dirs=ext.runtime_library_dirs,
@@ -558,7 +578,8 @@ class build_ext(Command):
export_symbols=self.get_export_symbols(ext),
debug=self.debug,
build_temp=self.build_temp,
target_lang=language)
target_lang=language,
)
def swig_sources(self, sources, extension):
"""Walk the list of source files in 'sources', looking for SWIG
@@ -578,15 +599,18 @@ class build_ext(Command):
if self.swig_cpp:
log.warn("--swig-cpp is deprecated - use --swig-opts=-c++")
if self.swig_cpp or ('-c++' in self.swig_opts) or \
('-c++' in extension.swig_opts):
if (
self.swig_cpp
or ('-c++' in self.swig_opts)
or ('-c++' in extension.swig_opts)
):
target_ext = '.cpp'
else:
target_ext = '.c'
for source in sources:
(base, ext) = os.path.splitext(source)
if ext == ".i": # SWIG interface file
if ext == ".i": # SWIG interface file
new_sources.append(base + '_wrap' + target_ext)
swig_sources.append(source)
swig_targets[source] = new_sources[-1]
@@ -633,8 +657,9 @@ class build_ext(Command):
return "swig.exe"
else:
raise DistutilsPlatformError(
"I don't know how to find (much less run) SWIG "
"on platform '%s'" % os.name)
"I don't know how to find (much less run) SWIG "
"on platform '%s'" % os.name
)
# -- Name generators -----------------------------------------------
# (extension names, filenames, whatever)
@@ -652,7 +677,7 @@ class build_ext(Command):
# no further work needed
# returning :
# build_dir/package/path/filename
filename = os.path.join(*modpath[:-1]+[filename])
filename = os.path.join(*modpath[:-1] + [filename])
return os.path.join(self.build_lib, filename)
# the inplace option requires to find the package directory
@@ -680,6 +705,7 @@ class build_ext(Command):
"foo\bar.pyd").
"""
from distutils.sysconfig import get_config_var
ext_path = ext_name.split('.')
ext_suffix = get_config_var('EXT_SUFFIX')
return os.path.join(*ext_path) + ext_suffix
@@ -705,7 +731,7 @@ class build_ext(Command):
ext.export_symbols.append(initfunc_name)
return ext.export_symbols
def get_libraries(self, ext):
def get_libraries(self, ext): # noqa: C901
"""Return the list of libraries to link against when building a
shared extension. On most platforms, this is just 'ext.libraries';
on Windows, we add the Python library (eg. python20.dll).
@@ -717,12 +743,15 @@ class build_ext(Command):
# Append '_d' to the python import library on debug builds.
if sys.platform == "win32":
from distutils._msvccompiler import MSVCCompiler
if not isinstance(self.compiler, MSVCCompiler):
template = "python%d%d"
if self.debug:
template = template + '_d'
pythonlib = (template %
(sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
pythonlib = template % (
sys.hexversion >> 24,
(sys.hexversion >> 16) & 0xFF,
)
# don't extend ext.libraries, it may be shared with other
# extensions, it is a reference to the original list
return ext.libraries + [pythonlib]
@@ -736,6 +765,7 @@ class build_ext(Command):
# Windows like MinGW) it is simply necessary that all symbols in
# shared libraries are resolved at link time.
from distutils.sysconfig import get_config_var
link_libpython = False
if get_config_var('Py_ENABLE_SHARED'):
# A native build on an Android device or on Cygwin

View File

@@ -8,11 +8,12 @@ import sys
import glob
from distutils.core import Command
from distutils.errors import *
from distutils.errors import DistutilsOptionError, DistutilsFileError
from distutils.util import convert_path
from distutils import log
class build_py (Command):
class build_py(Command):
description = "\"build\" pure Python modules (copy to build directory)"
@@ -20,14 +21,17 @@ class build_py (Command):
('build-lib=', 'd', "directory to \"build\" (copy) to"),
('compile', 'c', "compile .py to .pyc"),
('no-compile', None, "don't compile .py files [default]"),
('optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
(
'optimize=',
'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
),
('force', 'f', "forcibly build everything (ignore file timestamps)"),
]
]
boolean_options = ['compile', 'force']
negative_opt = {'no-compile' : 'compile'}
negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
self.build_lib = None
@@ -40,9 +44,9 @@ class build_py (Command):
self.force = None
def finalize_options(self):
self.set_undefined_options('build',
('build_lib', 'build_lib'),
('force', 'force'))
self.set_undefined_options(
'build', ('build_lib', 'build_lib'), ('force', 'force')
)
# Get the distribution options that are aliases for build_py
# options -- list of packages and list of modules.
@@ -109,42 +113,42 @@ class build_py (Command):
# Length of path to strip from found files
plen = 0
if src_dir:
plen = len(src_dir)+1
plen = len(src_dir) + 1
# Strip directory from globbed filenames
filenames = [
file[plen:] for file in self.find_data_files(package, src_dir)
]
filenames = [file[plen:] for file in self.find_data_files(package, src_dir)]
data.append((package, src_dir, build_dir, filenames))
return data
def find_data_files(self, package, src_dir):
"""Return filenames for package's data files in 'src_dir'"""
globs = (self.package_data.get('', [])
+ self.package_data.get(package, []))
globs = self.package_data.get('', []) + self.package_data.get(package, [])
files = []
for pattern in globs:
# Each pattern has to be converted to a platform-specific path
filelist = glob.glob(os.path.join(glob.escape(src_dir), convert_path(pattern)))
filelist = glob.glob(
os.path.join(glob.escape(src_dir), convert_path(pattern))
)
# Files that match more than one pattern are only added once
files.extend([fn for fn in filelist if fn not in files
and os.path.isfile(fn)])
files.extend(
[fn for fn in filelist if fn not in files and os.path.isfile(fn)]
)
return files
def build_package_data(self):
"""Copy data files into build directory"""
lastdir = None
for package, src_dir, build_dir, filenames in self.data_files:
for filename in filenames:
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
self.copy_file(os.path.join(src_dir, filename), target,
preserve_mode=False)
self.copy_file(
os.path.join(src_dir, filename), target, preserve_mode=False
)
def get_package_dir(self, package):
"""Return the directory, relative to the top of the source
distribution, where package 'package' should be found
(at least according to the 'package_dir' option, if any)."""
distribution, where package 'package' should be found
(at least according to the 'package_dir' option, if any)."""
path = package.split('.')
if not self.package_dir:
@@ -188,20 +192,19 @@ class build_py (Command):
if package_dir != "":
if not os.path.exists(package_dir):
raise DistutilsFileError(
"package directory '%s' does not exist" % package_dir)
"package directory '%s' does not exist" % package_dir
)
if not os.path.isdir(package_dir):
raise DistutilsFileError(
"supposed package directory '%s' exists, "
"but is not a directory" % package_dir)
"supposed package directory '%s' exists, "
"but is not a directory" % package_dir
)
# Require __init__.py for all but the "root package"
# Directories without __init__.py are namespace packages (PEP 420).
if package:
init_py = os.path.join(package_dir, "__init__.py")
if os.path.isfile(init_py):
return init_py
else:
log.warn(("package init file '%s' not found " +
"(or not a regular file)"), init_py)
# Either not in a package at all (__init__.py not expected), or
# __init__.py doesn't exist -- so don't return the filename.
@@ -313,17 +316,21 @@ class build_py (Command):
outputs.append(filename)
if include_bytecode:
if self.compile:
outputs.append(importlib.util.cache_from_source(
filename, optimization=''))
outputs.append(
importlib.util.cache_from_source(filename, optimization='')
)
if self.optimize > 0:
outputs.append(importlib.util.cache_from_source(
filename, optimization=self.optimize))
outputs.append(
importlib.util.cache_from_source(
filename, optimization=self.optimize
)
)
outputs += [
os.path.join(build_dir, filename)
for package, src_dir, build_dir, filenames in self.data_files
for filename in filenames
]
]
return outputs
@@ -332,7 +339,8 @@ class build_py (Command):
package = package.split('.')
elif not isinstance(package, (list, tuple)):
raise TypeError(
"'package' must be a string (dot-separated), list, or tuple")
"'package' must be a string (dot-separated), list, or tuple"
)
# Now put the module source file into the "build" area -- this is
# easy, we just copy it somewhere under self.build_lib (the build
@@ -377,6 +385,7 @@ class build_py (Command):
return
from distutils.util import byte_compile
prefix = self.build_lib
if prefix[-1] != os.sep:
prefix = prefix + os.sep
@@ -385,8 +394,14 @@ class build_py (Command):
# method of the "install_lib" command, except for the determination
# of the 'prefix' string. Hmmm.
if self.compile:
byte_compile(files, optimize=0,
force=self.force, prefix=prefix, dry_run=self.dry_run)
byte_compile(
files, optimize=0, force=self.force, prefix=prefix, dry_run=self.dry_run
)
if self.optimize > 0:
byte_compile(files, optimize=self.optimize,
force=self.force, prefix=prefix, dry_run=self.dry_run)
byte_compile(
files,
optimize=self.optimize,
force=self.force,
prefix=prefix,
dry_run=self.dry_run,
)

View File

@@ -2,7 +2,8 @@
Implements the Distutils 'build_scripts' command."""
import os, re
import os
import re
from stat import ST_MODE
from distutils import sysconfig
from distutils.core import Command
@@ -11,8 +12,14 @@ from distutils.util import convert_path
from distutils import log
import tokenize
# check if Python is called on the first line with this expression
first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$')
shebang_pattern = re.compile('^#!.*python[0-9.]*([ \t].*)?$')
"""
Pattern matching a Python interpreter indicated in first line of a script.
"""
# for Setuptools compatibility
first_line_re = shebang_pattern
class build_scripts(Command):
@@ -22,23 +29,23 @@ class build_scripts(Command):
('build-dir=', 'd', "directory to \"build\" (copy) to"),
('force', 'f', "forcibly build everything (ignore file timestamps"),
('executable=', 'e', "specify final destination interpreter path"),
]
]
boolean_options = ['force']
def initialize_options(self):
self.build_dir = None
self.scripts = None
self.force = None
self.executable = None
self.outfiles = None
def finalize_options(self):
self.set_undefined_options('build',
('build_scripts', 'build_dir'),
('force', 'force'),
('executable', 'executable'))
self.set_undefined_options(
'build',
('build_scripts', 'build_dir'),
('force', 'force'),
('executable', 'executable'),
)
self.scripts = self.distribution.scripts
def get_source_files(self):
@@ -49,104 +56,118 @@ class build_scripts(Command):
return
self.copy_scripts()
def copy_scripts(self):
r"""Copy each script listed in 'self.scripts'; if it's marked as a
Python script in the Unix way (first line matches 'first_line_re',
ie. starts with "\#!" and contains "python"), then adjust the first
line to refer to the current Python interpreter as we copy.
"""
Copy each script listed in ``self.scripts``.
If a script is marked as a Python script (first line matches
'shebang_pattern', i.e. starts with ``#!`` and contains
"python"), then adjust in the copy the first line to refer to
the current Python interpreter.
"""
self.mkpath(self.build_dir)
outfiles = []
updated_files = []
for script in self.scripts:
adjust = False
script = convert_path(script)
outfile = os.path.join(self.build_dir, os.path.basename(script))
outfiles.append(outfile)
self._copy_script(script, outfiles, updated_files)
if not self.force and not newer(script, outfile):
log.debug("not copying %s (up-to-date)", script)
continue
self._change_modes(outfiles)
# Always open the file, but ignore failures in dry-run mode --
# that way, we'll get accurate feedback if we can read the
# script.
try:
f = open(script, "rb")
except OSError:
if not self.dry_run:
raise
f = None
else:
encoding, lines = tokenize.detect_encoding(f.readline)
f.seek(0)
first_line = f.readline()
if not first_line:
self.warn("%s is an empty file (skipping)" % script)
continue
match = first_line_re.match(first_line)
if match:
adjust = True
post_interp = match.group(1) or b''
if adjust:
log.info("copying and adjusting %s -> %s", script,
self.build_dir)
updated_files.append(outfile)
if not self.dry_run:
if not sysconfig.python_build:
executable = self.executable
else:
executable = os.path.join(
sysconfig.get_config_var("BINDIR"),
"python%s%s" % (sysconfig.get_config_var("VERSION"),
sysconfig.get_config_var("EXE")))
executable = os.fsencode(executable)
shebang = b"#!" + executable + post_interp + b"\n"
# Python parser starts to read a script using UTF-8 until
# it gets a #coding:xxx cookie. The shebang has to be the
# first line of a file, the #coding:xxx cookie cannot be
# written before. So the shebang has to be decodable from
# UTF-8.
try:
shebang.decode('utf-8')
except UnicodeDecodeError:
raise ValueError(
"The shebang ({!r}) is not decodable "
"from utf-8".format(shebang))
# If the script is encoded to a custom encoding (use a
# #coding:xxx cookie), the shebang has to be decodable from
# the script encoding too.
try:
shebang.decode(encoding)
except UnicodeDecodeError:
raise ValueError(
"The shebang ({!r}) is not decodable "
"from the script encoding ({})"
.format(shebang, encoding))
with open(outfile, "wb") as outf:
outf.write(shebang)
outf.writelines(f.readlines())
if f:
f.close()
else:
if f:
f.close()
updated_files.append(outfile)
self.copy_file(script, outfile)
if os.name == 'posix':
for file in outfiles:
if self.dry_run:
log.info("changing mode of %s", file)
else:
oldmode = os.stat(file)[ST_MODE] & 0o7777
newmode = (oldmode | 0o555) & 0o7777
if newmode != oldmode:
log.info("changing mode of %s from %o to %o",
file, oldmode, newmode)
os.chmod(file, newmode)
# XXX should we modify self.outfiles?
return outfiles, updated_files
def _copy_script(self, script, outfiles, updated_files): # noqa: C901
shebang_match = None
script = convert_path(script)
outfile = os.path.join(self.build_dir, os.path.basename(script))
outfiles.append(outfile)
if not self.force and not newer(script, outfile):
log.debug("not copying %s (up-to-date)", script)
return
# Always open the file, but ignore failures in dry-run mode
# in order to attempt to copy directly.
try:
f = tokenize.open(script)
except OSError:
if not self.dry_run:
raise
f = None
else:
first_line = f.readline()
if not first_line:
self.warn("%s is an empty file (skipping)" % script)
return
shebang_match = shebang_pattern.match(first_line)
updated_files.append(outfile)
if shebang_match:
log.info("copying and adjusting %s -> %s", script, self.build_dir)
if not self.dry_run:
if not sysconfig.python_build:
executable = self.executable
else:
executable = os.path.join(
sysconfig.get_config_var("BINDIR"),
"python%s%s"
% (
sysconfig.get_config_var("VERSION"),
sysconfig.get_config_var("EXE"),
),
)
post_interp = shebang_match.group(1) or ''
shebang = "#!" + executable + post_interp + "\n"
self._validate_shebang(shebang, f.encoding)
with open(outfile, "w", encoding=f.encoding) as outf:
outf.write(shebang)
outf.writelines(f.readlines())
if f:
f.close()
else:
if f:
f.close()
self.copy_file(script, outfile)
def _change_modes(self, outfiles):
if os.name != 'posix':
return
for file in outfiles:
self._change_mode(file)
def _change_mode(self, file):
if self.dry_run:
log.info("changing mode of %s", file)
return
oldmode = os.stat(file)[ST_MODE] & 0o7777
newmode = (oldmode | 0o555) & 0o7777
if newmode != oldmode:
log.info("changing mode of %s from %o to %o", file, oldmode, newmode)
os.chmod(file, newmode)
@staticmethod
def _validate_shebang(shebang, encoding):
# Python parser starts to read a script using UTF-8 until
# it gets a #coding:xxx cookie. The shebang has to be the
# first line of a file, the #coding:xxx cookie cannot be
# written before. So the shebang has to be encodable to
# UTF-8.
try:
shebang.encode('utf-8')
except UnicodeEncodeError:
raise ValueError(
"The shebang ({!r}) is not encodable " "to utf-8".format(shebang)
)
# If the script is encoded to a custom encoding (use a
# #coding:xxx cookie), the shebang has to be encodable to
# the script encoding too.
try:
shebang.encode(encoding)
except UnicodeEncodeError:
raise ValueError(
"The shebang ({!r}) is not encodable "
"to the script encoding ({})".format(shebang, encoding)
)

View File

@@ -2,46 +2,56 @@
Implements the Distutils 'check' command.
"""
import contextlib
from distutils.core import Command
from distutils.errors import DistutilsSetupError
try:
# docutils is installed
from docutils.utils import Reporter
from docutils.parsers.rst import Parser
from docutils import frontend
from docutils import nodes
with contextlib.suppress(ImportError):
import docutils.utils
import docutils.parsers.rst
import docutils.frontend
import docutils.nodes
class SilentReporter(Reporter):
def __init__(self, source, report_level, halt_level, stream=None,
debug=0, encoding='ascii', error_handler='replace'):
class SilentReporter(docutils.utils.Reporter):
def __init__(
self,
source,
report_level,
halt_level,
stream=None,
debug=0,
encoding='ascii',
error_handler='replace',
):
self.messages = []
Reporter.__init__(self, source, report_level, halt_level, stream,
debug, encoding, error_handler)
super().__init__(
source, report_level, halt_level, stream, debug, encoding, error_handler
)
def system_message(self, level, message, *children, **kwargs):
self.messages.append((level, message, children, kwargs))
return nodes.system_message(message, level=level,
type=self.levels[level],
*children, **kwargs)
return docutils.nodes.system_message(
message, level=level, type=self.levels[level], *children, **kwargs
)
HAS_DOCUTILS = True
except Exception:
# Catch all exceptions because exceptions besides ImportError probably
# indicate that docutils is not ported to Py3k.
HAS_DOCUTILS = False
class check(Command):
"""This command checks the meta-data of the package.
"""
description = ("perform some checks on the package")
user_options = [('metadata', 'm', 'Verify meta-data'),
('restructuredtext', 'r',
('Checks if long string meta-data syntax '
'are reStructuredText-compliant')),
('strict', 's',
'Will exit with an error if a check fails')]
"""This command checks the meta-data of the package."""
description = "perform some checks on the package"
user_options = [
('metadata', 'm', 'Verify meta-data'),
(
'restructuredtext',
'r',
(
'Checks if long string meta-data syntax '
'are reStructuredText-compliant'
),
),
('strict', 's', 'Will exit with an error if a check fails'),
]
boolean_options = ['metadata', 'restructuredtext', 'strict']
@@ -66,8 +76,11 @@ class check(Command):
if self.metadata:
self.check_metadata()
if self.restructuredtext:
if HAS_DOCUTILS:
self.check_restructuredtext()
if 'docutils' in globals():
try:
self.check_restructuredtext()
except TypeError as exc:
raise DistutilsSetupError(str(exc))
elif self.strict:
raise DistutilsSetupError('The docutils package is needed.')
@@ -80,34 +93,19 @@ class check(Command):
"""Ensures that all required elements of meta-data are supplied.
Required fields:
name, version, URL
Recommended fields:
(author and author_email) or (maintainer and maintainer_email))
name, version
Warns if any are missing.
"""
metadata = self.distribution.metadata
missing = []
for attr in ('name', 'version', 'url'):
if not (hasattr(metadata, attr) and getattr(metadata, attr)):
for attr in 'name', 'version':
if not getattr(metadata, attr, None):
missing.append(attr)
if missing:
self.warn("missing required meta-data: %s" % ', '.join(missing))
if metadata.author:
if not metadata.author_email:
self.warn("missing meta-data: if 'author' supplied, " +
"'author_email' should be supplied too")
elif metadata.maintainer:
if not metadata.maintainer_email:
self.warn("missing meta-data: if 'maintainer' supplied, " +
"'maintainer_email' should be supplied too")
else:
self.warn("missing meta-data: either (author and author_email) " +
"or (maintainer and maintainer_email) " +
"should be supplied")
self.warn("missing required meta-data: %s" % ', '.join(missing))
def check_restructuredtext(self):
"""Checks if the long string fields are reST-compliant."""
@@ -117,32 +115,37 @@ class check(Command):
if line is None:
warning = warning[1]
else:
warning = '%s (line %s)' % (warning[1], line)
warning = '{} (line {})'.format(warning[1], line)
self.warn(warning)
def _check_rst_data(self, data):
"""Returns warnings when the provided data doesn't compile."""
# the include and csv_table directives need this to be a path
source_path = self.distribution.script_name or 'setup.py'
parser = Parser()
settings = frontend.OptionParser(components=(Parser,)).get_default_values()
parser = docutils.parsers.rst.Parser()
settings = docutils.frontend.OptionParser(
components=(docutils.parsers.rst.Parser,)
).get_default_values()
settings.tab_width = 4
settings.pep_references = None
settings.rfc_references = None
reporter = SilentReporter(source_path,
settings.report_level,
settings.halt_level,
stream=settings.warning_stream,
debug=settings.debug,
encoding=settings.error_encoding,
error_handler=settings.error_encoding_error_handler)
reporter = SilentReporter(
source_path,
settings.report_level,
settings.halt_level,
stream=settings.warning_stream,
debug=settings.debug,
encoding=settings.error_encoding,
error_handler=settings.error_encoding_error_handler,
)
document = nodes.document(settings, reporter, source=source_path)
document = docutils.nodes.document(settings, reporter, source=source_path)
document.note_source(source_path, -1)
try:
parser.parse(data, document)
except AttributeError as e:
reporter.messages.append(
(-1, 'Could not finish the parsing: %s.' % e, '', {}))
(-1, 'Could not finish the parsing: %s.' % e, '', {})
)
return reporter.messages

View File

@@ -9,22 +9,25 @@ from distutils.core import Command
from distutils.dir_util import remove_tree
from distutils import log
class clean(Command):
description = "clean up temporary files from 'build' command"
user_options = [
('build-base=', 'b',
"base build directory (default: 'build.build-base')"),
('build-lib=', None,
"build directory for all modules (default: 'build.build-lib')"),
('build-temp=', 't',
"temporary build directory (default: 'build.build-temp')"),
('build-scripts=', None,
"build directory for scripts (default: 'build.build-scripts')"),
('bdist-base=', None,
"temporary directory for built distributions"),
('all', 'a',
"remove all build output, not just temporary by-products")
('build-base=', 'b', "base build directory (default: 'build.build-base')"),
(
'build-lib=',
None,
"build directory for all modules (default: 'build.build-lib')",
),
('build-temp=', 't', "temporary build directory (default: 'build.build-temp')"),
(
'build-scripts=',
None,
"build directory for scripts (default: 'build.build-scripts')",
),
('bdist-base=', None, "temporary directory for built distributions"),
('all', 'a', "remove all build output, not just temporary by-products"),
]
boolean_options = ['all']
@@ -38,13 +41,14 @@ class clean(Command):
self.all = None
def finalize_options(self):
self.set_undefined_options('build',
('build_base', 'build_base'),
('build_lib', 'build_lib'),
('build_scripts', 'build_scripts'),
('build_temp', 'build_temp'))
self.set_undefined_options('bdist',
('bdist_base', 'bdist_base'))
self.set_undefined_options(
'build',
('build_base', 'build_base'),
('build_lib', 'build_lib'),
('build_scripts', 'build_scripts'),
('build_temp', 'build_temp'),
)
self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
def run(self):
# remove the build/temp.<plat> directory (unless it's already
@@ -52,19 +56,15 @@ class clean(Command):
if os.path.exists(self.build_temp):
remove_tree(self.build_temp, dry_run=self.dry_run)
else:
log.debug("'%s' does not exist -- can't clean it",
self.build_temp)
log.debug("'%s' does not exist -- can't clean it", self.build_temp)
if self.all:
# remove build directories
for directory in (self.build_lib,
self.bdist_base,
self.build_scripts):
for directory in (self.build_lib, self.bdist_base, self.build_scripts):
if os.path.exists(directory):
remove_tree(directory, dry_run=self.dry_run)
else:
log.warn("'%s' does not exist -- can't clean it",
directory)
log.warn("'%s' does not exist -- can't clean it", directory)
# just for the heck of it, try to remove the base build directory:
# we might have emptied it right now, but if not we don't care

View File

@@ -9,7 +9,8 @@ configure-like tasks: "try to compile this C code", or "figure out where
this header file lives".
"""
import os, re
import os
import re
from distutils.core import Command
from distutils.errors import DistutilsExecError
@@ -18,32 +19,26 @@ from distutils import log
LANG_EXT = {"c": ".c", "c++": ".cxx"}
class config(Command):
description = "prepare to build"
user_options = [
('compiler=', None,
"specify the compiler type"),
('cc=', None,
"specify the compiler executable"),
('include-dirs=', 'I',
"list of directories to search for header files"),
('define=', 'D',
"C preprocessor macros to define"),
('undef=', 'U',
"C preprocessor macros to undefine"),
('libraries=', 'l',
"external C libraries to link with"),
('library-dirs=', 'L',
"directories to search for external C libraries"),
('noisy', None,
"show every action (compile, link, run, ...) taken"),
('dump-source', None,
"dump generated source files before attempting to compile them"),
]
('compiler=', None, "specify the compiler type"),
('cc=', None, "specify the compiler executable"),
('include-dirs=', 'I', "list of directories to search for header files"),
('define=', 'D', "C preprocessor macros to define"),
('undef=', 'U', "C preprocessor macros to undefine"),
('libraries=', 'l', "external C libraries to link with"),
('library-dirs=', 'L', "directories to search for external C libraries"),
('noisy', None, "show every action (compile, link, run, ...) taken"),
(
'dump-source',
None,
"dump generated source files before attempting to compile them",
),
]
# The three standard command methods: since the "config" command
# does nothing by default, these are empty.
@@ -93,9 +88,11 @@ class config(Command):
# We do this late, and only on-demand, because this is an expensive
# import.
from distutils.ccompiler import CCompiler, new_compiler
if not isinstance(self.compiler, CCompiler):
self.compiler = new_compiler(compiler=self.compiler,
dry_run=self.dry_run, force=1)
self.compiler = new_compiler(
compiler=self.compiler, dry_run=self.dry_run, force=1
)
customize_compiler(self.compiler)
if self.include_dirs:
self.compiler.set_include_dirs(self.include_dirs)
@@ -132,14 +129,16 @@ class config(Command):
self.compiler.compile([src], include_dirs=include_dirs)
return (src, obj)
def _link(self, body, headers, include_dirs, libraries, library_dirs,
lang):
def _link(self, body, headers, include_dirs, libraries, library_dirs, lang):
(src, obj) = self._compile(body, headers, include_dirs, lang)
prog = os.path.splitext(os.path.basename(src))[0]
self.compiler.link_executable([obj], prog,
libraries=libraries,
library_dirs=library_dirs,
target_lang=lang)
self.compiler.link_executable(
[obj],
prog,
libraries=libraries,
library_dirs=library_dirs,
target_lang=lang,
)
if self.compiler.exe_extension is not None:
prog = prog + self.compiler.exe_extension
@@ -158,7 +157,6 @@ class config(Command):
except OSError:
pass
# XXX these ignore the dry-run flag: what to do, what to do? even if
# you want a dry-run build, you still need some sort of configuration
# info. My inclination is to make it up to the real config command to
@@ -177,6 +175,7 @@ class config(Command):
('body' probably isn't of much use, but what the heck.)
"""
from distutils.ccompiler import CompileError
self._check_compiler()
ok = True
try:
@@ -187,8 +186,7 @@ class config(Command):
self._clean()
return ok
def search_cpp(self, pattern, body=None, headers=None, include_dirs=None,
lang="c"):
def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, lang="c"):
"""Construct a source file (just like 'try_cpp()'), run it through
the preprocessor, and return true if any line of the output matches
'pattern'. 'pattern' should either be a compiled regex object or a
@@ -220,6 +218,7 @@ class config(Command):
Return true on success, false otherwise.
"""
from distutils.ccompiler import CompileError
self._check_compiler()
try:
self._compile(body, headers, include_dirs, lang)
@@ -231,17 +230,24 @@ class config(Command):
self._clean()
return ok
def try_link(self, body, headers=None, include_dirs=None, libraries=None,
library_dirs=None, lang="c"):
def try_link(
self,
body,
headers=None,
include_dirs=None,
libraries=None,
library_dirs=None,
lang="c",
):
"""Try to compile and link a source file, built from 'body' and
'headers', to executable form. Return true on success, false
otherwise.
"""
from distutils.ccompiler import CompileError, LinkError
self._check_compiler()
try:
self._link(body, headers, include_dirs,
libraries, library_dirs, lang)
self._link(body, headers, include_dirs, libraries, library_dirs, lang)
ok = True
except (CompileError, LinkError):
ok = False
@@ -250,17 +256,26 @@ class config(Command):
self._clean()
return ok
def try_run(self, body, headers=None, include_dirs=None, libraries=None,
library_dirs=None, lang="c"):
def try_run(
self,
body,
headers=None,
include_dirs=None,
libraries=None,
library_dirs=None,
lang="c",
):
"""Try to compile, link to an executable, and run a program
built from 'body' and 'headers'. Return true on success, false
otherwise.
"""
from distutils.ccompiler import CompileError, LinkError
self._check_compiler()
try:
src, obj, exe = self._link(body, headers, include_dirs,
libraries, library_dirs, lang)
src, obj, exe = self._link(
body, headers, include_dirs, libraries, library_dirs, lang
)
self.spawn([exe])
ok = True
except (CompileError, LinkError, DistutilsExecError):
@@ -270,13 +285,20 @@ class config(Command):
self._clean()
return ok
# -- High-level methods --------------------------------------------
# (these are the ones that are actually likely to be useful
# when implementing a real-world config command!)
def check_func(self, func, headers=None, include_dirs=None,
libraries=None, library_dirs=None, decl=0, call=0):
def check_func(
self,
func,
headers=None,
include_dirs=None,
libraries=None,
library_dirs=None,
decl=0,
call=0,
):
"""Determine if function 'func' is available by constructing a
source file that refers to 'func', and compiles and links it.
If everything succeeds, returns true; otherwise returns false.
@@ -302,11 +324,16 @@ class config(Command):
body.append("}")
body = "\n".join(body) + "\n"
return self.try_link(body, headers, include_dirs,
libraries, library_dirs)
return self.try_link(body, headers, include_dirs, libraries, library_dirs)
def check_lib(self, library, library_dirs=None, headers=None,
include_dirs=None, other_libraries=[]):
def check_lib(
self,
library,
library_dirs=None,
headers=None,
include_dirs=None,
other_libraries=[],
):
"""Determine if 'library' is available to be linked against,
without actually checking that any particular symbols are provided
by it. 'headers' will be used in constructing the source file to
@@ -316,17 +343,23 @@ class config(Command):
has symbols that depend on other libraries.
"""
self._check_compiler()
return self.try_link("int main (void) { }", headers, include_dirs,
[library] + other_libraries, library_dirs)
return self.try_link(
"int main (void) { }",
headers,
include_dirs,
[library] + other_libraries,
library_dirs,
)
def check_header(self, header, include_dirs=None, library_dirs=None,
lang="c"):
def check_header(self, header, include_dirs=None, library_dirs=None, lang="c"):
"""Determine if the system header file named by 'header_file'
exists and can be found by the preprocessor; return true if so,
false otherwise.
"""
return self.try_cpp(body="/* No body */", headers=[header],
include_dirs=include_dirs)
return self.try_cpp(
body="/* No body */", headers=[header], include_dirs=include_dirs
)
def dump_file(filename, head=None):
"""Dumps a file content into log.info.

View File

@@ -4,79 +4,91 @@ Implements the Distutils 'install' command."""
import sys
import os
import contextlib
import sysconfig
import itertools
from distutils import log
from distutils.core import Command
from distutils.debug import DEBUG
from distutils.sysconfig import get_config_vars
from distutils.errors import DistutilsPlatformError
from distutils.file_util import write_file
from distutils.util import convert_path, subst_vars, change_root
from distutils.util import get_platform
from distutils.errors import DistutilsOptionError
from distutils.errors import DistutilsOptionError, DistutilsPlatformError
from . import _framework_compat as fw
from .. import _collections
from site import USER_BASE
from site import USER_SITE
HAS_USER_SITE = True
WINDOWS_SCHEME = {
'purelib': '$base/Lib/site-packages',
'platlib': '$base/Lib/site-packages',
'headers': '$base/Include/$dist_name',
'scripts': '$base/Scripts',
'data' : '$base',
'purelib': '{base}/Lib/site-packages',
'platlib': '{base}/Lib/site-packages',
'headers': '{base}/Include/{dist_name}',
'scripts': '{base}/Scripts',
'data': '{base}',
}
INSTALL_SCHEMES = {
'unix_prefix': {
'purelib': '$base/lib/python$py_version_short/site-packages',
'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages',
'headers': '$base/include/python$py_version_short$abiflags/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
'unix_home': {
'purelib': '$base/lib/python',
'platlib': '$base/$platlibdir/python',
'headers': '$base/include/python/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
'posix_prefix': {
'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages',
'platlib': '{platbase}/{platlibdir}/{implementation_lower}'
'{py_version_short}/site-packages',
'headers': '{base}/include/{implementation_lower}'
'{py_version_short}{abiflags}/{dist_name}',
'scripts': '{base}/bin',
'data': '{base}',
},
'posix_home': {
'purelib': '{base}/lib/{implementation_lower}',
'platlib': '{base}/{platlibdir}/{implementation_lower}',
'headers': '{base}/include/{implementation_lower}/{dist_name}',
'scripts': '{base}/bin',
'data': '{base}',
},
'nt': WINDOWS_SCHEME,
'pypy': {
'purelib': '$base/site-packages',
'platlib': '$base/site-packages',
'headers': '$base/include/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
'purelib': '{base}/site-packages',
'platlib': '{base}/site-packages',
'headers': '{base}/include/{dist_name}',
'scripts': '{base}/bin',
'data': '{base}',
},
'pypy_nt': {
'purelib': '$base/site-packages',
'platlib': '$base/site-packages',
'headers': '$base/include/$dist_name',
'scripts': '$base/Scripts',
'data' : '$base',
},
}
'purelib': '{base}/site-packages',
'platlib': '{base}/site-packages',
'headers': '{base}/include/{dist_name}',
'scripts': '{base}/Scripts',
'data': '{base}',
},
}
# user site schemes
if HAS_USER_SITE:
INSTALL_SCHEMES['nt_user'] = {
'purelib': '$usersite',
'platlib': '$usersite',
'headers': '$userbase/Python$py_version_nodot/Include/$dist_name',
'scripts': '$userbase/Python$py_version_nodot/Scripts',
'data' : '$userbase',
}
'purelib': '{usersite}',
'platlib': '{usersite}',
'headers': '{userbase}/{implementation}{py_version_nodot_plat}'
'/Include/{dist_name}',
'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts',
'data': '{userbase}',
}
INSTALL_SCHEMES['posix_user'] = {
'purelib': '{usersite}',
'platlib': '{usersite}',
'headers': '{userbase}/include/{implementation_lower}'
'{py_version_short}{abiflags}/{dist_name}',
'scripts': '{userbase}/bin',
'data': '{userbase}',
}
INSTALL_SCHEMES.update(fw.schemes)
INSTALL_SCHEMES['unix_user'] = {
'purelib': '$usersite',
'platlib': '$usersite',
'headers':
'$userbase/include/python$py_version_short$abiflags/$dist_name',
'scripts': '$userbase/bin',
'data' : '$userbase',
}
# The keys to an installation scheme; if any new types of files are to be
# installed, be sure to add an entry to every installation scheme above,
@@ -84,78 +96,162 @@ if HAS_USER_SITE:
SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')
def _load_sysconfig_schemes():
with contextlib.suppress(AttributeError):
return {
scheme: sysconfig.get_paths(scheme, expand=False)
for scheme in sysconfig.get_scheme_names()
}
def _load_schemes():
"""
Extend default schemes with schemes from sysconfig.
"""
sysconfig_schemes = _load_sysconfig_schemes() or {}
return {
scheme: {
**INSTALL_SCHEMES.get(scheme, {}),
**sysconfig_schemes.get(scheme, {}),
}
for scheme in set(itertools.chain(INSTALL_SCHEMES, sysconfig_schemes))
}
def _get_implementation():
if hasattr(sys, 'pypy_version_info'):
return 'PyPy'
else:
return 'Python'
def _select_scheme(ob, name):
scheme = _inject_headers(name, _load_scheme(_resolve_scheme(name)))
vars(ob).update(_remove_set(ob, _scheme_attrs(scheme)))
def _remove_set(ob, attrs):
"""
Include only attrs that are None in ob.
"""
return {key: value for key, value in attrs.items() if getattr(ob, key) is None}
def _resolve_scheme(name):
os_name, sep, key = name.partition('_')
try:
resolved = sysconfig.get_preferred_scheme(key)
except Exception:
resolved = fw.scheme(_pypy_hack(name))
return resolved
def _load_scheme(name):
return _load_schemes()[name]
def _inject_headers(name, scheme):
"""
Given a scheme name and the resolved scheme,
if the scheme does not include headers, resolve
the fallback scheme for the name and use headers
from it. pypa/distutils#88
"""
# Bypass the preferred scheme, which may not
# have defined headers.
fallback = _load_scheme(_pypy_hack(name))
scheme.setdefault('headers', fallback['headers'])
return scheme
def _scheme_attrs(scheme):
"""Resolve install directories by applying the install schemes."""
return {f'install_{key}': scheme[key] for key in SCHEME_KEYS}
def _pypy_hack(name):
PY37 = sys.version_info < (3, 8)
old_pypy = hasattr(sys, 'pypy_version_info') and PY37
prefix = not name.endswith(('_user', '_home'))
pypy_name = 'pypy' + '_nt' * (os.name == 'nt')
return pypy_name if old_pypy and prefix else name
class install(Command):
description = "install everything from build directory"
user_options = [
# Select installation scheme and set base director(y|ies)
('prefix=', None,
"installation prefix"),
('exec-prefix=', None,
"(Unix only) prefix for platform-specific files"),
('home=', None,
"(Unix only) home directory to install under"),
('prefix=', None, "installation prefix"),
('exec-prefix=', None, "(Unix only) prefix for platform-specific files"),
('home=', None, "(Unix only) home directory to install under"),
# Or, just set the base director(y|ies)
('install-base=', None,
"base installation directory (instead of --prefix or --home)"),
('install-platbase=', None,
"base installation directory for platform-specific files " +
"(instead of --exec-prefix or --home)"),
('root=', None,
"install everything relative to this alternate root directory"),
(
'install-base=',
None,
"base installation directory (instead of --prefix or --home)",
),
(
'install-platbase=',
None,
"base installation directory for platform-specific files "
+ "(instead of --exec-prefix or --home)",
),
('root=', None, "install everything relative to this alternate root directory"),
# Or, explicitly set the installation scheme
('install-purelib=', None,
"installation directory for pure Python module distributions"),
('install-platlib=', None,
"installation directory for non-pure module distributions"),
('install-lib=', None,
"installation directory for all module distributions " +
"(overrides --install-purelib and --install-platlib)"),
('install-headers=', None,
"installation directory for C/C++ headers"),
('install-scripts=', None,
"installation directory for Python scripts"),
('install-data=', None,
"installation directory for data files"),
(
'install-purelib=',
None,
"installation directory for pure Python module distributions",
),
(
'install-platlib=',
None,
"installation directory for non-pure module distributions",
),
(
'install-lib=',
None,
"installation directory for all module distributions "
+ "(overrides --install-purelib and --install-platlib)",
),
('install-headers=', None, "installation directory for C/C++ headers"),
('install-scripts=', None, "installation directory for Python scripts"),
('install-data=', None, "installation directory for data files"),
# Byte-compilation options -- see install_lib.py for details, as
# these are duplicated from there (but only install_lib does
# anything with them).
('compile', 'c', "compile .py to .pyc [default]"),
('no-compile', None, "don't compile .py files"),
('optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
(
'optimize=',
'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
),
# Miscellaneous control options
('force', 'f',
"force installation (overwrite any existing files)"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('force', 'f', "force installation (overwrite any existing files)"),
('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
# Where to install documentation (eventually!)
#('doc-format=', None, "format of documentation to generate"),
#('install-man=', None, "directory for Unix man pages"),
#('install-html=', None, "directory for HTML documentation"),
#('install-info=', None, "directory for GNU info files"),
('record=', None,
"filename in which to record list of installed files"),
]
# ('doc-format=', None, "format of documentation to generate"),
# ('install-man=', None, "directory for Unix man pages"),
# ('install-html=', None, "directory for HTML documentation"),
# ('install-info=', None, "directory for GNU info files"),
('record=', None, "filename in which to record list of installed files"),
]
boolean_options = ['compile', 'force', 'skip-build']
if HAS_USER_SITE:
user_options.append(('user', None,
"install in user site-package '%s'" % USER_SITE))
user_options.append(
('user', None, "install in user site-package '%s'" % USER_SITE)
)
boolean_options.append('user')
negative_opt = {'no-compile' : 'compile'}
negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
"""Initializes options."""
@@ -177,10 +273,10 @@ class install(Command):
# supplied by the user, they are filled in using the installation
# scheme implied by prefix/exec-prefix/home and the contents of
# that installation scheme.
self.install_purelib = None # for pure module distributions
self.install_platlib = None # non-pure (dists w/ extensions)
self.install_headers = None # for C/C++ headers
self.install_lib = None # set to either purelib or platlib
self.install_purelib = None # for pure module distributions
self.install_platlib = None # non-pure (dists w/ extensions)
self.install_headers = None # for C/C++ headers
self.install_lib = None # set to either purelib or platlib
self.install_scripts = None
self.install_data = None
self.install_userbase = USER_BASE
@@ -222,20 +318,19 @@ class install(Command):
# Not defined yet because we don't know anything about
# documentation yet.
#self.install_man = None
#self.install_html = None
#self.install_info = None
# self.install_man = None
# self.install_html = None
# self.install_info = None
self.record = None
# -- Option finalizing methods -------------------------------------
# (This is rather more involved than for most commands,
# because this is where the policy for installing third-
# party Python modules on various platforms given a wide
# array of user input is decided. Yes, it's quite complex!)
def finalize_options(self):
def finalize_options(self): # noqa: C901
"""Finalizes options."""
# This method (and its helpers, like 'finalize_unix()',
# 'finalize_other()', and 'select_scheme()') is where the default
@@ -251,20 +346,30 @@ class install(Command):
# Check for errors/inconsistencies in the options; first, stuff
# that's wrong on any platform.
if ((self.prefix or self.exec_prefix or self.home) and
(self.install_base or self.install_platbase)):
if (self.prefix or self.exec_prefix or self.home) and (
self.install_base or self.install_platbase
):
raise DistutilsOptionError(
"must supply either prefix/exec-prefix/home or " +
"install-base/install-platbase -- not both")
"must supply either prefix/exec-prefix/home or "
+ "install-base/install-platbase -- not both"
)
if self.home and (self.prefix or self.exec_prefix):
raise DistutilsOptionError(
"must supply either home or prefix/exec-prefix -- not both")
"must supply either home or prefix/exec-prefix -- not both"
)
if self.user and (self.prefix or self.exec_prefix or self.home or
self.install_base or self.install_platbase):
raise DistutilsOptionError("can't combine user with prefix, "
"exec_prefix/home, or install_(plat)base")
if self.user and (
self.prefix
or self.exec_prefix
or self.home
or self.install_base
or self.install_platbase
):
raise DistutilsOptionError(
"can't combine user with prefix, "
"exec_prefix/home, or install_(plat)base"
)
# Next, stuff that's wrong (or dubious) only on certain platforms.
if os.name != "posix":
@@ -278,7 +383,7 @@ class install(Command):
# input a heady brew of prefix, exec_prefix, home, install_base,
# install_platbase, user-supplied versions of
# install_{purelib,platlib,lib,scripts,data,...}, and the
# INSTALL_SCHEME dictionary above. Phew!
# install schemes. Phew!
self.dump_dirs("pre-finalize_{unix,other}")
@@ -301,23 +406,36 @@ class install(Command):
except AttributeError:
# sys.abiflags may not be defined on all platforms.
abiflags = ''
self.config_vars = {'dist_name': self.distribution.get_name(),
'dist_version': self.distribution.get_version(),
'dist_fullname': self.distribution.get_fullname(),
'py_version': py_version,
'py_version_short': '%d.%d' % sys.version_info[:2],
'py_version_nodot': '%d%d' % sys.version_info[:2],
'sys_prefix': prefix,
'prefix': prefix,
'sys_exec_prefix': exec_prefix,
'exec_prefix': exec_prefix,
'abiflags': abiflags,
'platlibdir': getattr(sys, 'platlibdir', 'lib'),
}
local_vars = {
'dist_name': self.distribution.get_name(),
'dist_version': self.distribution.get_version(),
'dist_fullname': self.distribution.get_fullname(),
'py_version': py_version,
'py_version_short': '%d.%d' % sys.version_info[:2],
'py_version_nodot': '%d%d' % sys.version_info[:2],
'sys_prefix': prefix,
'prefix': prefix,
'sys_exec_prefix': exec_prefix,
'exec_prefix': exec_prefix,
'abiflags': abiflags,
'platlibdir': getattr(sys, 'platlibdir', 'lib'),
'implementation_lower': _get_implementation().lower(),
'implementation': _get_implementation(),
}
# vars for compatibility on older Pythons
compat_vars = dict(
# Python 3.9 and earlier
py_version_nodot_plat=getattr(sys, 'winver', '').replace('.', ''),
)
if HAS_USER_SITE:
self.config_vars['userbase'] = self.install_userbase
self.config_vars['usersite'] = self.install_usersite
local_vars['userbase'] = self.install_userbase
local_vars['usersite'] = self.install_usersite
self.config_vars = _collections.DictStack(
[fw.vars(), compat_vars, sysconfig.get_config_vars(), local_vars]
)
self.expand_basedirs()
@@ -325,13 +443,14 @@ class install(Command):
# Now define config vars for the base directories so we can expand
# everything else.
self.config_vars['base'] = self.install_base
self.config_vars['platbase'] = self.install_platbase
local_vars['base'] = self.install_base
local_vars['platbase'] = self.install_platbase
if DEBUG:
from pprint import pprint
print("config vars:")
pprint(self.config_vars)
pprint(dict(self.config_vars))
# Expand "~" and configuration variables in the installation
# directories.
@@ -348,17 +467,23 @@ class install(Command):
# module distribution is pure or not. Of course, if the user
# already specified install_lib, use their selection.
if self.install_lib is None:
if self.distribution.has_ext_modules(): # has extensions: non-pure
if self.distribution.has_ext_modules(): # has extensions: non-pure
self.install_lib = self.install_platlib
else:
self.install_lib = self.install_purelib
# Convert directories from Unix /-separated syntax to the local
# convention.
self.convert_paths('lib', 'purelib', 'platlib',
'scripts', 'data', 'headers',
'userbase', 'usersite')
self.convert_paths(
'lib',
'purelib',
'platlib',
'scripts',
'data',
'headers',
'userbase',
'usersite',
)
# Deprecated
# Well, we're not actually fully completely finalized yet: we still
@@ -366,21 +491,22 @@ class install(Command):
# non-packagized module distributions (hello, Numerical Python!) to
# get their own directories.
self.handle_extra_path()
self.install_libbase = self.install_lib # needed for .pth file
self.install_libbase = self.install_lib # needed for .pth file
self.install_lib = os.path.join(self.install_lib, self.extra_dirs)
# If a new root directory was supplied, make all the installation
# dirs relative to it.
if self.root is not None:
self.change_roots('libbase', 'lib', 'purelib', 'platlib',
'scripts', 'data', 'headers')
self.change_roots(
'libbase', 'lib', 'purelib', 'platlib', 'scripts', 'data', 'headers'
)
self.dump_dirs("after prepending root")
# Find out the build directories, ie. where to install from.
self.set_undefined_options('build',
('build_base', 'build_base'),
('build_lib', 'build_lib'))
self.set_undefined_options(
'build', ('build_base', 'build_base'), ('build_lib', 'build_lib')
)
# Punt on doc directories for now -- after all, we're punting on
# documentation completely!
@@ -390,6 +516,7 @@ class install(Command):
if not DEBUG:
return
from distutils.fancy_getopt import longopt_xlate
log.debug(msg + ":")
for opt in self.user_options:
opt_name = opt[0]
@@ -407,34 +534,43 @@ class install(Command):
def finalize_unix(self):
"""Finalizes options for posix platforms."""
if self.install_base is not None or self.install_platbase is not None:
if ((self.install_lib is None and
self.install_purelib is None and
self.install_platlib is None) or
self.install_headers is None or
self.install_scripts is None or
self.install_data is None):
incomplete_scheme = (
(
self.install_lib is None
and self.install_purelib is None
and self.install_platlib is None
)
or self.install_headers is None
or self.install_scripts is None
or self.install_data is None
)
if incomplete_scheme:
raise DistutilsOptionError(
"install-base or install-platbase supplied, but "
"installation scheme is incomplete")
"install-base or install-platbase supplied, but "
"installation scheme is incomplete"
)
return
if self.user:
if self.install_userbase is None:
raise DistutilsPlatformError(
"User base directory is not specified")
raise DistutilsPlatformError("User base directory is not specified")
self.install_base = self.install_platbase = self.install_userbase
self.select_scheme("unix_user")
self.select_scheme("posix_user")
elif self.home is not None:
self.install_base = self.install_platbase = self.home
self.select_scheme("unix_home")
self.select_scheme("posix_home")
else:
if self.prefix is None:
if self.exec_prefix is not None:
raise DistutilsOptionError(
"must not supply exec-prefix without prefix")
"must not supply exec-prefix without prefix"
)
self.prefix = os.path.normpath(sys.prefix)
self.exec_prefix = os.path.normpath(sys.exec_prefix)
# Allow Fedora to add components to the prefix
_prefix_addition = getattr(sysconfig, '_prefix_addition', "")
self.prefix = os.path.normpath(sys.prefix) + _prefix_addition
self.exec_prefix = os.path.normpath(sys.exec_prefix) + _prefix_addition
else:
if self.exec_prefix is None:
@@ -442,19 +578,18 @@ class install(Command):
self.install_base = self.prefix
self.install_platbase = self.exec_prefix
self.select_scheme("unix_prefix")
self.select_scheme("posix_prefix")
def finalize_other(self):
"""Finalizes options for non-posix platforms"""
if self.user:
if self.install_userbase is None:
raise DistutilsPlatformError(
"User base directory is not specified")
raise DistutilsPlatformError("User base directory is not specified")
self.install_base = self.install_platbase = self.install_userbase
self.select_scheme(os.name + "_user")
elif self.home is not None:
self.install_base = self.install_platbase = self.home
self.select_scheme("unix_home")
self.select_scheme("posix_home")
else:
if self.prefix is None:
self.prefix = os.path.normpath(sys.prefix)
@@ -464,23 +599,11 @@ class install(Command):
self.select_scheme(os.name)
except KeyError:
raise DistutilsPlatformError(
"I don't know how to install stuff on '%s'" % os.name)
"I don't know how to install stuff on '%s'" % os.name
)
def select_scheme(self, name):
"""Sets the install directories by applying the install schemes."""
# it's the caller's problem if they supply a bad name!
if (hasattr(sys, 'pypy_version_info') and
sys.version_info < (3, 8) and
not name.endswith(('_user', '_home'))):
if os.name == 'nt':
name = 'pypy_nt'
else:
name = 'pypy'
scheme = INSTALL_SCHEMES[name]
for key in SCHEME_KEYS:
attrname = 'install_' + key
if getattr(self, attrname) is None:
setattr(self, attrname, scheme[key])
_select_scheme(self, name)
def _expand_attrs(self, attrs):
for attr in attrs:
@@ -498,9 +621,16 @@ class install(Command):
def expand_dirs(self):
"""Calls `os.path.expanduser` on install dirs."""
self._expand_attrs(['install_purelib', 'install_platlib',
'install_lib', 'install_headers',
'install_scripts', 'install_data',])
self._expand_attrs(
[
'install_purelib',
'install_platlib',
'install_lib',
'install_headers',
'install_scripts',
'install_data',
]
)
def convert_paths(self, *names):
"""Call `convert_path` over `names`."""
@@ -527,8 +657,9 @@ class install(Command):
path_file, extra_dirs = self.extra_path
else:
raise DistutilsOptionError(
"'extra_path' option must be a list, tuple, or "
"comma-separated string with 1 or 2 elements")
"'extra_path' option must be a list, tuple, or "
"comma-separated string with 1 or 2 elements"
)
# convert to local form in case Unix notation used (as it
# should be in setup scripts)
@@ -554,7 +685,7 @@ class install(Command):
return
home = convert_path(os.path.expanduser("~"))
for name, path in self.config_vars.items():
if path.startswith(home) and not os.path.isdir(path):
if str(path).startswith(home) and not os.path.isdir(path):
self.debug_print("os.makedirs('%s', 0o700)" % path)
os.makedirs(path, 0o700)
@@ -571,8 +702,7 @@ class install(Command):
# internally, and not to sys.path, so we don't check the platform
# matches what we are running.
if self.warn_dir and build_plat != get_platform():
raise DistutilsPlatformError("Can't install when "
"cross-compiling")
raise DistutilsPlatformError("Can't install when " "cross-compiling")
# Run all sub-commands (at least those that need to be run)
for cmd_name in self.get_sub_commands():
@@ -584,38 +714,43 @@ class install(Command):
# write list of installed files, if requested.
if self.record:
outputs = self.get_outputs()
if self.root: # strip any package prefix
if self.root: # strip any package prefix
root_len = len(self.root)
for counter in range(len(outputs)):
outputs[counter] = outputs[counter][root_len:]
self.execute(write_file,
(self.record, outputs),
"writing list of installed files to '%s'" %
self.record)
self.execute(
write_file,
(self.record, outputs),
"writing list of installed files to '%s'" % self.record,
)
sys_path = map(os.path.normpath, sys.path)
sys_path = map(os.path.normcase, sys_path)
install_lib = os.path.normcase(os.path.normpath(self.install_lib))
if (self.warn_dir and
not (self.path_file and self.install_path_file) and
install_lib not in sys_path):
log.debug(("modules installed to '%s', which is not in "
"Python's module search path (sys.path) -- "
"you'll have to change the search path yourself"),
self.install_lib)
if (
self.warn_dir
and not (self.path_file and self.install_path_file)
and install_lib not in sys_path
):
log.debug(
(
"modules installed to '%s', which is not in "
"Python's module search path (sys.path) -- "
"you'll have to change the search path yourself"
),
self.install_lib,
)
def create_path_file(self):
"""Creates the .pth file"""
filename = os.path.join(self.install_libbase,
self.path_file + ".pth")
filename = os.path.join(self.install_libbase, self.path_file + ".pth")
if self.install_path_file:
self.execute(write_file,
(filename, [self.extra_dirs]),
"creating %s" % filename)
self.execute(
write_file, (filename, [self.extra_dirs]), "creating %s" % filename
)
else:
self.warn("path file '%s' not created" % filename)
# -- Reporting methods ---------------------------------------------
def get_outputs(self):
@@ -630,8 +765,7 @@ class install(Command):
outputs.append(filename)
if self.path_file and self.install_path_file:
outputs.append(os.path.join(self.install_libbase,
self.path_file + ".pth"))
outputs.append(os.path.join(self.install_libbase, self.path_file + ".pth"))
return outputs
@@ -650,8 +784,9 @@ class install(Command):
def has_lib(self):
"""Returns true if the current distribution has any Python
modules to install."""
return (self.distribution.has_pure_modules() or
self.distribution.has_ext_modules())
return (
self.distribution.has_pure_modules() or self.distribution.has_ext_modules()
)
def has_headers(self):
"""Returns true if the current distribution has any headers to
@@ -670,9 +805,10 @@ class install(Command):
# 'sub_commands': a list of commands this command might have to run to
# get its work done. See cmd.py for more info.
sub_commands = [('install_lib', has_lib),
('install_headers', has_headers),
('install_scripts', has_scripts),
('install_data', has_data),
('install_egg_info', lambda self:True),
]
sub_commands = [
('install_lib', has_lib),
('install_headers', has_headers),
('install_scripts', has_scripts),
('install_data', has_data),
('install_egg_info', lambda self: True),
]

View File

@@ -9,18 +9,21 @@ import os
from distutils.core import Command
from distutils.util import change_root, convert_path
class install_data(Command):
description = "install data files"
user_options = [
('install-dir=', 'd',
"base directory for installing data files "
"(default: installation base dir)"),
('root=', None,
"install everything relative to this alternate root directory"),
(
'install-dir=',
'd',
"base directory for installing data files "
"(default: installation base dir)",
),
('root=', None, "install everything relative to this alternate root directory"),
('force', 'f', "force installation (overwrite existing files)"),
]
]
boolean_options = ['force']
@@ -33,11 +36,12 @@ class install_data(Command):
self.warn_dir = 1
def finalize_options(self):
self.set_undefined_options('install',
('install_data', 'install_dir'),
('root', 'root'),
('force', 'force'),
)
self.set_undefined_options(
'install',
('install_data', 'install_dir'),
('root', 'root'),
('force', 'force'),
)
def run(self):
self.mkpath(self.install_dir)
@@ -46,9 +50,10 @@ class install_data(Command):
# it's a simple file, so copy it
f = convert_path(f)
if self.warn_dir:
self.warn("setup script did not provide a directory for "
"'%s' -- installing right in '%s'" %
(f, self.install_dir))
self.warn(
"setup script did not provide a directory for "
"'%s' -- installing right in '%s'" % (f, self.install_dir)
)
(out, _) = self.copy_file(f, self.install_dir)
self.outfiles.append(out)
else:

View File

@@ -1,12 +1,17 @@
"""distutils.command.install_egg_info
"""
distutils.command.install_egg_info
Implements the Distutils 'install_egg_info' command, for installing
a package's PKG-INFO metadata."""
a package's PKG-INFO metadata.
"""
import os
import sys
import re
from distutils.cmd import Command
from distutils import log, dir_util
import os, sys, re
class install_egg_info(Command):
"""Install an .egg-info file for the package"""
@@ -19,14 +24,21 @@ class install_egg_info(Command):
def initialize_options(self):
self.install_dir = None
def finalize_options(self):
self.set_undefined_options('install_lib',('install_dir','install_dir'))
basename = "%s-%s-py%d.%d.egg-info" % (
@property
def basename(self):
"""
Allow basename to be overridden by child class.
Ref pypa/distutils#2.
"""
return "%s-%s-py%d.%d.egg-info" % (
to_filename(safe_name(self.distribution.get_name())),
to_filename(safe_version(self.distribution.get_version())),
*sys.version_info[:2]
*sys.version_info[:2],
)
self.target = os.path.join(self.install_dir, basename)
def finalize_options(self):
self.set_undefined_options('install_lib', ('install_dir', 'install_dir'))
self.target = os.path.join(self.install_dir, self.basename)
self.outputs = [self.target]
def run(self):
@@ -34,10 +46,11 @@ class install_egg_info(Command):
if os.path.isdir(target) and not os.path.islink(target):
dir_util.remove_tree(target, dry_run=self.dry_run)
elif os.path.exists(target):
self.execute(os.unlink,(self.target,),"Removing "+target)
self.execute(os.unlink, (self.target,), "Removing " + target)
elif not os.path.isdir(self.install_dir):
self.execute(os.makedirs, (self.install_dir,),
"Creating "+self.install_dir)
self.execute(
os.makedirs, (self.install_dir,), "Creating " + self.install_dir
)
log.info("Writing %s", target)
if not self.dry_run:
with open(target, 'w', encoding='UTF-8') as f:
@@ -51,6 +64,7 @@ class install_egg_info(Command):
# can be replaced by importing them from pkg_resources once it is included
# in the stdlib.
def safe_name(name):
"""Convert an arbitrary string to a standard distribution name
@@ -65,7 +79,7 @@ def safe_version(version):
Spaces become dots, and all other non-alphanumeric characters become
dashes, with runs of multiple dashes condensed to a single dash.
"""
version = version.replace(' ','.')
version = version.replace(' ', '.')
return re.sub('[^A-Za-z0-9.]+', '-', version)
@@ -74,4 +88,4 @@ def to_filename(name):
Any '-' characters are currently replaced with '_'.
"""
return name.replace('-','_')
return name.replace('-', '_')

View File

@@ -11,11 +11,10 @@ class install_headers(Command):
description = "install C/C++ header files"
user_options = [('install-dir=', 'd',
"directory to install header files to"),
('force', 'f',
"force installation (overwrite existing files)"),
]
user_options = [
('install-dir=', 'd', "directory to install header files to"),
('force', 'f', "force installation (overwrite existing files)"),
]
boolean_options = ['force']
@@ -25,10 +24,9 @@ class install_headers(Command):
self.outfiles = []
def finalize_options(self):
self.set_undefined_options('install',
('install_headers', 'install_dir'),
('force', 'force'))
self.set_undefined_options(
'install', ('install_headers', 'install_dir'), ('force', 'force')
)
def run(self):
headers = self.distribution.headers

View File

@@ -14,6 +14,7 @@ from distutils.errors import DistutilsOptionError
# Extension for Python source files.
PYTHON_SOURCE_EXTENSION = ".py"
class install_lib(Command):
description = "install all Python modules (extensions and pure Python)"
@@ -35,18 +36,21 @@ class install_lib(Command):
user_options = [
('install-dir=', 'd', "directory to install to"),
('build-dir=','b', "build directory (where to install from)"),
('build-dir=', 'b', "build directory (where to install from)"),
('force', 'f', "force installation (overwrite existing files)"),
('compile', 'c', "compile .py to .pyc [default]"),
('no-compile', None, "don't compile .py files"),
('optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
(
'optimize=',
'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
),
('skip-build', None, "skip the build steps"),
]
]
boolean_options = ['force', 'compile', 'skip-build']
negative_opt = {'no-compile' : 'compile'}
negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
# let the 'install' command dictate our installation directory
@@ -61,14 +65,15 @@ class install_lib(Command):
# Get all the information we need to install pure Python modules
# from the umbrella 'install' command -- build (source) directory,
# install (target) directory, and whether to compile .py files.
self.set_undefined_options('install',
('build_lib', 'build_dir'),
('install_lib', 'install_dir'),
('force', 'force'),
('compile', 'compile'),
('optimize', 'optimize'),
('skip_build', 'skip_build'),
)
self.set_undefined_options(
'install',
('build_lib', 'build_dir'),
('install_lib', 'install_dir'),
('force', 'force'),
('compile', 'compile'),
('optimize', 'optimize'),
('skip_build', 'skip_build'),
)
if self.compile is None:
self.compile = True
@@ -110,8 +115,9 @@ class install_lib(Command):
if os.path.isdir(self.build_dir):
outfiles = self.copy_tree(self.build_dir, self.install_dir)
else:
self.warn("'%s' does not exist -- no Python modules to install" %
self.build_dir)
self.warn(
"'%s' does not exist -- no Python modules to install" % self.build_dir
)
return
return outfiles
@@ -129,14 +135,22 @@ class install_lib(Command):
install_root = self.get_finalized_command('install').root
if self.compile:
byte_compile(files, optimize=0,
force=self.force, prefix=install_root,
dry_run=self.dry_run)
byte_compile(
files,
optimize=0,
force=self.force,
prefix=install_root,
dry_run=self.dry_run,
)
if self.optimize > 0:
byte_compile(files, optimize=self.optimize,
force=self.force, prefix=install_root,
verbose=self.verbose, dry_run=self.dry_run)
byte_compile(
files,
optimize=self.optimize,
force=self.force,
prefix=install_root,
verbose=self.verbose,
dry_run=self.dry_run,
)
# -- Utility methods -----------------------------------------------
@@ -165,15 +179,18 @@ class install_lib(Command):
if ext != PYTHON_SOURCE_EXTENSION:
continue
if self.compile:
bytecode_files.append(importlib.util.cache_from_source(
py_file, optimization=''))
bytecode_files.append(
importlib.util.cache_from_source(py_file, optimization='')
)
if self.optimize > 0:
bytecode_files.append(importlib.util.cache_from_source(
py_file, optimization=self.optimize))
bytecode_files.append(
importlib.util.cache_from_source(
py_file, optimization=self.optimize
)
)
return bytecode_files
# -- External interface --------------------------------------------
# (called by outsiders)
@@ -182,19 +199,23 @@ class install_lib(Command):
were actually run. Not affected by the "dry-run" flag or whether
modules have actually been built yet.
"""
pure_outputs = \
self._mutate_outputs(self.distribution.has_pure_modules(),
'build_py', 'build_lib',
self.install_dir)
pure_outputs = self._mutate_outputs(
self.distribution.has_pure_modules(),
'build_py',
'build_lib',
self.install_dir,
)
if self.compile:
bytecode_outputs = self._bytecode_filenames(pure_outputs)
else:
bytecode_outputs = []
ext_outputs = \
self._mutate_outputs(self.distribution.has_ext_modules(),
'build_ext', 'build_lib',
self.install_dir)
ext_outputs = self._mutate_outputs(
self.distribution.has_ext_modules(),
'build_ext',
'build_lib',
self.install_dir,
)
return pure_outputs + bytecode_outputs + ext_outputs

View File

@@ -17,7 +17,7 @@ class install_scripts(Command):
user_options = [
('install-dir=', 'd', "directory to install scripts to"),
('build-dir=','b', "build directory (where to install from)"),
('build-dir=', 'b', "build directory (where to install from)"),
('force', 'f', "force installation (overwrite existing files)"),
('skip-build', None, "skip the build steps"),
]
@@ -32,11 +32,12 @@ class install_scripts(Command):
def finalize_options(self):
self.set_undefined_options('build', ('build_scripts', 'build_dir'))
self.set_undefined_options('install',
('install_scripts', 'install_dir'),
('force', 'force'),
('skip_build', 'skip_build'),
)
self.set_undefined_options(
'install',
('install_scripts', 'install_dir'),
('force', 'force'),
('skip_build', 'skip_build'),
)
def run(self):
if not self.skip_build:

View File

@@ -7,12 +7,13 @@ def _pythonlib_compat():
library. See pypa/distutils#9.
"""
from distutils import sysconfig
if not sysconfig.get_config_var('Py_ENABLED_SHARED'):
return
yield 'python{}.{}{}'.format(
sys.hexversion >> 24,
(sys.hexversion >> 16) & 0xff,
(sys.hexversion >> 16) & 0xFF,
sysconfig.get_config_var('ABIFLAGS'),
)

View File

@@ -7,24 +7,30 @@ Implements the Distutils 'register' command (register with the repository).
import getpass
import io
import urllib.parse, urllib.request
import urllib.parse
import urllib.request
from warnings import warn
from distutils.core import PyPIRCCommand
from distutils.errors import *
from distutils import log
class register(PyPIRCCommand):
description = ("register the distribution with the Python package index")
description = "register the distribution with the Python package index"
user_options = PyPIRCCommand.user_options + [
('list-classifiers', None,
'list the valid Trove classifiers'),
('strict', None ,
'Will stop the registering if the meta-data are not fully compliant')
]
('list-classifiers', None, 'list the valid Trove classifiers'),
(
'strict',
None,
'Will stop the registering if the meta-data are not fully compliant',
),
]
boolean_options = PyPIRCCommand.boolean_options + [
'verify', 'list-classifiers', 'strict']
'verify',
'list-classifiers',
'strict',
]
sub_commands = [('check', lambda self: True)]
@@ -36,8 +42,10 @@ class register(PyPIRCCommand):
def finalize_options(self):
PyPIRCCommand.finalize_options(self)
# setting options for the `check` subcommand
check_options = {'strict': ('register', self.strict),
'restructuredtext': ('register', 1)}
check_options = {
'strict': ('register', self.strict),
'restructuredtext': ('register', 1),
}
self.distribution.command_options['check'] = check_options
def run(self):
@@ -57,8 +65,11 @@ class register(PyPIRCCommand):
def check_metadata(self):
"""Deprecated API."""
warn("distutils.command.register.check_metadata is deprecated, \
use the check command instead", PendingDeprecationWarning)
warn(
"distutils.command.register.check_metadata is deprecated; "
"use the check command instead",
DeprecationWarning,
)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
check.strict = self.strict
@@ -66,8 +77,7 @@ class register(PyPIRCCommand):
check.run()
def _set_config(self):
''' Reads the configuration file and set attributes.
'''
'''Reads the configuration file and set attributes.'''
config = self._read_pypirc()
if config != {}:
self.username = config['username']
@@ -83,45 +93,43 @@ class register(PyPIRCCommand):
self.has_config = False
def classifiers(self):
''' Fetch the list of classifiers from the server.
'''
url = self.repository+'?:action=list_classifiers'
'''Fetch the list of classifiers from the server.'''
url = self.repository + '?:action=list_classifiers'
response = urllib.request.urlopen(url)
log.info(self._read_pypi_response(response))
def verify_metadata(self):
''' Send the metadata to the package index server to be checked.
'''
'''Send the metadata to the package index server to be checked.'''
# send the info to the server and report the result
(code, result) = self.post_to_server(self.build_post_data('verify'))
log.info('Server response (%s): %s', code, result)
def send_metadata(self):
''' Send the metadata to the package index server.
def send_metadata(self): # noqa: C901
'''Send the metadata to the package index server.
Well, do the following:
1. figure who the user is, and then
2. send the data as a Basic auth'ed POST.
Well, do the following:
1. figure who the user is, and then
2. send the data as a Basic auth'ed POST.
First we try to read the username/password from $HOME/.pypirc,
which is a ConfigParser-formatted file with a section
[distutils] containing username and password entries (both
in clear text). Eg:
First we try to read the username/password from $HOME/.pypirc,
which is a ConfigParser-formatted file with a section
[distutils] containing username and password entries (both
in clear text). Eg:
[distutils]
index-servers =
pypi
[distutils]
index-servers =
pypi
[pypi]
username: fred
password: sekrit
[pypi]
username: fred
password: sekrit
Otherwise, to figure who the user is, we offer the user three
choices:
Otherwise, to figure who the user is, we offer the user three
choices:
1. use existing login,
2. register as a new user, or
3. set the password to a random string and email the user.
1. use existing login,
2. register as a new user, or
3. set the password to a random string and email the user.
'''
# see if we can short-cut and get the username/password from the
@@ -137,13 +145,16 @@ class register(PyPIRCCommand):
# get the user's login info
choices = '1 2 3 4'.split()
while choice not in choices:
self.announce('''\
self.announce(
'''\
We need to know who you are, so please choose either:
1. use your existing login,
2. register as a new user,
3. have the server generate a new password for you (and email it to you), or
4. quit
Your selection [default 1]: ''', log.INFO)
Your selection [default 1]: ''',
log.INFO,
)
choice = input()
if not choice:
choice = '1'
@@ -162,10 +173,8 @@ Your selection [default 1]: ''', log.INFO)
host = urllib.parse.urlparse(self.repository)[1]
auth.add_password(self.realm, host, username, password)
# send the info to the server and report the result
code, result = self.post_to_server(self.build_post_data('submit'),
auth)
self.announce('Server response (%s): %s' % (code, result),
log.INFO)
code, result = self.post_to_server(self.build_post_data('submit'), auth)
self.announce('Server response ({}): {}'.format(code, result), log.INFO)
# possibly save the login
if code == 200:
@@ -174,10 +183,17 @@ Your selection [default 1]: ''', log.INFO)
# so the upload command can reuse it
self.distribution.password = password
else:
self.announce(('I can store your PyPI login so future '
'submissions will be faster.'), log.INFO)
self.announce('(the login will be stored in %s)' % \
self._get_rc_file(), log.INFO)
self.announce(
(
'I can store your PyPI login so future '
'submissions will be faster.'
),
log.INFO,
)
self.announce(
'(the login will be stored in %s)' % self._get_rc_file(),
log.INFO,
)
choice = 'X'
while choice.lower() not in 'yn':
choice = input('Save your login (y/N)?')
@@ -208,8 +224,7 @@ Your selection [default 1]: ''', log.INFO)
log.info('Server response (%s): %s', code, result)
else:
log.info('You will receive an email shortly.')
log.info(('Follow the instructions in it to '
'complete registration.'))
log.info('Follow the instructions in it to ' 'complete registration.')
elif choice == '3':
data = {':action': 'password_reset'}
data['email'] = ''
@@ -224,7 +239,7 @@ Your selection [default 1]: ''', log.INFO)
meta = self.distribution.metadata
data = {
':action': action,
'metadata_version' : '1.0',
'metadata_version': '1.0',
'name': meta.get_name(),
'version': meta.get_version(),
'summary': meta.get_description(),
@@ -246,13 +261,12 @@ Your selection [default 1]: ''', log.INFO)
data['metadata_version'] = '1.1'
return data
def post_to_server(self, data, auth=None):
''' Post a query to the server, and return a string response.
'''
def post_to_server(self, data, auth=None): # noqa: C901
'''Post a query to the server, and return a string response.'''
if 'name' in data:
self.announce('Registering %s to %s' % (data['name'],
self.repository),
log.INFO)
self.announce(
'Registering {} to {}'.format(data['name'], self.repository), log.INFO
)
# Build up the MIME payload for the urllib2 POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = '\n--' + boundary
@@ -260,12 +274,12 @@ Your selection [default 1]: ''', log.INFO)
body = io.StringIO()
for key, value in data.items():
# handle multiple entries for the same name
if type(value) not in (type([]), type( () )):
if type(value) not in (type([]), type(())):
value = [value]
for value in value:
value = str(value)
body.write(sep_boundary)
body.write('\nContent-Disposition: form-data; name="%s"'%key)
body.write('\nContent-Disposition: form-data; name="%s"' % key)
body.write("\n\n")
body.write(value)
if value and value[-1] == '\r':
@@ -276,8 +290,9 @@ Your selection [default 1]: ''', log.INFO)
# build the Request
headers = {
'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary,
'Content-length': str(len(body))
'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'
% boundary,
'Content-length': str(len(body)),
}
req = urllib.request.Request(self.repository, body, headers)

View File

@@ -15,7 +15,7 @@ from distutils.text_file import TextFile
from distutils.filelist import FileList
from distutils import log
from distutils.util import convert_path
from distutils.errors import DistutilsTemplateError, DistutilsOptionError
from distutils.errors import DistutilsOptionError, DistutilsTemplateError
def show_formats():
@@ -24,13 +24,12 @@ def show_formats():
"""
from distutils.fancy_getopt import FancyGetopt
from distutils.archive_util import ARCHIVE_FORMATS
formats = []
for format in ARCHIVE_FORMATS.keys():
formats.append(("formats=" + format, None,
ARCHIVE_FORMATS[format][2]))
formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2]))
formats.sort()
FancyGetopt(formats).print_help(
"List of available source distribution formats:")
FancyGetopt(formats).print_help("List of available source distribution formats:")
class sdist(Command):
@@ -44,55 +43,77 @@ class sdist(Command):
return self.metadata_check
user_options = [
('template=', 't',
"name of manifest template file [default: MANIFEST.in]"),
('manifest=', 'm',
"name of manifest file [default: MANIFEST]"),
('use-defaults', None,
"include the default file set in the manifest "
"[default; disable with --no-defaults]"),
('no-defaults', None,
"don't include the default file set"),
('prune', None,
"specifically exclude files/directories that should not be "
"distributed (build tree, RCS/CVS dirs, etc.) "
"[default; disable with --no-prune]"),
('no-prune', None,
"don't automatically exclude anything"),
('manifest-only', 'o',
"just regenerate the manifest and then stop "
"(implies --force-manifest)"),
('force-manifest', 'f',
"forcibly regenerate the manifest and carry on as usual. "
"Deprecated: now the manifest is always regenerated."),
('formats=', None,
"formats for source distribution (comma-separated list)"),
('keep-temp', 'k',
"keep the distribution tree around after creating " +
"archive file(s)"),
('dist-dir=', 'd',
"directory to put the source distribution archive(s) in "
"[default: dist]"),
('metadata-check', None,
"Ensure that all required elements of meta-data "
"are supplied. Warn if any missing. [default]"),
('owner=', 'u',
"Owner name used when creating a tar file [default: current user]"),
('group=', 'g',
"Group name used when creating a tar file [default: current group]"),
]
('template=', 't', "name of manifest template file [default: MANIFEST.in]"),
('manifest=', 'm', "name of manifest file [default: MANIFEST]"),
(
'use-defaults',
None,
"include the default file set in the manifest "
"[default; disable with --no-defaults]",
),
('no-defaults', None, "don't include the default file set"),
(
'prune',
None,
"specifically exclude files/directories that should not be "
"distributed (build tree, RCS/CVS dirs, etc.) "
"[default; disable with --no-prune]",
),
('no-prune', None, "don't automatically exclude anything"),
(
'manifest-only',
'o',
"just regenerate the manifest and then stop " "(implies --force-manifest)",
),
(
'force-manifest',
'f',
"forcibly regenerate the manifest and carry on as usual. "
"Deprecated: now the manifest is always regenerated.",
),
('formats=', None, "formats for source distribution (comma-separated list)"),
(
'keep-temp',
'k',
"keep the distribution tree around after creating " + "archive file(s)",
),
(
'dist-dir=',
'd',
"directory to put the source distribution archive(s) in " "[default: dist]",
),
(
'metadata-check',
None,
"Ensure that all required elements of meta-data "
"are supplied. Warn if any missing. [default]",
),
(
'owner=',
'u',
"Owner name used when creating a tar file [default: current user]",
),
(
'group=',
'g',
"Group name used when creating a tar file [default: current group]",
),
]
boolean_options = ['use-defaults', 'prune',
'manifest-only', 'force-manifest',
'keep-temp', 'metadata-check']
boolean_options = [
'use-defaults',
'prune',
'manifest-only',
'force-manifest',
'keep-temp',
'metadata-check',
]
help_options = [
('help-formats', None,
"list available distribution formats", show_formats),
]
('help-formats', None, "list available distribution formats", show_formats),
]
negative_opt = {'no-defaults': 'use-defaults',
'no-prune': 'prune' }
negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune'}
sub_commands = [('check', checking_metadata)]
@@ -131,8 +152,7 @@ class sdist(Command):
bad_format = archive_util.check_archive_formats(self.formats)
if bad_format:
raise DistutilsOptionError(
"unknown archive format '%s'" % bad_format)
raise DistutilsOptionError("unknown archive format '%s'" % bad_format)
if self.dist_dir is None:
self.dist_dir = "dist"
@@ -161,8 +181,11 @@ class sdist(Command):
def check_metadata(self):
"""Deprecated API."""
warn("distutils.command.sdist.check_metadata is deprecated, \
use the check command instead", PendingDeprecationWarning)
warn(
"distutils.command.sdist.check_metadata is deprecated, \
use the check command instead",
PendingDeprecationWarning,
)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
check.run()
@@ -189,9 +212,10 @@ class sdist(Command):
return
if not template_exists:
self.warn(("manifest template '%s' does not exist " +
"(using default file list)") %
self.template)
self.warn(
("manifest template '%s' does not exist " + "(using default file list)")
% self.template
)
self.filelist.findall()
if self.use_defaults:
@@ -259,8 +283,9 @@ class sdist(Command):
break
if not got_it:
self.warn("standard file not found: should have one of " +
', '.join(alts))
self.warn(
"standard file not found: should have one of " + ', '.join(alts)
)
else:
if self._cs_path_exists(fn):
self.filelist.append(fn)
@@ -328,14 +353,20 @@ class sdist(Command):
'self.filelist', which updates itself accordingly.
"""
log.info("reading manifest template '%s'", self.template)
template = TextFile(self.template, strip_comments=1, skip_blanks=1,
join_lines=1, lstrip_ws=1, rstrip_ws=1,
collapse_join=1)
template = TextFile(
self.template,
strip_comments=1,
skip_blanks=1,
join_lines=1,
lstrip_ws=1,
rstrip_ws=1,
collapse_join=1,
)
try:
while True:
line = template.readline()
if line is None: # end of file
if line is None: # end of file
break
try:
@@ -344,9 +375,10 @@ class sdist(Command):
# malformed lines, or a ValueError from the lower-level
# convert_path function
except (DistutilsTemplateError, ValueError) as msg:
self.warn("%s, line %d: %s" % (template.filename,
template.current_line,
msg))
self.warn(
"%s, line %d: %s"
% (template.filename, template.current_line, msg)
)
finally:
template.close()
@@ -369,9 +401,8 @@ class sdist(Command):
else:
seps = '/'
vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr',
'_darcs']
vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps)
vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', '_darcs']
vcs_ptrn = r'(^|{})({})({}).*'.format(seps, '|'.join(vcs_dirs), seps)
self.filelist.exclude_pattern(vcs_ptrn, is_regex=1)
def write_manifest(self):
@@ -380,14 +411,19 @@ class sdist(Command):
named by 'self.manifest'.
"""
if self._manifest_is_not_generated():
log.info("not writing to manually maintained "
"manifest file '%s'" % self.manifest)
log.info(
"not writing to manually maintained "
"manifest file '%s'" % self.manifest
)
return
content = self.filelist.files[:]
content.insert(0, '# file GENERATED by distutils, do NOT edit')
self.execute(file_util.write_file, (self.manifest, content),
"writing manifest file '%s'" % self.manifest)
self.execute(
file_util.write_file,
(self.manifest, content),
"writing manifest file '%s'" % self.manifest,
)
def _manifest_is_not_generated(self):
# check for special comment used in 3.1.3 and higher
@@ -437,10 +473,10 @@ class sdist(Command):
# out-of-date, because by default we blow away 'base_dir' when
# we're done making the distribution archives.)
if hasattr(os, 'link'): # can make hard links on this system
if hasattr(os, 'link'): # can make hard links on this system
link = 'hard'
msg = "making hard links in %s..." % base_dir
else: # nope, have to copy
else: # nope, have to copy
link = None
msg = "copying files to %s..." % base_dir
@@ -471,14 +507,15 @@ class sdist(Command):
base_name = os.path.join(self.dist_dir, base_dir)
self.make_release_tree(base_dir, self.filelist.files)
archive_files = [] # remember names of files we create
archive_files = [] # remember names of files we create
# tar archive must be created last to avoid overwrite and remove
if 'tar' in self.formats:
self.formats.append(self.formats.pop(self.formats.index('tar')))
for fmt in self.formats:
file = self.make_archive(base_name, fmt, base_dir=base_dir,
owner=self.owner, group=self.group)
file = self.make_archive(
base_name, fmt, base_dir=base_dir, owner=self.owner, group=self.group
)
archive_files.append(file)
self.distribution.dist_files.append(('sdist', '', file))

View File

@@ -31,10 +31,9 @@ class upload(PyPIRCCommand):
description = "upload binary package to PyPI"
user_options = PyPIRCCommand.user_options + [
('sign', 's',
'sign files to upload using gpg'),
('sign', 's', 'sign files to upload using gpg'),
('identity=', 'i', 'GPG identity used to sign files'),
]
]
boolean_options = PyPIRCCommand.boolean_options + ['sign']
@@ -49,9 +48,7 @@ class upload(PyPIRCCommand):
def finalize_options(self):
PyPIRCCommand.finalize_options(self)
if self.identity and not self.sign:
raise DistutilsOptionError(
"Must use --sign for --identity to have meaning"
)
raise DistutilsOptionError("Must use --sign for --identity to have meaning")
config = self._read_pypirc()
if config != {}:
self.username = config['username']
@@ -66,16 +63,17 @@ class upload(PyPIRCCommand):
def run(self):
if not self.distribution.dist_files:
msg = ("Must create and upload files in one command "
"(e.g. setup.py sdist upload)")
msg = (
"Must create and upload files in one command "
"(e.g. setup.py sdist upload)"
)
raise DistutilsOptionError(msg)
for command, pyversion, filename in self.distribution.dist_files:
self.upload_file(command, pyversion, filename)
def upload_file(self, command, pyversion, filename):
def upload_file(self, command, pyversion, filename): # noqa: C901
# Makes sure the repository URL is compliant
schema, netloc, url, params, query, fragments = \
urlparse(self.repository)
schema, netloc, url, params, query, fragments = urlparse(self.repository)
if params or query or fragments:
raise AssertionError("Incompatible url %s" % self.repository)
@@ -87,12 +85,11 @@ class upload(PyPIRCCommand):
gpg_args = ["gpg", "--detach-sign", "-a", filename]
if self.identity:
gpg_args[2:2] = ["--local-user", self.identity]
spawn(gpg_args,
dry_run=self.dry_run)
spawn(gpg_args, dry_run=self.dry_run)
# Fill in the data - send all the meta-data in case we need to
# register a new release
f = open(filename,'rb')
f = open(filename, 'rb')
try:
content = f.read()
finally:
@@ -103,16 +100,13 @@ class upload(PyPIRCCommand):
# action
':action': 'file_upload',
'protocol_version': '1',
# identify release
'name': meta.get_name(),
'version': meta.get_version(),
# file content
'content': (os.path.basename(filename),content),
'content': (os.path.basename(filename), content),
'filetype': command,
'pyversion': pyversion,
# additional meta-data
'metadata_version': '1.0',
'summary': meta.get_description(),
@@ -129,7 +123,7 @@ class upload(PyPIRCCommand):
'provides': meta.get_provides(),
'requires': meta.get_requires(),
'obsoletes': meta.get_obsoletes(),
}
}
data['comment'] = ''
@@ -145,8 +139,7 @@ class upload(PyPIRCCommand):
if self.sign:
with open(filename + ".asc", "rb") as f:
data['gpg_signature'] = (os.path.basename(filename) + ".asc",
f.read())
data['gpg_signature'] = (os.path.basename(filename) + ".asc", f.read())
# set up the authentication
user_pass = (self.username + ":" + self.password).encode('ascii')
@@ -177,7 +170,7 @@ class upload(PyPIRCCommand):
body.write(end_boundary)
body = body.getvalue()
msg = "Submitting %s to %s" % (filename, self.repository)
msg = "Submitting {} to {}".format(filename, self.repository)
self.announce(msg, log.INFO)
# build the Request
@@ -187,8 +180,7 @@ class upload(PyPIRCCommand):
'Authorization': auth,
}
request = Request(self.repository, data=body,
headers=headers)
request = Request(self.repository, data=body, headers=headers)
# send the data
try:
result = urlopen(request)
@@ -202,13 +194,12 @@ class upload(PyPIRCCommand):
raise
if status == 200:
self.announce('Server response (%s): %s' % (status, reason),
log.INFO)
self.announce('Server response ({}): {}'.format(status, reason), log.INFO)
if self.show_response:
text = self._read_pypi_response(result)
msg = '\n'.join(('-' * 75, text, '-' * 75))
self.announce(msg, log.INFO)
else:
msg = 'Upload failed (%s): %s' % (status, reason)
msg = 'Upload failed ({}): {}'.format(status, reason)
self.announce(msg, log.ERROR)
raise DistutilsError(msg)