summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-12-19 18:51:35 +0100
committerVictor Stinner <victor.stinner@gmail.com>2014-12-19 18:51:35 +0100
commitf1a0f0aa574bbe317b37e245c9d1ddcc2c0f8e36 (patch)
treefed6d45487f05794a88c5371a6e4105760ef9745
parent2d35f973943192e9350120210212ee9c8700bc2d (diff)
parent6d44dfe5984166cd621a39432bb283d82f9dde47 (diff)
downloadtrollius-f1a0f0aa574bbe317b37e245c9d1ddcc2c0f8e36.tar.gz
Merge Tulip into Trollius
-rw-r--r--release.py252
1 files changed, 196 insertions, 56 deletions
diff --git a/release.py b/release.py
index 630458b..8525a2b 100644
--- a/release.py
+++ b/release.py
@@ -4,12 +4,16 @@ Script to upload 32 bits and 64 bits wheel packages for Python 3.3 on Windows.
Usage: "python release.py HG_TAG" where HG_TAG is a Mercurial tag, usually
a version number like "3.4.2".
-Modify manually the dry_run attribute to upload files.
+Requirements:
-It requires the Windows SDK 7.1 on Windows 64 bits and the aiotest module.
+- Python 3.3 and newer requires the Windows SDK 7.1 to build wheel packages
+- Python 2.7 requires the Windows SDK 7.0
+- the aiotest module is required to run aiotest tests
"""
import contextlib
+import optparse
import os
+import platform
import re
import shutil
import subprocess
@@ -42,6 +46,16 @@ class PythonVersion:
self.bits = bits
self._executable = None
+ @staticmethod
+ def running():
+ arch = platform.architecture()[0]
+ bits = int(arch[:2])
+ pyver = PythonVersion(sys.version_info.major,
+ sys.version_info.minor,
+ bits)
+ pyver._executable = sys.executable
+ return pyver
+
def get_executable(self, app):
if self._executable:
return self._executable
@@ -80,12 +94,15 @@ class Release(object):
root = os.path.dirname(__file__)
self.root = os.path.realpath(root)
# Set these attributes to True to run also register sdist upload
+ self.wheel = False
+ self.test = False
self.register = False
self.sdist = False
- self.dry_run = True
- self.test = True
self.aiotest = False
self.verbose = False
+ self.upload = False
+ # Release mode: enable more tests
+ self.release = False
self.python_versions = [
PythonVersion(pyver[0], pyver[1], bits)
for pyver, bits in PYTHON_VERSIONS]
@@ -108,6 +125,14 @@ class Release(object):
stdout, stderr = proc.communicate()
return proc.returncode, stdout
+ def check_output(self, *args, **kw):
+ exitcode, output = self.get_output(*args, **kw)
+ if exitcode:
+ sys.stdout.write(output)
+ sys.stdout.flush()
+ sys.exit(1)
+ return output
+
def run_command(self, *args, **kw):
with self._popen(args, **kw) as proc:
exitcode = proc.wait()
@@ -115,7 +140,7 @@ class Release(object):
sys.exit(exitcode)
def get_local_changes(self):
- exitcode, status = self.get_output(HG, 'status')
+ status = self.check_output(HG, 'status')
return [line for line in status.splitlines()
if not line.startswith("?")]
@@ -135,13 +160,15 @@ class Release(object):
def windows_sdk_setenv(self, pyver):
if (pyver.major, pyver.minor) >= (3, 3):
- sdkver = "v7.1"
+ path = "v7.1"
+ sdkver = (7, 1)
else:
- sdkver = "v7.0"
- setenv = os.path.join(SDK_ROOT, sdkver, 'Bin', 'SetEnv.cmd')
+ path = "v7.0"
+ sdkver = (7, 0)
+ setenv = os.path.join(SDK_ROOT, path, 'Bin', 'SetEnv.cmd')
if not os.path.exists(setenv):
- print("Unable to find Windows SDK %s for %s"
- % (sdkver, pyver))
+ print("Unable to find Windows SDK %s.%s for %s"
+ % (sdkver[0], sdkver[1], pyver))
print("Please download and install it")
print("%s does not exists" % setenv)
sys.exit(1)
@@ -149,7 +176,8 @@ class Release(object):
arch = '/x64'
else:
arch = '/x86'
- return ["CALL", setenv, "/release", arch]
+ cmd = ["CALL", setenv, "/release", arch]
+ return (cmd, sdkver)
def quote(self, arg):
if not re.search("[ '\"]", arg):
@@ -172,11 +200,7 @@ class Release(object):
self.cleanup()
self.run_command(sys.executable, 'setup.py', 'sdist', 'upload')
- def runtests(self, pyver):
- print("Run tests on %s" % pyver)
-
- python = pyver.get_executable(self)
-
+ def build_inplace(self, pyver):
print("Build _overlapped.pyd for %s" % pyver)
self.build(pyver, 'build')
if pyver.bits == 64:
@@ -188,23 +212,34 @@ class Release(object):
dst = os.path.join(self.root, PROJECT, '_overlapped.pyd')
shutil.copyfile(src, dst)
+ def runtests(self, pyver):
+ print("Run tests on %s" % pyver)
+
+ if not self.options.no_compile:
+ self.build_inplace(pyver)
+
release_env = dict(os.environ)
release_env.pop(DEBUG_ENV_VAR, None)
dbg_env = dict(os.environ)
dbg_env[DEBUG_ENV_VAR] = '1'
+ python = pyver.get_executable(self)
args = (python, 'runtests.py', '-r')
- print("Run runtests.py in release mode on %s" % pyver)
- self.run_command(*args, env=release_env)
+
+ if self.release:
+ print("Run runtests.py in release mode on %s" % pyver)
+ self.run_command(*args, env=release_env)
print("Run runtests.py in debug mode on %s" % pyver)
self.run_command(*args, env=dbg_env)
if self.aiotest:
args = (python, 'run_aiotest.py')
- print("Run aiotest in release mode on %s" % pyver)
- self.run_command(*args, env=release_env)
+
+ if self.release:
+ print("Run aiotest in release mode on %s" % pyver)
+ self.run_command(*args, env=release_env)
print("Run aiotest in debug mode on %s" % pyver)
self.run_command(*args, env=dbg_env)
@@ -213,7 +248,7 @@ class Release(object):
def build(self, pyver, *cmds):
self.cleanup()
- setenv = self.windows_sdk_setenv(pyver)
+ setenv, sdkver = self.windows_sdk_setenv(pyver)
python = pyver.get_executable(self)
@@ -234,15 +269,14 @@ class Release(object):
try:
if self.verbose:
- print("Setup Windows SDK")
+ print("Setup Windows SDK %s.%s" % sdkver)
print("+ " + ' '.join(cmd))
- if self.verbose:
+ # SDK 7.1 uses the COLOR command which makes SetEnv.cmd failing
+ # if the stdout is not a TTY (if we redirect stdout into a file)
+ if self.verbose or sdkver >= (7, 1):
self.run_command(temp.name, verbose=False)
else:
- exitcode, stdout = self.get_output(temp.name, verbose=False)
- if exitcode:
- sys.stdout.write(stdout)
- sys.stdout.flush()
+ self.check_output(temp.name, verbose=False)
finally:
os.unlink(temp.name)
@@ -253,22 +287,114 @@ class Release(object):
def publish_wheel(self, pyver):
self.build(pyver, 'bdist_wheel', 'upload')
- def main(self):
- try:
- pos = sys.argv[1:].index('--ignore')
- except ValueError:
- ignore = False
+ def parse_options(self):
+ parser = optparse.OptionParser(
+ description="Run all unittests.",
+ usage="%prog [options] command")
+ parser.add_option(
+ '-v', '--verbose', action="store_true", dest='verbose',
+ default=0, help='verbose')
+ parser.add_option(
+ '-t', '--tag', type="str",
+ help='Mercurial tag or revision, required to release')
+ parser.add_option(
+ '-p', '--python', type="str",
+ help='Only build/test one specific Python version, ex: "2.7:32"')
+ parser.add_option(
+ '-C', "--no-compile", action="store_true",
+ help="Don't compile the module, this options implies --running",
+ default=False)
+ parser.add_option(
+ '-r', "--running", action="store_true",
+ help='Only use the running Python version',
+ default=False)
+ parser.add_option(
+ '--ignore', action="store_true",
+ help='Ignore local changes',
+ default=False)
+ self.options, args = parser.parse_args()
+ if len(args) == 1:
+ command = args[0]
+ else:
+ command = None
+
+ if self.options.no_compile:
+ self.options.running = True
+
+ if command == 'clean':
+ self.options.verbose = True
+ elif command == 'build':
+ self.options.running = True
+ elif command == 'test_wheel':
+ self.wheel = True
+ elif command == 'test':
+ self.test = True
+ elif command == 'release':
+ if not self.options.tag:
+ print("The release command requires the --tag option")
+ sys.exit(1)
+
+ self.release = True
+ self.wheel = True
+ self.test = True
+ #self.upload = True
else:
- ignore = True
- del sys.argv[1+pos]
- if len(sys.argv) != 2:
- print("usage: %s hg_tag" % sys.argv[0])
+ if command:
+ print("Invalid command: %s" % command)
+ else:
+ parser.print_usage()
+
+ print("Available commands:")
+ print("- build: build asyncio in place, imply --running")
+ print("- test: run tests")
+ print("- test_wheel: test building wheel packages")
+ print("- release: run tests and publish wheel packages,")
+ print(" require the --tag option")
+ print("- clean: cleanup the project")
sys.exit(1)
+ if self.options.python and self.options.running:
+ print("--python and --running options are exclusive")
+ sys.exit(1)
+
+ python = self.options.python
+ if python:
+ match = re.match("^([23])\.([0-9])/(32|64)$", python)
+ if not match:
+ print("Invalid Python version: %s" % python)
+ print('Format of a Python version: "x.y/bits"')
+ print("Example: 2.7/32")
+ sys.exit(1)
+ major = int(match.group(1))
+ minor = int(match.group(2))
+ bits = int(match.group(3))
+ self.python_versions = [PythonVersion(major, minor, bits)]
+
+ if self.options.running:
+ self.python_versions = [PythonVersion.running()]
+
+ self.verbose = self.options.verbose
+ self.command = command
+
+ def main(self):
+ self.parse_options()
+
print("Directory: %s" % self.root)
os.chdir(self.root)
- if not ignore:
+ if self.command == "clean":
+ self.cleanup()
+ sys.exit(1)
+
+ if self.command == "build":
+ if len(self.python_versions) != 1:
+ print("build command requires one specific Python version")
+ print("Use the --python command line option")
+ sys.exit(1)
+ pyver = self.python_versions[0]
+ self.build_inplace(pyver)
+
+ if (self.register or self.upload) and (not self.options.ignore):
lines = self.get_local_changes()
else:
lines = ()
@@ -281,41 +407,55 @@ class Release(object):
print("or use the --ignore command line option")
sys.exit(1)
- hg_tag = sys.argv[1]
- print("Update repository to revision %s" % hg_tag)
- exitcode, output = self.get_output(HG, 'update', hg_tag)
- if exitcode:
- sys.stdout.write(output)
- sys.stdout.flush()
- sys.exit(exitcode)
+ hg_tag = self.options.tag
+ if hg_tag:
+ print("Update repository to revision %s" % hg_tag)
+ self.check_output(HG, 'update', hg_tag)
+
+ hg_rev = self.check_output(HG, 'id').rstrip()
+
+ if self.wheel:
+ for pyver in self.python_versions:
+ self.test_wheel(pyver)
if self.test:
for pyver in self.python_versions:
self.runtests(pyver)
- for pyver in self.python_versions:
- self.test_wheel(pyver)
-
- if self.dry_run:
- sys.exit(0)
-
if self.register:
self.run_command(sys.executable, 'setup.py', 'register')
if self.sdist:
self.sdist_upload()
- for pyver in self.python_versions:
- self.publish_wheel(pyver)
+ if self.upload:
+ for pyver in self.python_versions:
+ self.publish_wheel(pyver)
+
+ hg_rev2 = self.check_output(HG, 'id').rstrip()
+ if hg_rev != hg_rev2:
+ print("ERROR: The Mercurial revision changed")
+ print("Before: %s" % hg_rev)
+ print("After: %s" % hg_rev2)
+ sys.exit(1)
print("")
+ print("Mercurial revision: %s" % hg_rev)
+ if self.command == 'build':
+ print("Inplace compilation done")
+ if self.wheel:
+ print("Compilation of wheel packages succeeded")
+ if self.test:
+ print("Tests succeeded")
if self.register:
- print("Publish version %s" % hg_tag)
- print("Uploaded:")
+ print("Project registered on the Python cheeseshop (PyPI)")
if self.sdist:
- print("- sdist")
+ print("Project source code uploaded to the Python cheeseshop (PyPI)")
+ if self.upload:
+ print("Wheel packages uploaded to the Python cheeseshop (PyPI)")
for pyver in self.python_versions:
- print("- Windows wheel package for %s" % pyver)
+ print("- %s" % pyver)
+
if __name__ == "__main__":
Release().main()