mirror of
https://github.com/hanzoai/pymssql.git
synced 2026-10-30 14:54:29 +00:00
290 lines
9.5 KiB
Python
290 lines
9.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Download and build FreeTDS library and build pymssql.
|
|
"""
|
|
|
|
import argparse
|
|
import multiprocessing
|
|
import os
|
|
from pathlib import Path
|
|
import platform
|
|
import shutil
|
|
from subprocess import check_call, check_output, STDOUT
|
|
import sys
|
|
import tarfile
|
|
|
|
|
|
def run(cmd, cwd=None, env=None, shell=True):
|
|
|
|
print(f"build.py: {cmd}")
|
|
check_call(str(cmd), cwd=str(cwd) if cwd else cwd, env=env, shell=shell, stderr=STDOUT)
|
|
|
|
|
|
def download(args):
|
|
|
|
args.ws_dir.mkdir(parents=True, exist_ok=True)
|
|
if args.freetds_version.lower() == 'latest':
|
|
args.freetds_version ='patched'
|
|
arcname = f"freetds-{args.freetds_version}.tar.gz"
|
|
url = f"{args.freetds_url.strip('/')}/{arcname}"
|
|
freetds_dest = args.ws_dir / arcname
|
|
if freetds_dest.exists() and not args.force_download:
|
|
print(f"{freetds_dest} already exists")
|
|
else:
|
|
print(f"downloading {url} to {freetds_dest}")
|
|
run(f"curl -sSf --retry 10 --max-time 10 {url} -o {freetds_dest}")
|
|
|
|
if platform.system() == 'Windows':
|
|
url = f"{args.iconv_url.strip('/')}/v{args.iconv_version}"
|
|
iconv_dest = args.ws_dir / "win-iconv.zip"
|
|
if iconv_dest.exists() and not args.force_download:
|
|
print(f"{iconv_dest} already exists")
|
|
else:
|
|
run(f"curl -sSf {url} -o {iconv_dest}")
|
|
else:
|
|
iconv_dest = None
|
|
|
|
print("downloading DONE")
|
|
return freetds_dest, iconv_dest
|
|
|
|
|
|
def build(args, freetds_archive):
|
|
|
|
tar = tarfile.open(freetds_archive)
|
|
topdir = tar.next().name
|
|
srcdir = args.ws_dir / f"{topdir}"
|
|
if srcdir.exists():
|
|
shutil.rmtree(srcdir)
|
|
tar.extractall(args.ws_dir)
|
|
tar.close()
|
|
if args.prefix is None:
|
|
args.prefix = args.ws_dir / f"{topdir}-bin"
|
|
|
|
blddir = args.ws_dir / f"{topdir}-build"
|
|
if blddir.exists():
|
|
shutil.rmtree(blddir)
|
|
blddir.mkdir(parents=True)
|
|
|
|
configure = srcdir / "configure"
|
|
configure = [ f'"{configure}"',
|
|
f"--prefix={args.prefix}",
|
|
"--enable-msdblib",
|
|
f"--with-tdsver={args.with_tdsver}",
|
|
#"--disable-apps",
|
|
"--disable-server",
|
|
"--disable-pool",
|
|
"--disable-odbc",
|
|
f"--with-openssl={args.with_openssl}",
|
|
"--with-gnutls=no",
|
|
]
|
|
if args.enable_krb5:
|
|
configure.append("--enable-krb5")
|
|
if args.static_freetds:
|
|
configure.extend(["--enable-static", "--disable-shared"])
|
|
else:
|
|
configure.extend(["--enable-shared", "--disable-static"])
|
|
cmd = ' '.join(configure)
|
|
env = os.environ.copy()
|
|
env.update(CFLAGS="-fPIC -Wno-incompatible-pointer-types")
|
|
run(cmd, cwd=blddir, env=env)
|
|
make = f"make -j {multiprocessing.cpu_count()}"
|
|
run(make, cwd=blddir, env=env)
|
|
make = "make install"
|
|
run(make, cwd=blddir, env=env)
|
|
|
|
|
|
def find_vcvarsall_env():
|
|
|
|
from setuptools import distutils
|
|
from distutils import _msvccompiler as _msvcc
|
|
from distutils.util import get_platform
|
|
|
|
plat_name = get_platform()
|
|
CIBW_ARCHS_WINDOWS = os.environ.get("CIBW_ARCHS_WINDOWS")
|
|
if CIBW_ARCHS_WINDOWS == 'x86':
|
|
plat_name = 'win32'
|
|
|
|
try:
|
|
plat_spec = _msvcc.PLAT_TO_VCVARS[plat_name]
|
|
except AttributeError:
|
|
# setuptools removed PLAT_TO_VCVARS in 74.0
|
|
PLAT_TO_VCVARS = {
|
|
'win32' : 'x86',
|
|
'win-amd64' : 'x86_amd64',
|
|
'win-arm32' : 'x86_arm',
|
|
'win-arm64' : 'x86_arm64'
|
|
}
|
|
plat_spec = PLAT_TO_VCVARS[plat_name]
|
|
vcvarsall, _ = _msvcc._find_vcvarsall(plat_spec)
|
|
cmd = f'(call "{vcvarsall}" {plat_spec}>nul)&&"{sys.executable}" -c "import os;print(repr(os.environ))"'
|
|
env = check_output(cmd, shell=True)
|
|
env = eval(env.decode('ascii').strip('environ'))
|
|
return env
|
|
|
|
|
|
def build_windows(args, freetds_archive, iconv_archive):
|
|
|
|
from zipfile import ZipFile
|
|
|
|
wiconv = args.ws_dir / "win-iconv"
|
|
if wiconv.exists():
|
|
shutil.rmtree(wiconv)
|
|
wiconv.mkdir(parents=True)
|
|
print(f"extracting {iconv_archive.name} -> {wiconv}")
|
|
with ZipFile(iconv_archive) as zipf:
|
|
for m in zipf.namelist():
|
|
fn = m.split('/', 1)[1]
|
|
if fn:
|
|
(wiconv / fn).write_bytes(zipf.read(m))
|
|
|
|
#env = find_vcvarsall_env()
|
|
env = os.environ
|
|
|
|
cmd = f'"{args.cmake}" -G "NMake Makefiles" ' \
|
|
'-DCMAKE_BUILD_TYPE=Release -DBUILD_STATIC=on -DBUILD_SHARED=off ' \
|
|
'-DBUILD_EXECUTABLE=off -DBUILD_TEST=off ' \
|
|
'.'
|
|
print(f"running cmake in {wiconv}")
|
|
run(cmd, cwd=wiconv, env=env)
|
|
print(f"running nmake in {wiconv}")
|
|
run("nmake", cwd=wiconv, env=env)
|
|
|
|
tar = tarfile.open(freetds_archive)
|
|
topdir = tar.next().name
|
|
srcdir = args.ws_dir / f"{topdir}"
|
|
if srcdir.exists():
|
|
shutil.rmtree(srcdir)
|
|
tar.extractall(args.ws_dir)
|
|
tar.close()
|
|
if args.prefix is None:
|
|
args.prefix = args.ws_dir / f"{topdir}-bin"
|
|
|
|
blddir = args.ws_dir / f"{topdir}-build"
|
|
if blddir.exists():
|
|
shutil.rmtree(blddir)
|
|
blddir.mkdir(parents=True)
|
|
|
|
os.makedirs(blddir / "lib", exist_ok=True)
|
|
shutil.copy(wiconv / "iconv.h", blddir)
|
|
shutil.copy(wiconv / "iconv.lib", blddir / "lib")
|
|
|
|
krb5 = "ON" if args.enable_krb5 else "OFF"
|
|
cmd = f'"{args.cmake}" -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release " \
|
|
"-DWITH_OPENSSL=ON -DENABLE_KRB5={krb5} -DCMAKE_INSTALL_PREFIX="{args.prefix}" "{srcdir}"'
|
|
env["PATH"] += f";{args.msys}"
|
|
run(cmd, cwd=blddir, env=env)
|
|
|
|
run("nmake install", cwd=blddir, env=env)
|
|
|
|
|
|
def freetds_version(freetds_version):
|
|
|
|
if freetds_version:
|
|
return freetds_version
|
|
|
|
freetds_version = "latest"
|
|
|
|
pyproject_toml = Path(__file__).parent.parent / "pyproject.toml"
|
|
if not pyproject_toml.exists():
|
|
print("pyproject.toml not found, use latest FreeTDS")
|
|
return freetds_version
|
|
|
|
try:
|
|
from tomllib import loads
|
|
except ModuleNotFoundError:
|
|
from tomli import loads
|
|
except ModuleNotFoundError:
|
|
print("Neither tomllib no tomli module found, try latest FreeTDS.")
|
|
return freetds_version
|
|
|
|
pyproject = loads(pyproject_toml.read_text())
|
|
try:
|
|
freetds_version = pyproject['tool']['freetds']['version_for_pypi_wheels']
|
|
except:
|
|
pass
|
|
return freetds_version
|
|
|
|
|
|
def parse_args(argv):
|
|
|
|
parser = argparse.ArgumentParser(description=(__doc__))
|
|
a = parser.add_argument
|
|
|
|
a('-f', '--force-download', action='store_true',
|
|
help="force archive download")
|
|
a('-u', '--freetds-url', default="https://www.freetds.org/files/stable/",
|
|
help="URL to download FreeTDS archive")
|
|
a('-v', '--freetds-version', default='', type=freetds_version,
|
|
help="""Specific FreeTDS version to build or 'latest'.
|
|
If not set, try to read from pyproject.toml""")
|
|
if platform.system() != 'Windows':
|
|
a('-t', '--with-tdsver', choices=['5.0', '7.1', '7.2', '7.3', '7.4', 'auto'], default='auto',
|
|
help="TDS protocol version")
|
|
a('-o', '--with-openssl', choices=['yes', 'no'], default='yes',
|
|
help="Build FreeTDS with or without OpenSSL support, default is 'yes'")
|
|
a('-S', '--static-freetds', action='store_true',
|
|
help="build FreeTDS staticly")
|
|
a('-k', '--enable-krb5', dest="enable_krb5", action='store_true',
|
|
help="enable krb5 support")
|
|
|
|
base = Path('~/freetds').expanduser()
|
|
a('-w', '--ws-dir', default=base, type=Path,
|
|
help="workspace directory for building FreeTDS")
|
|
a('-p', '--prefix', default=None, type=lambda x: Path(x) if x else None,
|
|
help="prefix for installing FreeTDS, default is WS_DIR/prefix")
|
|
|
|
a('-W', '--wheel', action='store_true',
|
|
help="Build wheel.")
|
|
a('-d', '--dist-dir', default=Path('./dist'), type=Path,
|
|
help="where to put pymssql wheel, default is './dist'")
|
|
a('-s', '--sdist', action='store_true',
|
|
help="build sdist archive")
|
|
|
|
if platform.system() == 'Windows':
|
|
a('-U', '--iconv-url', default="https://codeload.github.com/win-iconv/win-iconv/zip",
|
|
help="URL to download win-iconv.zip archive for build on Windows")
|
|
a('-V', '--iconv-version', default='0.0.8',
|
|
help="FreeTDS version to build")
|
|
a('-m', '--msys', type=Path, default=Path("c:/tools/msys64/usr/bin"),
|
|
help="Msys binaries installation directory")
|
|
a('--cmake', type=Path, default=Path("C:/Program Files/CMake/bin/cmake.exe"),
|
|
help="cmake executable for building FreeTDS")
|
|
|
|
args = parser.parse_args(argv)
|
|
return args
|
|
|
|
|
|
def main(argv):
|
|
|
|
args = parse_args(argv)
|
|
args.ws_dir = args.ws_dir.absolute()
|
|
if args.prefix is not None:
|
|
args.prefix = args.prefix.absolute()
|
|
|
|
freetds_archive, iconv_archive = download(args)
|
|
|
|
if platform.system() == 'Windows':
|
|
os.environ["PATH"] += f";{args.msys}"
|
|
build_windows(args, freetds_archive, iconv_archive)
|
|
else:
|
|
build(args, freetds_archive)
|
|
|
|
if args.wheel:
|
|
args.dist_dir = args.dist_dir.absolute()
|
|
env = os.environ.copy()
|
|
env.update(PYMSSQL_FREETDS=f"{args.prefix}")
|
|
run(f"{sys.executable} -m pip wheel . -w {args.dist_dir}", shell=True, env=env)
|
|
if args.sdist:
|
|
fmt = 'zip' if platform.system() == 'Windows' else 'gztar'
|
|
run(f"{sys.executable} setup.py sdist --formats={fmt} -d {args.dist_dir}", shell=True, env=env)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
try:
|
|
main(sys.argv[1:])
|
|
except Exception as exc:
|
|
print(f"Build failed: {exc}")
|
|
sys.exit(1)
|
|
|