mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-29 12:23:42 +03:00
Added support for wheel building and uploading
To be used by the psycopg/psycopg2-wheels project.
This commit is contained in:
parent
9eec303cf7
commit
637a990e09
|
@ -18,7 +18,6 @@ from glob import glob
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from functools import lru_cache
|
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
||||||
opt = None
|
opt = None
|
||||||
|
@ -39,8 +38,7 @@ def main():
|
||||||
cmd()
|
cmd()
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
def setup_build_env():
|
||||||
def setup_env():
|
|
||||||
"""
|
"""
|
||||||
Set the environment variables according to the build environment
|
Set the environment variables according to the build environment
|
||||||
"""
|
"""
|
||||||
|
@ -69,7 +67,7 @@ def setup_env():
|
||||||
if not (vc_dir() / r"bin\amd64\vcvars64.bat").exists():
|
if not (vc_dir() / r"bin\amd64\vcvars64.bat").exists():
|
||||||
logger.info("Fixing VS2010 Express and 64bit builds")
|
logger.info("Fixing VS2010 Express and 64bit builds")
|
||||||
copy_file(
|
copy_file(
|
||||||
clone_dir() / r"scripts\vcvars64-vs2010.bat",
|
package_dir() / r"scripts\vcvars64-vs2010.bat",
|
||||||
vc_dir() / r"bin\amd64\vcvars64.bat",
|
vc_dir() / r"bin\amd64\vcvars64.bat",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,6 +89,17 @@ def step_install():
|
||||||
configure_sdk()
|
configure_sdk()
|
||||||
configure_postgres()
|
configure_postgres()
|
||||||
|
|
||||||
|
if is_wheel():
|
||||||
|
install_wheel_support()
|
||||||
|
|
||||||
|
|
||||||
|
def install_wheel_support():
|
||||||
|
"""
|
||||||
|
Install an up-to-date pip wheel package to build wheels.
|
||||||
|
"""
|
||||||
|
run_command([py_exe()] + "-m pip install --upgrade pip".split())
|
||||||
|
run_command([py_exe()] + "-m pip install wheel".split())
|
||||||
|
|
||||||
|
|
||||||
def configure_sdk():
|
def configure_sdk():
|
||||||
# The program rc.exe on 64bit with some versions look in the wrong path
|
# The program rc.exe on 64bit with some versions look in the wrong path
|
||||||
|
@ -106,7 +115,9 @@ def configure_sdk():
|
||||||
|
|
||||||
|
|
||||||
def configure_postgres():
|
def configure_postgres():
|
||||||
# Change PostgreSQL config before service starts
|
"""
|
||||||
|
Set up PostgreSQL config before the service starts.
|
||||||
|
"""
|
||||||
logger.info("Configuring Postgres")
|
logger.info("Configuring Postgres")
|
||||||
with (pg_data_dir() / 'postgresql.conf').open('a') as f:
|
with (pg_data_dir() / 'postgresql.conf').open('a') as f:
|
||||||
# allow > 1 prepared transactions for test cases
|
# allow > 1 prepared transactions for test cases
|
||||||
|
@ -144,18 +155,21 @@ def configure_postgres():
|
||||||
|
|
||||||
|
|
||||||
def run_openssl(args):
|
def run_openssl(args):
|
||||||
"""Run the appveyor-installed openssl"""
|
"""Run the appveyor-installed openssl with some args."""
|
||||||
# https://www.appveyor.com/docs/windows-images-software/
|
# https://www.appveyor.com/docs/windows-images-software/
|
||||||
openssl = Path(r"C:\OpenSSL-v111-Win64") / 'bin' / 'openssl'
|
openssl = Path(r"C:\OpenSSL-v111-Win64") / 'bin' / 'openssl'
|
||||||
return run_command([openssl] + args)
|
return run_command([openssl] + args)
|
||||||
|
|
||||||
|
|
||||||
def step_build_script():
|
def step_build_script():
|
||||||
setup_env()
|
setup_build_env()
|
||||||
build_openssl()
|
build_openssl()
|
||||||
build_libpq()
|
build_libpq()
|
||||||
build_psycopg()
|
build_psycopg()
|
||||||
|
|
||||||
|
if is_wheel():
|
||||||
|
build_binary_packages()
|
||||||
|
|
||||||
|
|
||||||
def build_openssl():
|
def build_openssl():
|
||||||
top = base_dir() / 'openssl'
|
top = base_dir() / 'openssl'
|
||||||
|
@ -305,14 +319,9 @@ $config->{openssl} = "%s";
|
||||||
|
|
||||||
|
|
||||||
def build_psycopg():
|
def build_psycopg():
|
||||||
os.chdir(clone_dir())
|
os.chdir(package_dir())
|
||||||
|
patch_package_name()
|
||||||
# Find the pg_config just built
|
add_pg_config_path()
|
||||||
path = os.pathsep.join(
|
|
||||||
[str(base_dir() / r'postgresql\bin'), os.environ['PATH']]
|
|
||||||
)
|
|
||||||
setenv('PATH', path)
|
|
||||||
|
|
||||||
run_command(
|
run_command(
|
||||||
[py_exe(), "setup.py", "build_ext", "--have-ssl"]
|
[py_exe(), "setup.py", "build_ext", "--have-ssl"]
|
||||||
+ ["-l", "libpgcommon", "-l", "libpgport"]
|
+ ["-l", "libpgcommon", "-l", "libpgport"]
|
||||||
|
@ -320,25 +329,87 @@ def build_psycopg():
|
||||||
+ ['-I', base_dir() / r'openssl\include']
|
+ ['-I', base_dir() / r'openssl\include']
|
||||||
)
|
)
|
||||||
run_command([py_exe(), "setup.py", "build_py"])
|
run_command([py_exe(), "setup.py", "build_py"])
|
||||||
|
|
||||||
|
|
||||||
|
def patch_package_name():
|
||||||
|
"""Change the psycopg2 package name in the setup.py if required."""
|
||||||
|
conf = os.environ.get('CONFIGURATION', 'psycopg2')
|
||||||
|
if conf == 'psycopg2':
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info("changing package name to %s", conf)
|
||||||
|
|
||||||
|
with (package_dir() / 'setup.py').open() as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
# Replace the name of the package with what desired
|
||||||
|
rex = re.compile(r"""name=["']psycopg2["']""")
|
||||||
|
assert len(rex.findall(data)) == 1, rex.findall(data)
|
||||||
|
data = rex.sub(f'name="{conf}"', data)
|
||||||
|
|
||||||
|
with (package_dir() / 'setup.py').open('w') as f:
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
def build_binary_packages():
|
||||||
|
"""Create wheel/exe binary packages."""
|
||||||
|
os.chdir(package_dir())
|
||||||
|
|
||||||
|
add_pg_config_path()
|
||||||
|
|
||||||
|
# Build .exe packages for whom still use them
|
||||||
|
if os.environ['CONFIGURATION'] == 'psycopg2':
|
||||||
|
run_command([py_exe(), 'setup.py', 'bdist_wininst', "-d", dist_dir()])
|
||||||
|
|
||||||
|
# Build .whl packages
|
||||||
|
run_command([py_exe(), 'setup.py', 'bdist_wheel', "-d", dist_dir()])
|
||||||
|
|
||||||
|
|
||||||
|
def step_after_build():
|
||||||
|
if not is_wheel():
|
||||||
|
install_built_package()
|
||||||
|
else:
|
||||||
|
install_binary_package()
|
||||||
|
|
||||||
|
|
||||||
|
def install_built_package():
|
||||||
|
"""Install the package just built by setup build."""
|
||||||
|
os.chdir(package_dir())
|
||||||
|
|
||||||
|
# Install the psycopg just built
|
||||||
|
add_pg_config_path()
|
||||||
run_command([py_exe(), "setup.py", "install"])
|
run_command([py_exe(), "setup.py", "install"])
|
||||||
shutil.rmtree("psycopg2.egg-info")
|
shutil.rmtree("psycopg2.egg-info")
|
||||||
|
|
||||||
|
|
||||||
|
def install_binary_package():
|
||||||
|
"""Install the package from a packaged wheel."""
|
||||||
|
run_command(
|
||||||
|
[py_exe(), '-m', 'pip', 'install', '--no-index', '-f', dist_dir()]
|
||||||
|
+ [os.environ['CONFIGURATION']]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def add_pg_config_path():
|
||||||
|
"""Allow finding in the path the pg_config just built."""
|
||||||
|
pg_path = str(base_dir() / r'postgresql\bin')
|
||||||
|
if pg_path not in os.environ['PATH'].split(os.pathsep):
|
||||||
|
setenv('PATH', os.pathsep.join([pg_path, os.environ['PATH']]))
|
||||||
|
|
||||||
|
|
||||||
def step_before_test():
|
def step_before_test():
|
||||||
# Add PostgreSQL binaries to the path
|
print_psycopg2_version()
|
||||||
setenv('PATH', os.pathsep.join([str(pg_bin_dir()), os.environ['PATH']]))
|
|
||||||
|
|
||||||
# Create and setup PostgreSQL database for the tests
|
# Create and setup PostgreSQL database for the tests
|
||||||
run_command(['createdb', os.environ['PSYCOPG2_TESTDB']])
|
run_command([pg_bin_dir() / 'createdb', os.environ['PSYCOPG2_TESTDB']])
|
||||||
run_command(
|
run_command(
|
||||||
['psql', '-d', os.environ['PSYCOPG2_TESTDB']]
|
[pg_bin_dir() / 'psql', '-d', os.environ['PSYCOPG2_TESTDB']]
|
||||||
+ ['-c', "CREATE EXTENSION hstore"]
|
+ ['-c', "CREATE EXTENSION hstore"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def step_after_build():
|
def print_psycopg2_version():
|
||||||
# Print psycopg and libpq versions
|
"""Print psycopg2 and libpq versions installed."""
|
||||||
|
|
||||||
for expr in (
|
for expr in (
|
||||||
'psycopg2.__version__',
|
'psycopg2.__version__',
|
||||||
'psycopg2.__libpq_version__',
|
'psycopg2.__libpq_version__',
|
||||||
|
@ -349,6 +420,36 @@ def step_after_build():
|
||||||
|
|
||||||
|
|
||||||
def step_test_script():
|
def step_test_script():
|
||||||
|
check_libpq_version()
|
||||||
|
run_test_suite()
|
||||||
|
|
||||||
|
|
||||||
|
def check_libpq_version():
|
||||||
|
"""
|
||||||
|
Fail if the package installed is not using the expected libpq version.
|
||||||
|
"""
|
||||||
|
want_ver = tuple(map(int, os.environ['POSTGRES_VERSION'].split('_')))
|
||||||
|
want_ver = "%d%04d" % want_ver
|
||||||
|
got_ver = (
|
||||||
|
out_command(
|
||||||
|
[py_exe(), '-c']
|
||||||
|
+ ["import psycopg2; print(psycopg2.extensions.libpq_version())"]
|
||||||
|
)
|
||||||
|
.decode('ascii')
|
||||||
|
.rstrip()
|
||||||
|
)
|
||||||
|
assert want_ver == got_ver, "libpq version mismatch: %r != %r" % (
|
||||||
|
want_ver,
|
||||||
|
got_ver,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_suite():
|
||||||
|
# Remove this var, which would make badly a configured OpenSSL 1.1 work
|
||||||
|
os.environ.pop('OPENSSL_CONF', None)
|
||||||
|
|
||||||
|
# Run the unit test
|
||||||
|
os.chdir(package_dir())
|
||||||
run_command(
|
run_command(
|
||||||
[py_exe(), '-c']
|
[py_exe(), '-c']
|
||||||
+ ["import tests; tests.unittest.main(defaultTest='tests.test_suite')"]
|
+ ["import tests; tests.unittest.main(defaultTest='tests.test_suite')"]
|
||||||
|
@ -356,6 +457,72 @@ def step_test_script():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def step_on_success():
|
||||||
|
print_sha1_hashes()
|
||||||
|
if setup_ssh():
|
||||||
|
upload_packages()
|
||||||
|
|
||||||
|
|
||||||
|
def print_sha1_hashes():
|
||||||
|
"""
|
||||||
|
Print the packages sha1 so their integrity can be checked upon signing.
|
||||||
|
"""
|
||||||
|
logger.info("artifacts SHA1 hashes:")
|
||||||
|
|
||||||
|
os.chdir(package_dir() / 'dist')
|
||||||
|
run_command([which('sha1sum'), '-b', f'psycopg2-*/*'])
|
||||||
|
|
||||||
|
|
||||||
|
def setup_ssh():
|
||||||
|
"""
|
||||||
|
Configure ssh to upload built packages where they can be retrieved.
|
||||||
|
|
||||||
|
Return False if can't configure and upload shoould be skipped.
|
||||||
|
"""
|
||||||
|
# If we are not on the psycopg AppVeyor account, the environment variable
|
||||||
|
# REMOTE_KEY will not be decrypted. In that case skip uploading.
|
||||||
|
if os.environ['APPVEYOR_ACCOUNT_NAME'] != 'psycopg':
|
||||||
|
logger.warn("skipping artifact upload: you are not psycopg")
|
||||||
|
return False
|
||||||
|
|
||||||
|
pkey = os.environ.get('REMOTE_KEY', None)
|
||||||
|
if not pkey:
|
||||||
|
logger.warn("skipping artifact upload: no remote key")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Write SSH Private Key file from environment variable
|
||||||
|
pkey = pkey.replace(' ', '\n')
|
||||||
|
with (clone_dir() / 'id_rsa').open('w') as f:
|
||||||
|
f.write(
|
||||||
|
f"""\
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
{pkey}
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make a directory to please MinGW's version of ssh
|
||||||
|
ensure_dir(r"C:\MinGW\msys\1.0\home\appveyor\.ssh")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def upload_packages():
|
||||||
|
# Upload built artifacts
|
||||||
|
logger.info("uploading artifacts")
|
||||||
|
|
||||||
|
ssh_cmd = r"C:\MinGW\msys\1.0\bin\ssh -i %s -o UserKnownHostsFile=%s" % (
|
||||||
|
clone_dir() / "id_rsa",
|
||||||
|
clone_dir() / 'known_hosts',
|
||||||
|
)
|
||||||
|
|
||||||
|
os.chdir(package_dir())
|
||||||
|
run_command(
|
||||||
|
[r"C:\MinGW\msys\1.0\bin\rsync", "-avr"]
|
||||||
|
+ ["-e", ssh_cmd, "dist/", "upload@initd.org:"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def download(url, fn):
|
def download(url, fn):
|
||||||
"""Download a file locally"""
|
"""Download a file locally"""
|
||||||
logger.info("downloading %s", url)
|
logger.info("downloading %s", url)
|
||||||
|
@ -523,6 +690,42 @@ def build_dir():
|
||||||
return ensure_dir(rv)
|
return ensure_dir(rv)
|
||||||
|
|
||||||
|
|
||||||
|
def package_dir():
|
||||||
|
"""
|
||||||
|
Return the directory containing the psycopg code checkout dir
|
||||||
|
|
||||||
|
Building psycopg is clone_dir(), building the wheel packages is a submodule.
|
||||||
|
"""
|
||||||
|
return clone_dir() / 'psycopg2' if is_wheel() else clone_dir()
|
||||||
|
|
||||||
|
|
||||||
|
def is_wheel():
|
||||||
|
"""
|
||||||
|
Return whether we are building the wheel packages or just the extension.
|
||||||
|
"""
|
||||||
|
project_name = os.environ['APPVEYOR_PROJECT_NAME']
|
||||||
|
if project_name == 'psycopg2':
|
||||||
|
return False
|
||||||
|
elif project_name == 'psycopg2-wheels':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
raise Exception(f"unexpected project name: {project_name}")
|
||||||
|
|
||||||
|
|
||||||
|
def dist_dir():
|
||||||
|
return package_dir() / 'dist' / ('psycopg2-%s' % package_version())
|
||||||
|
|
||||||
|
|
||||||
|
def package_version():
|
||||||
|
with (package_dir() / 'setup.py').open() as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
m = re.search(
|
||||||
|
r"""^PSYCOPG_VERSION\s*=\s*['"](.*)['"]""", data, re.MULTILINE
|
||||||
|
)
|
||||||
|
return m.group(1)
|
||||||
|
|
||||||
|
|
||||||
def ensure_dir(dir):
|
def ensure_dir(dir):
|
||||||
if not isinstance(dir, Path):
|
if not isinstance(dir, Path):
|
||||||
dir = Path(dir)
|
dir = Path(dir)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user