Enhance release.py

* Only get the python executable once per python version
* Much lesser verbose output: by default, hide all logs when building trollius
This commit is contained in:
Victor Stinner
2014-12-19 17:07:56 +01:00
parent 023fdeb8d3
commit 692066b2c2

View File

@@ -28,6 +28,47 @@ HG = 'hg'
SDK_ROOT = r"C:\Program Files\Microsoft SDKs\Windows" SDK_ROOT = r"C:\Program Files\Microsoft SDKs\Windows"
BATCH_FAIL_ON_ERROR = "@IF %errorlevel% neq 0 exit /b %errorlevel%" BATCH_FAIL_ON_ERROR = "@IF %errorlevel% neq 0 exit /b %errorlevel%"
class PythonVersion:
def __init__(self, major, minor, bits):
self.major = major
self.minor = minor
self.bits = bits
self._executable = None
def get_executable(self, app):
if self._executable:
return self._executable
if self.bits == 32:
python = 'c:\\Python%s%s_32bit\\python.exe' % (self.major, self.minor)
else:
python = 'c:\\Python%s%s\\python.exe' % (self.major, self.minor)
if not os.path.exists(python):
print("Unable to find python %s" % self)
print("%s does not exists" % python)
sys.exit(1)
code = (
'import platform, sys; '
'print("{ver.major}.{ver.minor} {bits}".format('
'ver=sys.version_info, '
'bits=platform.architecture()[0]))'
)
exitcode, stdout = app.get_output(python, '-c', code)
stdout = stdout.rstrip()
expected = "%s.%s %sbit" % (self.major, self.minor, self.bits)
if stdout != expected:
print("Python version or architecture doesn't match")
print("got %r, expected %r" % (stdout, expected))
print(python)
sys.exit(1)
self._executable = python
return python
def __str__(self):
return 'Python %s.%s (%s bits)' % (self.major, self.minor, self.bits)
class Release(object): class Release(object):
def __init__(self): def __init__(self):
root = os.path.dirname(__file__) root = os.path.dirname(__file__)
@@ -37,10 +78,16 @@ class Release(object):
self.sdist = False self.sdist = False
self.dry_run = True self.dry_run = True
self.test = True self.test = True
self.aiotest = True self.aiotest = False
self.verbose = False
self.python_versions = [
PythonVersion(pyver[0], pyver[1], bits)
for pyver, bits in PYTHON_VERSIONS]
@contextlib.contextmanager @contextlib.contextmanager
def _popen(self, args, **kw): def _popen(self, args, **kw):
verbose = kw.pop('verbose', True)
if self.verbose and verbose:
print('+ ' + ' '.join(args)) print('+ ' + ' '.join(args))
if PY3: if PY3:
kw['universal_newlines'] = True kw['universal_newlines'] = True
@@ -49,9 +96,11 @@ class Release(object):
yield proc yield proc
def get_output(self, *args, **kw): def get_output(self, *args, **kw):
with self._popen(args, stdout=subprocess.PIPE, **kw) as proc: kw['stdout'] = subprocess.PIPE
kw['stderr'] = subprocess.STDOUT
with self._popen(args, **kw) as proc:
stdout, stderr = proc.communicate() stdout, stderr = proc.communicate()
return stdout return proc.returncode, stdout
def run_command(self, *args, **kw): def run_command(self, *args, **kw):
with self._popen(args, **kw) as proc: with self._popen(args, **kw) as proc:
@@ -60,65 +109,42 @@ class Release(object):
sys.exit(exitcode) sys.exit(exitcode)
def get_local_changes(self): def get_local_changes(self):
status = self.get_output(HG, 'status') exitcode, status = self.get_output(HG, 'status')
return [line for line in status.splitlines() return [line for line in status.splitlines()
if not line.startswith("?")] if not line.startswith("?")]
def remove_directory(self, name): def remove_directory(self, name):
path = os.path.join(self.root, name) path = os.path.join(self.root, name)
if os.path.exists(path): if os.path.exists(path):
if self.verbose:
print("Remove directory: %s" % name) print("Remove directory: %s" % name)
shutil.rmtree(path) shutil.rmtree(path)
def remove_file(self, name): def remove_file(self, name):
path = os.path.join(self.root, name) path = os.path.join(self.root, name)
if os.path.exists(path): if os.path.exists(path):
if self.verbose:
print("Remove file: %s" % name) print("Remove file: %s" % name)
os.unlink(path) os.unlink(path)
def windows_sdk_setenv(self, pyver, bits): def windows_sdk_setenv(self, pyver):
if pyver >= (3, 3): if (pyver.major, pyver.minor) >= (3, 3):
sdkver = "v7.1" sdkver = "v7.1"
else: else:
sdkver = "v7.0" sdkver = "v7.0"
setenv = os.path.join(SDK_ROOT, sdkver, 'Bin', 'SetEnv.cmd') setenv = os.path.join(SDK_ROOT, sdkver, 'Bin', 'SetEnv.cmd')
if not os.path.exists(setenv): if not os.path.exists(setenv):
print("Unable to find Windows SDK %s for Python %s.%s" print("Unable to find Windows SDK %s for %s"
% (sdkver, pyver[0], pyver[1])) % (sdkver, pyver))
print("Please download and install it") print("Please download and install it")
print("%s does not exists" % setenv) print("%s does not exists" % setenv)
sys.exit(1) sys.exit(1)
if bits == 64: if pyver.bits == 64:
arch = '/x64' arch = '/x64'
else: else:
arch = '/x86' arch = '/x86'
return ["CALL", setenv, "/release", arch] return ["CALL", setenv, "/release", arch]
def get_python(self, version, bits):
if bits == 32:
python = 'c:\\Python%s%s_32bit\\python.exe' % version
else:
python = 'c:\\Python%s%s\\python.exe' % version
if not os.path.exists(python):
print("Unable to find python%s.%s" % version)
print("%s does not exists" % python)
sys.exit(1)
code = (
'import platform, sys; '
'print("{ver.major}.{ver.minor} {bits}".format('
'ver=sys.version_info, '
'bits=platform.architecture()[0]))'
)
stdout = self.get_output(python, '-c', code)
stdout = stdout.rstrip()
expected = "%s.%s %sbit" % (version[0], version[1], bits)
if stdout != expected:
print("Python version or architecture doesn't match")
print("got %r, expected %r" % (stdout, expected))
print(python)
sys.exit(1)
return python
def quote(self, arg): def quote(self, arg):
if not re.search("[ '\"]", arg): if not re.search("[ '\"]", arg):
return arg return arg
@@ -129,6 +155,8 @@ class Release(object):
return ' '.join(self.quote(arg) for arg in args) return ' '.join(self.quote(arg) for arg in args)
def cleanup(self): def cleanup(self):
if self.verbose:
print("Cleanup")
self.remove_directory('build') self.remove_directory('build')
self.remove_directory('dist') self.remove_directory('dist')
self.remove_file('_overlapped.pyd') self.remove_file('_overlapped.pyd')
@@ -138,16 +166,18 @@ class Release(object):
self.cleanup() self.cleanup()
self.run_command(sys.executable, 'setup.py', 'sdist', 'upload') self.run_command(sys.executable, 'setup.py', 'sdist', 'upload')
def runtests(self, pyver, bits): def runtests(self, pyver):
pythonstr = "%s.%s (%s bits)" % (pyver[0], pyver[1], bits) print("Run tests on %s" % pyver)
python = self.get_python(pyver, bits)
self.build(pyver, bits, 'build') python = pyver.get_executable(self)
if bits == 64:
print("Build _overlapped.pyd for %s" % pyver)
self.build(pyver, 'build')
if pyver.bits == 64:
arch = 'win-amd64' arch = 'win-amd64'
else: else:
arch = 'win32' arch = 'win32'
build_dir = 'lib.%s-%s.%s' % (arch, pyver[0], pyver[1]) build_dir = 'lib.%s-%s.%s' % (arch, pyver.major, pyver.minor)
src = os.path.join(self.root, 'build', build_dir, PROJECT, '_overlapped.pyd') src = os.path.join(self.root, 'build', build_dir, PROJECT, '_overlapped.pyd')
dst = os.path.join(self.root, PROJECT, '_overlapped.pyd') dst = os.path.join(self.root, PROJECT, '_overlapped.pyd')
shutil.copyfile(src, dst) shutil.copyfile(src, dst)
@@ -159,26 +189,27 @@ class Release(object):
dbg_env[DEBUG_ENV_VAR] = '1' dbg_env[DEBUG_ENV_VAR] = '1'
args = (python, 'runtests.py', '-r') args = (python, 'runtests.py', '-r')
print("Run runtests.py in release mode with %s" % pythonstr) print("Run runtests.py in release mode on %s" % pyver)
self.run_command(*args, env=release_env) self.run_command(*args, env=release_env)
print("Run runtests.py in debug mode with %s" % pythonstr) print("Run runtests.py in debug mode on %s" % pyver)
self.run_command(*args, env=dbg_env) self.run_command(*args, env=dbg_env)
if self.aiotest: if self.aiotest:
args = (python, 'run_aiotest.py') args = (python, 'run_aiotest.py')
print("Run aiotest in release mode with %s" % pythonstr) print("Run aiotest in release mode on %s" % pyver)
self.run_command(*args, env=release_env) self.run_command(*args, env=release_env)
print("Run aiotest in debug mode with %s" % pythonstr) print("Run aiotest in debug mode on %s" % pyver)
self.run_command(*args, env=dbg_env) self.run_command(*args, env=dbg_env)
print("")
def build(self, pyver, bits, *cmds): def build(self, pyver, *cmds):
self.cleanup() self.cleanup()
setenv = self.windows_sdk_setenv(pyver, bits) setenv = self.windows_sdk_setenv(pyver)
python = self.get_python(pyver, bits) python = pyver.get_executable(self)
cmd = [python, 'setup.py'] + list(cmds) cmd = [python, 'setup.py'] + list(cmds)
@@ -196,15 +227,25 @@ class Release(object):
print(BATCH_FAIL_ON_ERROR, file=temp) print(BATCH_FAIL_ON_ERROR, file=temp)
try: try:
self.run_command(temp.name) if self.verbose:
print("Setup Windows SDK")
print("+ " + ' '.join(cmd))
if self.verbose:
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()
finally: finally:
os.unlink(temp.name) os.unlink(temp.name)
def test_wheel(self, pyver, bits): def test_wheel(self, pyver):
self.build(pyver, bits, 'bdist_wheel') print("Test building wheel package for %s" % pyver)
self.build(pyver, 'bdist_wheel')
def publish_wheel(self, pyver, bits): def publish_wheel(self, pyver):
self.build(pyver, bits, 'bdist_wheel', 'upload') self.build(pyver, 'bdist_wheel', 'upload')
def main(self): def main(self):
try: try:
@@ -235,14 +276,19 @@ class Release(object):
sys.exit(1) sys.exit(1)
hg_tag = sys.argv[1] hg_tag = sys.argv[1]
self.run_command(HG, 'up', hg_tag) 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)
if self.test: if self.test:
for pyver, bits in PYTHON_VERSIONS: for pyver in self.python_versions:
self.runtests(pyver, bits) self.runtests(pyver)
for pyver, bits in PYTHON_VERSIONS: for pyver in self.python_versions:
self.test_wheel(pyver, bits) self.test_wheel(pyver)
if self.dry_run: if self.dry_run:
sys.exit(0) sys.exit(0)
@@ -253,8 +299,8 @@ class Release(object):
if self.sdist: if self.sdist:
self.sdist_upload() self.sdist_upload()
for pyver, bits in PYTHON_VERSIONS: for pyver in self.python_versions:
self.publish_wheel(pyver, bits) self.publish_wheel(pyver)
print("") print("")
if self.register: if self.register:
@@ -262,9 +308,8 @@ class Release(object):
print("Uploaded:") print("Uploaded:")
if self.sdist: if self.sdist:
print("- sdist") print("- sdist")
for pyver, bits in PYTHON_VERSIONS: for pyver in self.python_versions:
print("- Windows wheel %s bits package for Python %s.%s" print("- Windows wheel package for %s" % pyver)
% (bits, pyver[0], pyver[1]))
if __name__ == "__main__": if __name__ == "__main__":
Release().main() Release().main()