2014-03-30 06:51:32 +00:00
|
|
|
|
# -*- coding: utf-8 -*-
|
2021-04-15 22:51:21 +00:00
|
|
|
|
from contextlib import suppress
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
import re
|
|
|
|
|
import shutil
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
from distutils.core import Command
|
|
|
|
|
from typing import Dict, Any
|
|
|
|
|
|
|
|
|
|
from generateChangelog import generateDebianChangelog
|
2014-01-12 15:35:31 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
# Maximum allowed repository size difference (in kB) following merge.
|
2019-08-29 20:56:25 +00:00
|
|
|
|
# This is used to prevent large files from being inappropriately added to
|
2014-04-03 20:56:17 +00:00
|
|
|
|
# the repository history.
|
|
|
|
|
MERGE_SIZE_LIMIT = 100
|
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
# Paths that are checked for style by flake and flake_diff
|
|
|
|
|
FLAKE_CHECK_PATHS = ['pyqtgraph', 'examples', 'tools']
|
|
|
|
|
|
|
|
|
|
# Flake style checks -- mandatory, recommended, optional
|
|
|
|
|
# See: http://pep8.readthedocs.org/en/1.4.6/intro.html
|
|
|
|
|
# and https://flake8.readthedocs.org/en/2.0/warnings.html
|
|
|
|
|
FLAKE_MANDATORY = set([
|
|
|
|
|
'E101', # indentation contains mixed spaces and tabs
|
|
|
|
|
'E112', # expected an indented block
|
|
|
|
|
'E122', # continuation line missing indentation or outdented
|
|
|
|
|
'E125', # continuation line does not distinguish itself from next line
|
|
|
|
|
'E133', # closing bracket is missing indentation
|
|
|
|
|
|
|
|
|
|
'E223', # tab before operator
|
|
|
|
|
'E224', # tab after operator
|
|
|
|
|
'E242', # tab after ‘,’
|
|
|
|
|
'E273', # tab after keyword
|
|
|
|
|
'E274', # tab before keyword
|
|
|
|
|
|
|
|
|
|
'E901', # SyntaxError or IndentationError
|
|
|
|
|
'E902', # IOError
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'W191', # indentation contains tabs
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'W601', # .has_key() is deprecated, use ‘in’
|
|
|
|
|
'W602', # deprecated form of raising exception
|
|
|
|
|
'W603', # ‘<>’ is deprecated, use ‘!=’
|
2019-08-29 20:56:25 +00:00
|
|
|
|
'W604', # backticks are deprecated, use ‘repr()’
|
2014-03-30 06:51:32 +00:00
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
FLAKE_RECOMMENDED = set([
|
|
|
|
|
'E124', # closing bracket does not match visual indentation
|
|
|
|
|
'E231', # missing whitespace after ‘,’
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'E211', # whitespace before ‘(‘
|
|
|
|
|
'E261', # at least two spaces before inline comment
|
|
|
|
|
'E271', # multiple spaces after keyword
|
|
|
|
|
'E272', # multiple spaces before keyword
|
|
|
|
|
'E304', # blank lines found after function decorator
|
|
|
|
|
|
|
|
|
|
'F401', # module imported but unused
|
|
|
|
|
'F402', # import module from line N shadowed by loop variable
|
|
|
|
|
'F403', # ‘from module import *’ used; unable to detect undefined names
|
|
|
|
|
'F404', # future import(s) name after other statements
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'E501', # line too long (82 > 79 characters)
|
|
|
|
|
'E502', # the backslash is redundant between brackets
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'E702', # multiple statements on one line (semicolon)
|
|
|
|
|
'E703', # statement ends with a semicolon
|
|
|
|
|
'E711', # comparison to None should be ‘if cond is None:’
|
|
|
|
|
'E712', # comparison to True should be ‘if cond is True:’ or ‘if cond:’
|
|
|
|
|
'E721', # do not compare types, use ‘isinstance()’
|
|
|
|
|
|
|
|
|
|
'F811', # redefinition of unused name from line N
|
|
|
|
|
'F812', # list comprehension redefines name from line N
|
|
|
|
|
'F821', # undefined name name
|
|
|
|
|
'F822', # undefined name name in __all__
|
|
|
|
|
'F823', # local variable name ... referenced before assignment
|
|
|
|
|
'F831', # duplicate argument name in function definition
|
|
|
|
|
'F841', # local variable name is assigned to but never used
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'W292', # no newline at end of file
|
|
|
|
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
FLAKE_OPTIONAL = set([
|
|
|
|
|
'E121', # continuation line indentation is not a multiple of four
|
|
|
|
|
'E123', # closing bracket does not match indentation of opening bracket
|
|
|
|
|
'E126', # continuation line over-indented for hanging indent
|
|
|
|
|
'E127', # continuation line over-indented for visual indent
|
|
|
|
|
'E128', # continuation line under-indented for visual indent
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'E201', # whitespace after ‘(‘
|
|
|
|
|
'E202', # whitespace before ‘)’
|
|
|
|
|
'E203', # whitespace before ‘:’
|
|
|
|
|
'E221', # multiple spaces before operator
|
|
|
|
|
'E222', # multiple spaces after operator
|
|
|
|
|
'E225', # missing whitespace around operator
|
|
|
|
|
'E227', # missing whitespace around bitwise or shift operator
|
|
|
|
|
'E226', # missing whitespace around arithmetic operator
|
|
|
|
|
'E228', # missing whitespace around modulo operator
|
|
|
|
|
'E241', # multiple spaces after ‘,’
|
|
|
|
|
'E251', # unexpected spaces around keyword / parameter equals
|
2019-08-29 20:56:25 +00:00
|
|
|
|
'E262', # inline comment should start with ‘# ‘
|
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'E301', # expected 1 blank line, found 0
|
|
|
|
|
'E302', # expected 2 blank lines, found 0
|
|
|
|
|
'E303', # too many blank lines (3)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'E401', # multiple imports on one line
|
|
|
|
|
|
|
|
|
|
'E701', # multiple statements on one line (colon)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'W291', # trailing whitespace
|
|
|
|
|
'W293', # blank line contains whitespace
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'W391', # blank line at end of file
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
FLAKE_IGNORE = set([
|
|
|
|
|
# 111 and 113 are ignored because they appear to be broken.
|
|
|
|
|
'E111', # indentation is not a multiple of four
|
|
|
|
|
'E113', # unexpected indentation
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def checkStyle():
|
|
|
|
|
""" Run flake8, checking only lines that are modified since the last
|
|
|
|
|
git commit. """
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
# First check _all_ code against mandatory error codes
|
|
|
|
|
print('flake8: check all code against mandatory error set...')
|
|
|
|
|
errors = ','.join(FLAKE_MANDATORY)
|
|
|
|
|
cmd = ['flake8', '--select=' + errors] + FLAKE_CHECK_PATHS
|
|
|
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
|
|
|
|
#ret = proc.wait()
|
|
|
|
|
output = proc.stdout.read().decode('utf-8')
|
|
|
|
|
ret = proc.wait()
|
|
|
|
|
printFlakeOutput(output)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 19:56:22 +00:00
|
|
|
|
# Check for DOS newlines
|
|
|
|
|
print('check line endings in all files...')
|
|
|
|
|
count = 0
|
|
|
|
|
allowedEndings = set([None, '\n'])
|
|
|
|
|
for path, dirs, files in os.walk('.'):
|
2019-08-29 20:56:25 +00:00
|
|
|
|
if path.startswith("." + os.path.sep + ".tox"):
|
|
|
|
|
continue
|
2014-04-03 19:56:22 +00:00
|
|
|
|
for f in files:
|
|
|
|
|
if os.path.splitext(f)[1] not in ('.py', '.rst'):
|
|
|
|
|
continue
|
|
|
|
|
filename = os.path.join(path, f)
|
|
|
|
|
fh = open(filename, 'U')
|
2019-08-29 20:56:25 +00:00
|
|
|
|
_ = fh.readlines()
|
|
|
|
|
endings = set(
|
|
|
|
|
fh.newlines
|
|
|
|
|
if isinstance(fh.newlines, tuple)
|
|
|
|
|
else (fh.newlines,)
|
|
|
|
|
)
|
2014-04-03 19:56:22 +00:00
|
|
|
|
endings -= allowedEndings
|
|
|
|
|
if len(endings) > 0:
|
2019-08-29 20:56:25 +00:00
|
|
|
|
print("\033[0;31m"
|
|
|
|
|
+ "File has invalid line endings: "
|
|
|
|
|
+ "%s" % filename + "\033[0m")
|
2014-04-03 19:56:22 +00:00
|
|
|
|
ret = ret | 2
|
|
|
|
|
count += 1
|
|
|
|
|
print('checked line endings in %d files' % count)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
# Next check new code with optional error codes
|
|
|
|
|
print('flake8: check new code against recommended error set...')
|
|
|
|
|
diff = subprocess.check_output(['git', 'diff'])
|
2019-08-29 20:56:25 +00:00
|
|
|
|
proc = subprocess.Popen(['flake8', '--diff', # '--show-source',
|
2014-03-30 06:51:32 +00:00
|
|
|
|
'--ignore=' + errors],
|
2019-08-29 20:56:25 +00:00
|
|
|
|
stdin=subprocess.PIPE,
|
2014-03-30 06:51:32 +00:00
|
|
|
|
stdout=subprocess.PIPE)
|
|
|
|
|
proc.stdin.write(diff)
|
|
|
|
|
proc.stdin.close()
|
|
|
|
|
output = proc.stdout.read().decode('utf-8')
|
|
|
|
|
ret |= printFlakeOutput(output)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
if ret == 0:
|
2014-04-03 19:56:22 +00:00
|
|
|
|
print('style test passed.')
|
2014-03-30 06:51:32 +00:00
|
|
|
|
else:
|
2014-04-03 19:56:22 +00:00
|
|
|
|
print('style test failed: %d' % ret)
|
2014-04-03 20:56:17 +00:00
|
|
|
|
return ret
|
2014-03-30 06:51:32 +00:00
|
|
|
|
|
|
|
|
|
def printFlakeOutput(text):
|
|
|
|
|
""" Print flake output, colored by error category.
|
|
|
|
|
Return 2 if there were any mandatory errors,
|
|
|
|
|
1 if only recommended / optional errors, and
|
|
|
|
|
0 if only optional errors.
|
|
|
|
|
"""
|
|
|
|
|
ret = 0
|
|
|
|
|
gotError = False
|
|
|
|
|
for line in text.split('\n'):
|
|
|
|
|
m = re.match(r'[^\:]+\:\d+\:\d+\: (\w+) .*', line)
|
|
|
|
|
if m is None:
|
|
|
|
|
print(line)
|
|
|
|
|
else:
|
|
|
|
|
gotError = True
|
|
|
|
|
error = m.group(1)
|
|
|
|
|
if error in FLAKE_MANDATORY:
|
|
|
|
|
print("\033[0;31m" + line + "\033[0m")
|
|
|
|
|
ret |= 2
|
|
|
|
|
elif error in FLAKE_RECOMMENDED:
|
|
|
|
|
print("\033[0;33m" + line + "\033[0m")
|
|
|
|
|
#ret |= 1
|
|
|
|
|
elif error in FLAKE_OPTIONAL:
|
|
|
|
|
print("\033[0;32m" + line + "\033[0m")
|
|
|
|
|
elif error in FLAKE_IGNORE:
|
|
|
|
|
continue
|
|
|
|
|
else:
|
|
|
|
|
print("\033[0;36m" + line + "\033[0m")
|
|
|
|
|
if not gotError:
|
|
|
|
|
print(" [ no errors ]\n")
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def unitTests():
|
2014-04-03 20:56:17 +00:00
|
|
|
|
"""
|
|
|
|
|
Run all unit tests (using py.test)
|
|
|
|
|
Return the exit code.
|
|
|
|
|
"""
|
2014-03-30 06:51:32 +00:00
|
|
|
|
try:
|
|
|
|
|
if sys.version[0] == '3':
|
2021-04-15 22:51:21 +00:00
|
|
|
|
out = subprocess.check_output('PYTHONPATH=. py.test-3', shell=True)
|
2014-03-30 06:51:32 +00:00
|
|
|
|
else:
|
2021-04-15 22:51:21 +00:00
|
|
|
|
out = subprocess.check_output('PYTHONPATH=. py.test', shell=True)
|
2014-03-30 06:51:32 +00:00
|
|
|
|
ret = 0
|
|
|
|
|
except Exception as e:
|
|
|
|
|
out = e.output
|
|
|
|
|
ret = e.returncode
|
|
|
|
|
print(out.decode('utf-8'))
|
|
|
|
|
return ret
|
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
|
2019-08-29 20:56:25 +00:00
|
|
|
|
def checkMergeSize(
|
|
|
|
|
sourceBranch=None,
|
|
|
|
|
targetBranch=None,
|
|
|
|
|
sourceRepo=None,
|
|
|
|
|
targetRepo=None
|
|
|
|
|
):
|
2014-04-03 20:56:17 +00:00
|
|
|
|
"""
|
2019-08-29 20:56:25 +00:00
|
|
|
|
Check that a git merge would not increase the repository size by
|
|
|
|
|
MERGE_SIZE_LIMIT.
|
2014-04-03 20:56:17 +00:00
|
|
|
|
"""
|
|
|
|
|
if sourceBranch is None:
|
|
|
|
|
sourceBranch = getGitBranch()
|
|
|
|
|
sourceRepo = '..'
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 21:20:01 +00:00
|
|
|
|
if targetBranch is None:
|
2020-10-01 04:04:09 +00:00
|
|
|
|
if sourceBranch == 'master':
|
|
|
|
|
targetBranch = 'master'
|
2014-04-03 21:20:01 +00:00
|
|
|
|
targetRepo = 'https://github.com/pyqtgraph/pyqtgraph.git'
|
|
|
|
|
else:
|
2020-10-01 04:04:09 +00:00
|
|
|
|
targetBranch = 'master'
|
2014-04-03 21:20:01 +00:00
|
|
|
|
targetRepo = '..'
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
workingDir = '__merge-test-clone'
|
2019-08-29 20:56:25 +00:00
|
|
|
|
env = dict(TARGET_BRANCH=targetBranch,
|
|
|
|
|
SOURCE_BRANCH=sourceBranch,
|
|
|
|
|
TARGET_REPO=targetRepo,
|
2014-04-03 20:56:17 +00:00
|
|
|
|
SOURCE_REPO=sourceRepo,
|
|
|
|
|
WORKING_DIR=workingDir,
|
|
|
|
|
)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
print("Testing merge size difference:\n"
|
|
|
|
|
" SOURCE: {SOURCE_REPO} {SOURCE_BRANCH}\n"
|
|
|
|
|
" TARGET: {TARGET_BRANCH} {TARGET_REPO}".format(**env))
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
setup = """
|
|
|
|
|
mkdir {WORKING_DIR} && cd {WORKING_DIR} &&
|
|
|
|
|
git init && git remote add -t {TARGET_BRANCH} target {TARGET_REPO} &&
|
2019-08-29 20:56:25 +00:00
|
|
|
|
git fetch target {TARGET_BRANCH} &&
|
|
|
|
|
git checkout -qf target/{TARGET_BRANCH} &&
|
2014-04-03 20:56:17 +00:00
|
|
|
|
git gc -q --aggressive
|
|
|
|
|
""".format(**env)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
checkSize = """
|
2019-08-29 20:56:25 +00:00
|
|
|
|
cd {WORKING_DIR} &&
|
2014-04-03 20:56:17 +00:00
|
|
|
|
du -s . | sed -e "s/\t.*//"
|
|
|
|
|
""".format(**env)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
merge = """
|
|
|
|
|
cd {WORKING_DIR} &&
|
2019-08-29 20:56:25 +00:00
|
|
|
|
git pull -q {SOURCE_REPO} {SOURCE_BRANCH} &&
|
2014-04-03 20:56:17 +00:00
|
|
|
|
git gc -q --aggressive
|
|
|
|
|
""".format(**env)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
try:
|
|
|
|
|
print("Check out target branch:\n" + setup)
|
2021-04-15 22:51:21 +00:00
|
|
|
|
subprocess.check_call(setup, shell=True)
|
|
|
|
|
targetSize = int(subprocess.check_output(checkSize, shell=True))
|
2014-04-03 20:56:17 +00:00
|
|
|
|
print("TARGET SIZE: %d kB" % targetSize)
|
|
|
|
|
print("Merge source branch:\n" + merge)
|
2021-04-15 22:51:21 +00:00
|
|
|
|
subprocess.check_call(merge, shell=True)
|
|
|
|
|
mergeSize = int(subprocess.check_output(checkSize, shell=True))
|
2014-04-03 20:56:17 +00:00
|
|
|
|
print("MERGE SIZE: %d kB" % mergeSize)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
diff = mergeSize - targetSize
|
|
|
|
|
if diff <= MERGE_SIZE_LIMIT:
|
|
|
|
|
print("DIFFERENCE: %d kB [OK]" % diff)
|
|
|
|
|
return 0
|
|
|
|
|
else:
|
2019-08-29 20:56:25 +00:00
|
|
|
|
print("\033[0;31m"
|
|
|
|
|
+ "DIFFERENCE: %d kB [exceeds %d kB]" % (
|
|
|
|
|
diff,
|
|
|
|
|
MERGE_SIZE_LIMIT)
|
|
|
|
|
+ "\033[0m")
|
2014-04-03 20:56:17 +00:00
|
|
|
|
return 2
|
|
|
|
|
finally:
|
|
|
|
|
if os.path.isdir(workingDir):
|
|
|
|
|
shutil.rmtree(workingDir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mergeTests():
|
|
|
|
|
ret = checkMergeSize()
|
|
|
|
|
ret |= unitTests()
|
|
|
|
|
ret |= checkStyle()
|
|
|
|
|
if ret == 0:
|
|
|
|
|
print("\033[0;32m" + "\nAll merge tests passed." + "\033[0m")
|
|
|
|
|
else:
|
|
|
|
|
print("\033[0;31m" + "\nMerge tests failed." + "\033[0m")
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
2014-01-12 15:35:31 +00:00
|
|
|
|
def listAllPackages(pkgroot):
|
|
|
|
|
path = os.getcwd()
|
|
|
|
|
n = len(path.split(os.path.sep))
|
2019-08-29 20:56:25 +00:00
|
|
|
|
subdirs = [
|
|
|
|
|
i[0].split(os.path.sep)[n:]
|
|
|
|
|
for i in os.walk(os.path.join(path, pkgroot))
|
|
|
|
|
if '__init__.py' in i[2]
|
|
|
|
|
]
|
2014-01-12 15:35:31 +00:00
|
|
|
|
return ['.'.join(p) for p in subdirs]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def getInitVersion(pkgroot):
|
|
|
|
|
"""Return the version string defined in __init__.py"""
|
|
|
|
|
path = os.getcwd()
|
|
|
|
|
initfile = os.path.join(path, pkgroot, '__init__.py')
|
|
|
|
|
init = open(initfile).read()
|
|
|
|
|
m = re.search(r'__version__ = (\S+)\n', init)
|
|
|
|
|
if m is None or len(m.groups()) != 1:
|
2019-08-29 20:56:25 +00:00
|
|
|
|
raise Exception("Cannot determine __version__ from init file: "
|
|
|
|
|
+ "'%s'!" % initfile)
|
2014-01-12 15:35:31 +00:00
|
|
|
|
version = m.group(1).strip('\'\"')
|
|
|
|
|
return version
|
|
|
|
|
|
|
|
|
|
def gitCommit(name):
|
|
|
|
|
"""Return the commit ID for the given name."""
|
2021-04-15 22:51:21 +00:00
|
|
|
|
commit = subprocess.check_output(
|
2019-08-29 20:56:25 +00:00
|
|
|
|
['git', 'show', name],
|
|
|
|
|
universal_newlines=True).split('\n')[0]
|
2014-01-12 15:35:31 +00:00
|
|
|
|
assert commit[:7] == 'commit '
|
|
|
|
|
return commit[7:]
|
|
|
|
|
|
|
|
|
|
def getGitVersion(tagPrefix):
|
|
|
|
|
"""Return a version string with information about this git checkout.
|
2019-08-29 20:56:25 +00:00
|
|
|
|
If the checkout is an unmodified, tagged commit, then return the tag
|
|
|
|
|
version
|
|
|
|
|
|
|
|
|
|
If this is not a tagged commit, return the output of
|
|
|
|
|
``git describe --tags``
|
|
|
|
|
|
2014-01-12 15:35:31 +00:00
|
|
|
|
If this checkout has been modified, append "+" to the version.
|
|
|
|
|
"""
|
|
|
|
|
path = os.getcwd()
|
|
|
|
|
if not os.path.isdir(os.path.join(path, '.git')):
|
|
|
|
|
return None
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2021-04-15 22:51:21 +00:00
|
|
|
|
try:
|
|
|
|
|
v = (
|
|
|
|
|
subprocess.check_output(
|
|
|
|
|
["git", "describe", "--tags", "--dirty", '--match="%s*"' % tagPrefix],
|
|
|
|
|
stderr=subprocess.DEVNULL)
|
|
|
|
|
.strip()
|
|
|
|
|
.decode("utf-8")
|
|
|
|
|
)
|
|
|
|
|
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
|
|
|
return None
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2016-09-22 00:40:40 +00:00
|
|
|
|
# chop off prefix
|
|
|
|
|
assert v.startswith(tagPrefix)
|
|
|
|
|
v = v[len(tagPrefix):]
|
|
|
|
|
|
|
|
|
|
# split up version parts
|
|
|
|
|
parts = v.split('-')
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2016-09-22 00:40:40 +00:00
|
|
|
|
# has working tree been modified?
|
2014-01-12 15:35:31 +00:00
|
|
|
|
modified = False
|
2016-09-22 00:40:40 +00:00
|
|
|
|
if parts[-1] == 'dirty':
|
|
|
|
|
modified = True
|
|
|
|
|
parts = parts[:-1]
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2016-09-22 00:40:40 +00:00
|
|
|
|
# have commits been added on top of last tagged version?
|
|
|
|
|
# (git describe adds -NNN-gXXXXXXX if this is the case)
|
|
|
|
|
local = None
|
2019-08-29 20:56:25 +00:00
|
|
|
|
if (len(parts) > 2 and
|
|
|
|
|
re.match(r'\d+', parts[-2]) and
|
|
|
|
|
re.match(r'g[0-9a-f]{7}', parts[-1])):
|
2016-09-22 00:40:40 +00:00
|
|
|
|
local = parts[-1]
|
|
|
|
|
parts = parts[:-2]
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2016-09-22 00:40:40 +00:00
|
|
|
|
gitVersion = '-'.join(parts)
|
|
|
|
|
if local is not None:
|
|
|
|
|
gitVersion += '+' + local
|
2014-01-12 15:35:31 +00:00
|
|
|
|
if modified:
|
2016-09-22 00:40:40 +00:00
|
|
|
|
gitVersion += 'm'
|
2014-01-12 15:35:31 +00:00
|
|
|
|
|
|
|
|
|
return gitVersion
|
|
|
|
|
|
2014-01-22 19:23:10 +00:00
|
|
|
|
def getGitBranch():
|
2019-08-29 20:56:25 +00:00
|
|
|
|
m = re.search(
|
|
|
|
|
r'\* (.*)',
|
2021-04-15 22:51:21 +00:00
|
|
|
|
subprocess.check_output(['git', 'branch'],
|
2019-08-29 20:56:25 +00:00
|
|
|
|
universal_newlines=True))
|
2014-01-22 19:23:10 +00:00
|
|
|
|
if m is None:
|
|
|
|
|
return ''
|
|
|
|
|
else:
|
|
|
|
|
return m.group(1)
|
|
|
|
|
|
2014-01-12 15:35:31 +00:00
|
|
|
|
def getVersionStrings(pkg):
|
|
|
|
|
"""
|
2019-08-29 20:56:25 +00:00
|
|
|
|
Returns 4 version strings:
|
|
|
|
|
|
2014-01-12 15:35:31 +00:00
|
|
|
|
* the version string to use for this build,
|
|
|
|
|
* version string requested with --force-version (or None)
|
|
|
|
|
* version string that describes the current git checkout (or None).
|
2019-08-29 20:56:25 +00:00
|
|
|
|
* version string in the pkg/__init__.py,
|
|
|
|
|
|
2014-01-12 15:35:31 +00:00
|
|
|
|
The first return value is (forceVersion or gitVersion or initVersion).
|
|
|
|
|
"""
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 15:35:31 +00:00
|
|
|
|
## Determine current version string from __init__.py
|
2016-09-22 00:40:40 +00:00
|
|
|
|
initVersion = getInitVersion(pkgroot=pkg)
|
2014-01-12 15:35:31 +00:00
|
|
|
|
|
2019-08-29 20:56:25 +00:00
|
|
|
|
# If this is a git checkout
|
|
|
|
|
# try to generate a more descriptive version string
|
2014-01-12 15:35:31 +00:00
|
|
|
|
try:
|
2016-09-22 00:40:40 +00:00
|
|
|
|
gitVersion = getGitVersion(tagPrefix=pkg+'-')
|
2014-01-12 15:35:31 +00:00
|
|
|
|
except:
|
|
|
|
|
gitVersion = None
|
2019-08-29 20:56:25 +00:00
|
|
|
|
sys.stderr.write("This appears to be a git checkout, but an error "
|
|
|
|
|
"occurred while attempting to determine a version "
|
|
|
|
|
"string for the current commit.\n")
|
2014-01-12 15:35:31 +00:00
|
|
|
|
sys.excepthook(*sys.exc_info())
|
|
|
|
|
|
|
|
|
|
# See whether a --force-version flag was given
|
|
|
|
|
forcedVersion = None
|
2019-08-29 20:56:25 +00:00
|
|
|
|
for i, arg in enumerate(sys.argv):
|
2014-01-12 15:35:31 +00:00
|
|
|
|
if arg.startswith('--force-version'):
|
|
|
|
|
if arg == '--force-version':
|
|
|
|
|
forcedVersion = sys.argv[i+1]
|
|
|
|
|
sys.argv.pop(i)
|
|
|
|
|
sys.argv.pop(i)
|
|
|
|
|
elif arg.startswith('--force-version='):
|
|
|
|
|
forcedVersion = sys.argv[i].replace('--force-version=', '')
|
|
|
|
|
sys.argv.pop(i)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
|
|
|
|
|
2014-01-12 15:35:31 +00:00
|
|
|
|
## Finally decide on a version string to use:
|
|
|
|
|
if forcedVersion is not None:
|
|
|
|
|
version = forcedVersion
|
|
|
|
|
else:
|
|
|
|
|
version = initVersion
|
2018-05-30 15:53:32 +00:00
|
|
|
|
# if git says this is a modified branch, add local version information
|
|
|
|
|
if gitVersion is not None:
|
2020-10-18 05:22:29 +00:00
|
|
|
|
_, _, local = gitVersion.partition('+')
|
2018-05-30 15:53:32 +00:00
|
|
|
|
if local != '':
|
|
|
|
|
version = version + '+' + local
|
2019-08-29 20:56:25 +00:00
|
|
|
|
sys.stderr.write("Detected git commit; "
|
|
|
|
|
+ "will use version string: '%s'\n" % version)
|
2014-01-12 15:35:31 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
return version, forcedVersion, gitVersion, initVersion
|
|
|
|
|
|
|
|
|
|
|
2021-04-15 22:51:21 +00:00
|
|
|
|
DEFAULT_ASV: Dict[str, Any] = {
|
|
|
|
|
"version": 1,
|
|
|
|
|
"project": "pyqtgraph",
|
|
|
|
|
"project_url": "http://pyqtgraph.org/",
|
|
|
|
|
"repo": ".",
|
|
|
|
|
"branches": ["master"],
|
|
|
|
|
"environment_type": "virtualenv",
|
|
|
|
|
"show_commit_url": "http://github.com/pyqtgraph/pyqtgraph/commit/",
|
|
|
|
|
# "pythons": ["3.7", "3.8", "3.9"],
|
|
|
|
|
"matrix": {
|
|
|
|
|
# "numpy": ["1.17", "1.18", "1.19", ""],
|
|
|
|
|
"numpy": "",
|
|
|
|
|
"pyqt5": ["", None],
|
|
|
|
|
"pyside2": ["", None],
|
|
|
|
|
},
|
|
|
|
|
"exclude": [
|
|
|
|
|
{"pyqt5": "", "pyside2": ""},
|
|
|
|
|
{"pyqt5": None, "pyside2": None}
|
|
|
|
|
],
|
|
|
|
|
"benchmark_dir": "benchmarks",
|
|
|
|
|
"env_dir": ".asv/env",
|
|
|
|
|
"results_dir": ".asv/results",
|
|
|
|
|
"html_dir": ".asv/html",
|
|
|
|
|
"build_cache_size": 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ASVConfigCommand(Command):
|
|
|
|
|
description = "Setup the ASV benchmarking config for this system"
|
|
|
|
|
user_options = []
|
|
|
|
|
|
|
|
|
|
def initialize_options(self) -> None:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def finalize_options(self) -> None:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def run(self) -> None:
|
|
|
|
|
config = DEFAULT_ASV
|
|
|
|
|
with suppress(FileNotFoundError, subprocess.CalledProcessError):
|
|
|
|
|
cuda_check = subprocess.check_output(["nvcc", "--version"])
|
|
|
|
|
match = re.search(r"release (\d{1,2}\.\d)", cuda_check.decode("utf-8"))
|
|
|
|
|
ver = match.groups()[0] # e.g. 11.0
|
|
|
|
|
ver_str = ver.replace(".", "") # e.g. 110
|
|
|
|
|
config["matrix"][f"cupy-cuda{ver_str}"] = ""
|
|
|
|
|
|
|
|
|
|
with open("asv.conf.json", "w") as conf_file:
|
|
|
|
|
conf_file.write(json.dumps(config, indent=2))
|
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
|
|
|
|
|
class DebCommand(Command):
|
|
|
|
|
description = "build .deb package using `debuild -us -uc`"
|
2014-01-13 04:59:53 +00:00
|
|
|
|
maintainer = "Luke Campagnola <luke.campagnola@gmail.com>"
|
2014-01-22 19:23:10 +00:00
|
|
|
|
debTemplate = "debian"
|
2014-01-12 16:50:52 +00:00
|
|
|
|
debDir = "deb_build"
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
user_options = []
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
def initialize_options(self):
|
|
|
|
|
self.cwd = None
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
def finalize_options(self):
|
|
|
|
|
self.cwd = os.getcwd()
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
def run(self):
|
|
|
|
|
version = self.distribution.get_version()
|
|
|
|
|
pkgName = self.distribution.get_name()
|
|
|
|
|
debName = "python-" + pkgName
|
|
|
|
|
debDir = self.debDir
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2021-04-15 22:51:21 +00:00
|
|
|
|
assert os.getcwd() == self.cwd, 'Must be in package root: %s' % self.cwd
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
if os.path.isdir(debDir):
|
|
|
|
|
raise Exception('DEB build dir already exists: "%s"' % debDir)
|
|
|
|
|
sdist = "dist/%s-%s.tar.gz" % (pkgName, version)
|
|
|
|
|
if not os.path.isfile(sdist):
|
2019-08-29 20:56:25 +00:00
|
|
|
|
raise Exception("No source distribution; "
|
|
|
|
|
+ "run `setup.py sdist` first.")
|
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
# copy sdist to build directory and extract
|
|
|
|
|
os.mkdir(debDir)
|
|
|
|
|
renamedSdist = '%s_%s.orig.tar.gz' % (debName, version)
|
2014-01-13 04:59:53 +00:00
|
|
|
|
print("copy %s => %s" % (sdist, os.path.join(debDir, renamedSdist)))
|
2014-01-12 16:50:52 +00:00
|
|
|
|
shutil.copy(sdist, os.path.join(debDir, renamedSdist))
|
2014-01-13 04:59:53 +00:00
|
|
|
|
print("cd %s; tar -xzf %s" % (debDir, renamedSdist))
|
2014-01-12 16:50:52 +00:00
|
|
|
|
if os.system("cd %s; tar -xzf %s" % (debDir, renamedSdist)) != 0:
|
|
|
|
|
raise Exception("Error extracting source distribution.")
|
|
|
|
|
buildDir = '%s/%s-%s' % (debDir, pkgName, version)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
# copy debian control structure
|
2014-01-13 04:59:53 +00:00
|
|
|
|
print("copytree %s => %s" % (self.debTemplate, buildDir+'/debian'))
|
2014-01-12 16:50:52 +00:00
|
|
|
|
shutil.copytree(self.debTemplate, buildDir+'/debian')
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
# Write new changelog
|
2019-08-29 20:56:25 +00:00
|
|
|
|
chlog = generateDebianChangelog(
|
|
|
|
|
pkgName,
|
|
|
|
|
'CHANGELOG',
|
|
|
|
|
version,
|
|
|
|
|
self.maintainer)
|
2014-01-13 04:59:53 +00:00
|
|
|
|
print("write changelog %s" % buildDir+'/debian/changelog')
|
2014-01-12 16:50:52 +00:00
|
|
|
|
open(buildDir+'/debian/changelog', 'w').write(chlog)
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-01-12 16:50:52 +00:00
|
|
|
|
# build package
|
2014-01-13 04:59:53 +00:00
|
|
|
|
print('cd %s; debuild -us -uc' % buildDir)
|
2014-01-12 16:50:52 +00:00
|
|
|
|
if os.system('cd %s; debuild -us -uc' % buildDir) != 0:
|
|
|
|
|
raise Exception("Error during debuild.")
|
|
|
|
|
|
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
class DebugCommand(Command):
|
|
|
|
|
"""Just for learning about distutils."""
|
2014-01-12 16:50:52 +00:00
|
|
|
|
description = ""
|
|
|
|
|
user_options = []
|
|
|
|
|
def initialize_options(self):
|
|
|
|
|
pass
|
|
|
|
|
def finalize_options(self):
|
|
|
|
|
pass
|
|
|
|
|
def run(self):
|
|
|
|
|
global cmd
|
|
|
|
|
cmd = self
|
2014-01-13 04:59:53 +00:00
|
|
|
|
print(self.distribution.name)
|
|
|
|
|
print(self.distribution.version)
|
2014-03-30 06:51:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestCommand(Command):
|
2019-08-29 20:56:25 +00:00
|
|
|
|
description = "Run all package tests and exit immediately with ", \
|
|
|
|
|
"informative return code."
|
2014-03-30 06:51:32 +00:00
|
|
|
|
user_options = []
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
def run(self):
|
|
|
|
|
sys.exit(unitTests())
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
def initialize_options(self):
|
|
|
|
|
pass
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
def finalize_options(self):
|
|
|
|
|
pass
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
|
|
|
|
|
class StyleCommand(Command):
|
2019-08-29 20:56:25 +00:00
|
|
|
|
description = "Check all code for style, exit immediately with ", \
|
|
|
|
|
"informative return code."
|
2014-03-30 06:51:32 +00:00
|
|
|
|
user_options = []
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
def run(self):
|
|
|
|
|
sys.exit(checkStyle())
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
def initialize_options(self):
|
|
|
|
|
pass
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-03-30 06:51:32 +00:00
|
|
|
|
def finalize_options(self):
|
|
|
|
|
pass
|
2014-04-03 20:56:17 +00:00
|
|
|
|
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
class MergeTestCommand(Command):
|
2019-08-29 20:56:25 +00:00
|
|
|
|
description = "Run all tests needed to determine whether the current ",\
|
|
|
|
|
"code is suitable for merge."
|
2014-04-03 20:56:17 +00:00
|
|
|
|
user_options = []
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
def run(self):
|
|
|
|
|
sys.exit(mergeTests())
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
def initialize_options(self):
|
|
|
|
|
pass
|
2019-08-29 20:56:25 +00:00
|
|
|
|
|
2014-04-03 20:56:17 +00:00
|
|
|
|
def finalize_options(self):
|
|
|
|
|
pass
|