summaryrefslogtreecommitdiffstats
path: root/psycopg_c/setup.py
blob: c6da3a1591d8263919b84110cae2cea8e67ba11b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env python3
"""
PostgreSQL database adapter for Python - optimisation package
"""

# Copyright (C) 2020 The Psycopg Team

import os
import re
import sys
import subprocess as sp

from setuptools import setup, Extension
from distutils.command.build_ext import build_ext
from distutils import log

# Move to the directory of setup.py: executing this file from another location
# (e.g. from the project root) will fail
here = os.path.abspath(os.path.dirname(__file__))
if os.path.abspath(os.getcwd()) != here:
    os.chdir(here)

with open("psycopg_c/version.py") as f:
    data = f.read()
    m = re.search(r"""(?m)^__version__\s*=\s*['"]([^'"]+)['"]""", data)
    if m is None:
        raise Exception(f"cannot find version in {f.name}")
    version = m.group(1)


def get_config(what: str) -> str:
    pg_config = "pg_config"
    try:
        out = sp.run([pg_config, f"--{what}"], stdout=sp.PIPE, check=True)
    except Exception as e:
        log.error(f"couldn't run {pg_config!r} --{what}: %s", e)
        raise
    else:
        return out.stdout.strip().decode()


class psycopg_build_ext(build_ext):
    def finalize_options(self) -> None:
        self._setup_ext_build()
        super().finalize_options()

    def _setup_ext_build(self) -> None:
        cythonize = None

        # In the sdist there are not .pyx, only c, so we don't need Cython.
        # Otherwise Cython is a requirement and it is used to compile pyx to c.
        if os.path.exists("psycopg_c/_psycopg.pyx"):
            from Cython.Build import cythonize

        # Add include and lib dir for the libpq.
        includedir = get_config("includedir")
        libdir = get_config("libdir")
        for ext in self.distribution.ext_modules:
            ext.include_dirs.append(includedir)
            ext.library_dirs.append(libdir)

            if sys.platform == "win32":
                # For __imp_htons and others
                ext.libraries.append("ws2_32")

        if cythonize is not None:
            for ext in self.distribution.ext_modules:
                for i in range(len(ext.sources)):
                    base, fext = os.path.splitext(ext.sources[i])
                    if fext == ".c" and os.path.exists(base + ".pyx"):
                        ext.sources[i] = base + ".pyx"

            self.distribution.ext_modules = cythonize(
                self.distribution.ext_modules,
                language_level=3,
                compiler_directives={
                    "always_allow_keywords": False,
                },
                annotate=False,  # enable to get an html view of the C module
            )
        else:
            self.distribution.ext_modules = [pgext, pqext]


# MSVC requires an explicit "libpq"
libpq = "pq" if sys.platform != "win32" else "libpq"

# Some details missing, to be finished by psycopg_build_ext.finalize_options
pgext = Extension(
    "psycopg_c._psycopg",
    [
        "psycopg_c/_psycopg.c",
        "psycopg_c/types/numutils.c",
    ],
    libraries=[libpq],
    include_dirs=[],
)

pqext = Extension(
    "psycopg_c.pq",
    ["psycopg_c/pq.c"],
    libraries=[libpq],
    include_dirs=[],
)

setup(
    version=version,
    ext_modules=[pgext, pqext],
    cmdclass={"build_ext": psycopg_build_ext},
)