# -*- coding: ISO-8859-1 -*- # setup.py: the distutils script # import os import shutil import subprocess import json import sys import platform from glob import glob from setuptools import setup, Extension # If you need to change anything, it should be enough to change setup.cfg. PACKAGE_NAME = 'sqlcipher3' VERSION = '0.5.2' # Mapping from Conan architectures to Python machine types CONAN_ARCHS = { 'x86_64': ['amd64', 'x86_64', 'x64'], 'x86': ['i386', 'i686', 'x86'], 'armv8': ['arm64', 'aarch64', 'aarch64_be', 'armv8b', 'armv8l'], 'ppc64le': ['ppc64le', 'powerpc'], 's390x': ['s390', 's390x'], } # define sqlite sources sources = glob("src/*.c") + ["src/sqlcipher/sqlite3.c"] include_dirs = ["./src"] # define packages packages = [PACKAGE_NAME] EXTENSION_MODULE_NAME = "._sqlite3" # Work around clang raising hard error for unused arguments if sys.platform == "darwin": os.environ['CFLAGS'] = "-Qunused-arguments" def get_arch() -> str: """Get the Conan compilation target architecture. If not explicitly set using the `SQLCIPHER3_COMPILE_TARGET` environment variable, this will be determined using the host machine's platform information. """ env_arch = os.getenv('SQLCIPHER3_COMPILE_TARGET', '') if env_arch: return env_arch if ( platform.architecture()[0] == '32bit' and platform.machine().lower() in (CONAN_ARCHS['x86'] + CONAN_ARCHS['x86_64']) ): return 'x86' for k, v in CONAN_ARCHS.items(): if platform.machine().lower() in v: return k raise RuntimeError('Unable to determine the compilation target architecture') def install_openssl(arch: str) -> dict: """Install openssl using Conan. """ settings = [] if platform.system() == 'Windows': settings.append('os=Windows') elif platform.system() == 'Darwin': settings.append('os=Macos') if arch == 'x86_64': settings.append('os.version=10.9') else: settings.append('os.version=11.0') settings.append('compiler=apple-clang') settings.append('compiler.libcxx=libc++') elif platform.system() == 'Linux': settings.append('os=Linux') settings.append(f'arch={arch}') build = ['missing'] if os.path.isdir('/lib') and any(e.startswith('libc.musl') for e in os.listdir('/lib')): # Need to compile openssl if musllinux build.append('openssl*') subprocess.run(['conan', 'profile', 'detect']) conan_output = os.path.join('conan_output', arch) result = subprocess.run([ 'conan', 'install', *[x for s in settings for x in ('-s', s)], *[x for b in build for x in ('-b', b)], '-of', conan_output, '--deployer=direct_deploy', '--format=json', '.' ], stdout=subprocess.PIPE).stdout.decode() conan_info = json.loads(result) return conan_info def fetch_openssl_dir(conan_info: dict) -> str: """Find directory of openssl. """ for dep in conan_info['graph']['nodes'].values(): if dep.get('name') == 'openssl': return dep.get('package_folder') def quote_argument(arg): q = '\\"' if sys.platform == 'win32' and sys.version_info < (3, 8) else '"' return q + arg + q define_macros = [ ('MODULE_NAME', quote_argument(PACKAGE_NAME + '.dbapi2')), ('ENABLE_FTS3', '1'), ('ENABLE_FTS3_PARENTHESIS', '1'), ('ENABLE_FTS4', '1'), ('ENABLE_FTS5', '1'), ('ENABLE_JSON1', '1'), ('ENABLE_LOAD_EXTENSION', '1'), ('ENABLE_RTREE', '1'), ('ENABLE_STAT4', '1'), ('ENABLE_UPDATE_DELETE_LIMIT', '1'), ('SOUNDEX', '1'), ('USE_URI', '1'), # Required for SQLCipher. ('SQLITE_HAS_CODEC', '1'), ('HAS_CODEC', '1'), ("SQLITE_TEMP_STORE", "2"), # Increase the maximum number of "host parameters". ("SQLITE_MAX_VARIABLE_NUMBER", "250000"), # Additional nice-to-have. ('SQLITE_DEFAULT_PAGE_SIZE', '4096'), ('SQLITE_DEFAULT_CACHE_SIZE', '-8000'), ] arch = get_arch() if arch == 'universal2': conan_info_x64 = install_openssl('x86_64') openssl_dir_x64 = fetch_openssl_dir(conan_info_x64) conan_info_arm = install_openssl('armv8') openssl_dir_arm = fetch_openssl_dir(conan_info_arm) openssl_dir_universal2 = openssl_dir_arm.replace('armv8', 'universal2') subprocess.run( [ 'python3', './lipo-dir-merge/lipo-dir-merge.py', openssl_dir_x64, openssl_dir_arm, openssl_dir_universal2 ] ) shutil.rmtree(openssl_dir_x64) shutil.move(openssl_dir_universal2, openssl_dir_x64) openssl_dir = openssl_dir_x64 else: conan_info = install_openssl(arch) openssl_dir = fetch_openssl_dir(conan_info) extra_link_args = [] if sys.platform != "win32": # Include math library, required for fts5, and crypto. extra_link_args.extend(["-lm", "-lcrypto"]) openssl_lib_path = os.path.join(openssl_dir, "lib") # Configure the compiler include_dirs.append(os.path.join(openssl_dir, "include")) define_macros.append(("inline", "__inline")) # Configure the linker if sys.platform == "win32": # https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md#linking-native-applications extra_link_args.append("WS2_32.LIB") extra_link_args.append("GDI32.LIB") extra_link_args.append("ADVAPI32.LIB") extra_link_args.append("CRYPT32.LIB") extra_link_args.append("USER32.LIB") extra_link_args.append('libcrypto.lib') else: extra_link_args.append('libcrypto.a') module = Extension( name=PACKAGE_NAME + EXTENSION_MODULE_NAME, sources=sources, define_macros=define_macros, library_dirs=[openssl_lib_path], include_dirs=include_dirs, extra_link_args=extra_link_args, language="c", ) with open("README.md", "r", encoding="utf-8") as fr: long_description = fr.read() if __name__ == "__main__": setup( name=PACKAGE_NAME, version=VERSION, description="DB-API 2.0 interface for SQLCipher 3.x", long_description=long_description, author="Charles Leifer", author_email="coleifer@gmail.com", license="zlib/libpng", platforms="ALL", url="https://github.com/coleifer/sqlcipher3", package_dir={PACKAGE_NAME: "sqlcipher3"}, packages=packages, ext_modules=[module], classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: zlib/libpng License", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Programming Language :: C", "Programming Language :: Python", "Topic :: Database :: Database Engines/Servers", "Topic :: Software Development :: Libraries :: Python Modules"], )