251 lines
8 KiB
Python
251 lines
8 KiB
Python
# -*- coding: ISO-8859-1 -*-
|
|
# setup.py: the distutils script
|
|
#
|
|
import os
|
|
import subprocess
|
|
import json
|
|
import sys
|
|
import shutil
|
|
import platform
|
|
from glob import glob
|
|
from setuptools import setup, Extension
|
|
from typing import Any, Optional
|
|
|
|
# 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"]
|
|
|
|
library_dirs = []
|
|
include_dirs = ["./src"]
|
|
|
|
|
|
def get_native_arch() -> str:
|
|
for k, v in CONAN_ARCHS.items():
|
|
if platform.machine().lower() in v:
|
|
return k
|
|
|
|
# Failover
|
|
return platform.machine().lower()
|
|
|
|
|
|
def get_arch() -> str:
|
|
arch_env = os.getenv("SQLCIPHER4_COMPILE_TARGET")
|
|
if isinstance(arch_env, str):
|
|
arch = arch_env
|
|
else:
|
|
arch = get_native_arch()
|
|
|
|
return arch
|
|
|
|
|
|
def install_openssl(arch: str) -> "dict[Any, Any]":
|
|
"""Install openssl using Conan."""
|
|
settings: list[str] = []
|
|
options: list[str] = []
|
|
|
|
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}")
|
|
options.append("openssl/*:no_zlib=True")
|
|
|
|
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", "-f"])
|
|
# Latest openssl need center2.conan.io instead of center.conan.io
|
|
subprocess.run(
|
|
["conan", "remote", "update", "conancenter", "--url=https://center2.conan.io"]
|
|
)
|
|
|
|
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)],
|
|
*[x for o in options for x in ("-o", o)],
|
|
"-of",
|
|
conan_output,
|
|
"--deployer=direct_deploy",
|
|
"--format=json",
|
|
".",
|
|
],
|
|
stdout=subprocess.PIPE,
|
|
).stdout.decode()
|
|
conan_info = json.loads(result)
|
|
|
|
return conan_info
|
|
|
|
|
|
def add_deps(conan_info: "dict[Any, Any]") -> "tuple[list[str], list[str]]":
|
|
"""Find directories of dependencies."""
|
|
library_dirs: list[str] = []
|
|
include_dirs: list[str] = []
|
|
for dep in conan_info["graph"]["nodes"].values():
|
|
package_folder = dep.get("package_folder")
|
|
if package_folder is None:
|
|
continue
|
|
|
|
library_dirs.append(os.path.join(package_folder, "lib"))
|
|
include_dirs.append(os.path.join(package_folder, "include"))
|
|
|
|
return library_dirs, include_dirs
|
|
|
|
|
|
def quote_argument(arg: str) -> str:
|
|
is_cibuildwheel = os.environ.get("CIBUILDWHEEL", "0") == "1"
|
|
|
|
if sys.platform == "win32" and (
|
|
(is_cibuildwheel and sys.version_info < (3, 7))
|
|
or (not is_cibuildwheel and sys.version_info < (3, 9))
|
|
):
|
|
q = '\\"'
|
|
else:
|
|
q = '"'
|
|
|
|
return q + arg + q
|
|
|
|
|
|
if __name__ == "__main__":
|
|
define_macros: "list[tuple[str, Optional[str]]]" = [
|
|
("MODULE_NAME", quote_argument("sqlcipher4.dbapi2")),
|
|
("SQLITE_ENABLE_FTS3", "1"),
|
|
("SQLITE_ENABLE_FTS3_PARENTHESIS", "1"),
|
|
("SQLITE_ENABLE_FTS4", "1"),
|
|
("SQLITE_ENABLE_FTS5", "1"),
|
|
("SQLITE_ENABLE_JSON1", "1"),
|
|
("SQLITE_ENABLE_LOAD_EXTENSION", "1"),
|
|
("SQLITE_ENABLE_RTREE", "1"),
|
|
("SQLITE_ENABLE_STAT4", "1"),
|
|
("SQLITE_ENABLE_UPDATE_DELETE_LIMIT", "1"),
|
|
("SQLITE_SOUNDEX", "1"),
|
|
("SQLITE_USE_URI", "1"),
|
|
# Required for SQLCipher.
|
|
("SQLITE_HAS_CODEC", "1"),
|
|
("SQLITE_TEMP_STORE", "2"),
|
|
("SQLITE_THREADSAFE", "1"),
|
|
("SQLITE_EXTRA_INIT", "sqlcipher_extra_init"),
|
|
("SQLITE_EXTRA_SHUTDOWN", "sqlcipher_extra_shutdown"),
|
|
("HAVE_STDINT_H", "1"),
|
|
# 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"),
|
|
("inline", "__inline"),
|
|
]
|
|
|
|
# Debian builds (and similar) should be able to use system OpenSSL
|
|
# instead of downloading OpenSSL via Conan.
|
|
use_system_openssl = os.environ.get("BOUQUIN_SYSTEM_OPENSSL", "0") == "1"
|
|
if use_system_openssl and sys.platform == "win32":
|
|
raise RuntimeError("BOUQUIN_SYSTEM_OPENSSL=1 is not supported on Windows")
|
|
|
|
# Configure the compiler
|
|
arch = get_arch()
|
|
if (not use_system_openssl) and arch == "universal2":
|
|
conan_info_x64 = install_openssl("x86_64")
|
|
conan_build_folder_x64: str = conan_info_x64["graph"]["nodes"]["0"][
|
|
"build_folder"
|
|
]
|
|
library_dirs_x64, include_dirs_x64 = add_deps(conan_info_x64)
|
|
conan_info_arm = install_openssl("armv8")
|
|
conan_build_folder_arm: str = conan_info_arm["graph"]["nodes"]["0"][
|
|
"build_folder"
|
|
]
|
|
library_dirs_arm, include_dirs_arm = add_deps(conan_info_arm)
|
|
|
|
if get_native_arch() == "x86_64":
|
|
lipo_dir_merge_src = conan_build_folder_x64
|
|
lipo_dir_merge_dst = conan_build_folder_arm
|
|
library_dirs = library_dirs_x64
|
|
include_dirs = include_dirs_x64
|
|
elif get_native_arch() == "armv8":
|
|
lipo_dir_merge_src = conan_build_folder_arm
|
|
lipo_dir_merge_dst = conan_build_folder_x64
|
|
library_dirs = library_dirs_arm
|
|
include_dirs = include_dirs_arm
|
|
else:
|
|
raise RuntimeError("Invalid arch: " + arch)
|
|
|
|
lipo_dir_merge_result = conan_build_folder_arm.replace("armv8", "universal2")
|
|
shutil.rmtree(lipo_dir_merge_result, ignore_errors=True)
|
|
|
|
subprocess.run(
|
|
[
|
|
"python3",
|
|
"-m",
|
|
"lipomerge",
|
|
lipo_dir_merge_src,
|
|
lipo_dir_merge_dst,
|
|
lipo_dir_merge_result,
|
|
]
|
|
)
|
|
|
|
shutil.rmtree(lipo_dir_merge_src)
|
|
shutil.move(lipo_dir_merge_result, lipo_dir_merge_src)
|
|
elif not use_system_openssl:
|
|
conan_info = install_openssl(arch)
|
|
library_dirs, include_dirs = add_deps(conan_info)
|
|
|
|
extra_compile_args: "list[str]" = (
|
|
["-Qunused-arguments"] if sys.platform == "darwin" else []
|
|
)
|
|
|
|
# Configure the linker
|
|
extra_link_args: "list[str]" = []
|
|
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:
|
|
# Include math library, required for fts5, and crypto.
|
|
extra_link_args.extend(["-lm", "-lcrypto"])
|
|
|
|
module = Extension(
|
|
name="sqlcipher4._sqlite3",
|
|
sources=sources,
|
|
define_macros=define_macros,
|
|
library_dirs=library_dirs,
|
|
include_dirs=include_dirs,
|
|
extra_compile_args=extra_compile_args,
|
|
extra_link_args=extra_link_args,
|
|
language="c",
|
|
)
|
|
|
|
setup(
|
|
# With pyproject.toml, all are not necessary except ext_modules
|
|
# However, they are kept for building python 3.6 wheels
|
|
name="sqlcipher4",
|
|
version="0.5.6",
|
|
package_dir={"sqlcipher4": "sqlcipher4"},
|
|
packages=["sqlcipher4"],
|
|
ext_modules=[module],
|
|
)
|