1#!/usr/bin/env python3
2"""
3PostgreSQL database adapter for Python - optimisation package
4"""
5
6# Copyright (C) 2020-2021 The Psycopg Team
7
8import os
9import re
10import sys
11import subprocess as sp
12
13from setuptools import setup, Extension
14from distutils.command.build_ext import build_ext
15from distutils import log
16
17# Move to the directory of setup.py: executing this file from another location
18# (e.g. from the project root) will fail
19here = os.path.abspath(os.path.dirname(__file__))
20if os.path.abspath(os.getcwd()) != here:
21    os.chdir(here)
22
23with open("psycopg_c/version.py") as f:
24    data = f.read()
25    m = re.search(r"""(?m)^__version__\s*=\s*['"]([^'"]+)['"]""", data)
26    if m is None:
27        raise Exception(f"cannot find version in {f.name}")
28    version = m.group(1)
29
30
31def get_config(what: str) -> str:
32    pg_config = "pg_config"
33    try:
34        out = sp.run([pg_config, f"--{what}"], stdout=sp.PIPE, check=True)
35    except Exception as e:
36        log.error(f"couldn't run {pg_config!r} --{what}: %s", e)
37        raise
38    else:
39        return out.stdout.strip().decode()
40
41
42class psycopg_build_ext(build_ext):
43    def finalize_options(self) -> None:
44        self._setup_ext_build()
45        super().finalize_options()
46
47    def _setup_ext_build(self) -> None:
48        cythonize = None
49
50        # In the sdist there are not .pyx, only c, so we don't need Cython
51        # Otherwise Cython is a requirement and is be used to compile pyx to c
52        if os.path.exists("psycopg_c/_psycopg.pyx"):
53            from Cython.Build import cythonize
54
55        # Add include and lib dir for the libpq.
56        includedir = get_config("includedir")
57        libdir = get_config("libdir")
58        for ext in self.distribution.ext_modules:
59            ext.include_dirs.append(includedir)
60            ext.library_dirs.append(libdir)
61
62            if sys.platform == "win32":
63                # For __imp_htons and others
64                ext.libraries.append("ws2_32")
65
66        if cythonize is not None:
67            for ext in self.distribution.ext_modules:
68                for i in range(len(ext.sources)):
69                    base, fext = os.path.splitext(ext.sources[i])
70                    if fext == ".c" and os.path.exists(base + ".pyx"):
71                        ext.sources[i] = base + ".pyx"
72
73            self.distribution.ext_modules = cythonize(
74                self.distribution.ext_modules,
75                language_level=3,
76                compiler_directives={
77                    "always_allow_keywords": False,
78                },
79                annotate=False,  # enable to get an html view of the C module
80            )
81        else:
82            self.distribution.ext_modules = [pgext, pqext]
83
84
85# MSVC requires an explicit "libpq"
86libpq = "pq" if sys.platform != "win32" else "libpq"
87
88# Some details missing, to be finished by psycopg_build_ext.finalize_options
89pgext = Extension(
90    "psycopg_c._psycopg",
91    [
92        "psycopg_c/_psycopg.c",
93        "psycopg_c/types/numutils.c",
94    ],
95    libraries=[libpq],
96    include_dirs=[],
97)
98
99pqext = Extension(
100    "psycopg_c.pq",
101    ["psycopg_c/pq.c"],
102    libraries=[libpq],
103    include_dirs=[],
104)
105
106setup(
107    version=version,
108    ext_modules=[pgext, pqext],
109    cmdclass={"build_ext": psycopg_build_ext},
110)
111