xref: /petsc/src/binding/petsc4py/setup.py (revision 8577b683712d1cca1e9b8fdaa9ae028364224dad)
11b37a2a7SPierre Jolivet#!/usr/bin/env python3
25808f684SSatish Balay# Author:  Lisandro Dalcin
35808f684SSatish Balay# Contact: dalcinl@gmail.com
45808f684SSatish Balay
55808f684SSatish Balayimport re
6fb83dddaSLisandro Dalcinimport os
7fb83dddaSLisandro Dalcinimport sys
89fb6d20cSLisandro Dalcinimport warnings
95808f684SSatish Balay
105808f684SSatish Balaytry:
115808f684SSatish Balay    import setuptools
125808f684SSatish Balayexcept ImportError:
135808f684SSatish Balay    setuptools = None
145808f684SSatish Balay
15f46a955bSLisandro Dalcintopdir = os.path.abspath(os.path.dirname(__file__))
16f46a955bSLisandro Dalcinsys.path.insert(0, os.path.join(topdir, 'conf'))
17f46a955bSLisandro Dalcin
185808f684SSatish Balaypyver = sys.version_info[:2]
19aab42556SSatish Balayif pyver < (3, 6):
20aab42556SSatish Balay    raise RuntimeError('Python version 3.6 or higher is required')
211e83877aSSatish Balay
22f46a955bSLisandro DalcinPNAME = 'PETSc'
23f46a955bSLisandro DalcinEMAIL = 'petsc-maint@mcs.anl.gov'
24f46a955bSLisandro DalcinPLIST = [PNAME]
25fb83dddaSLisandro Dalcin
265808f684SSatish Balay# --------------------------------------------------------------------
275808f684SSatish Balay# Metadata
285808f684SSatish Balay# --------------------------------------------------------------------
295808f684SSatish Balay
307b8d1e39SLisandro Dalcinpy_limited_api = (3, 10)
317b8d1e39SLisandro Dalcin
326f336411SStefano Zampini
33f46a955bSLisandro Dalcindef F(string):
34f46a955bSLisandro Dalcin    return string.format(
35f46a955bSLisandro Dalcin        Name=PNAME,
36f46a955bSLisandro Dalcin        name=PNAME.lower(),
37f46a955bSLisandro Dalcin        pyname=PNAME.lower() + '4py',
38f46a955bSLisandro Dalcin    )
395808f684SSatish Balay
406f336411SStefano Zampini
41f46a955bSLisandro Dalcindef get_name():
42f46a955bSLisandro Dalcin    return F('{pyname}')
435808f684SSatish Balay
446f336411SStefano Zampini
45f46a955bSLisandro Dalcindef get_version():
46f46a955bSLisandro Dalcin    try:
47f46a955bSLisandro Dalcin        return get_version.result
48f46a955bSLisandro Dalcin    except AttributeError:
49f46a955bSLisandro Dalcin        pass
50f46a955bSLisandro Dalcin    pkg_init_py = os.path.join(F('{pyname}'), '__init__.py')
51f46a955bSLisandro Dalcin    with open(os.path.join(topdir, 'src', pkg_init_py)) as f:
525808f684SSatish Balay        m = re.search(r"__version__\s*=\s*'(.*)'", f.read())
53f46a955bSLisandro Dalcin    version = m.groups()[0]
54f46a955bSLisandro Dalcin    get_version.result = version
55f46a955bSLisandro Dalcin    return version
565808f684SSatish Balay
576f336411SStefano Zampini
585808f684SSatish Balaydef description():
59f46a955bSLisandro Dalcin    return F('{Name} for Python')
60fb83dddaSLisandro Dalcin
616f336411SStefano Zampini
62fb83dddaSLisandro Dalcindef long_description():
635808f684SSatish Balay    with open(os.path.join(topdir, 'DESCRIPTION.rst')) as f:
645808f684SSatish Balay        return f.read()
655808f684SSatish Balay
666f336411SStefano Zampini
67f46a955bSLisandro Dalcinurl = F('https://gitlab.com/{name}/{name}')
68f46a955bSLisandro Dalcinpypiroot = F('https://pypi.io/packages/source')
69f46a955bSLisandro Dalcinpypislug = F('{pyname}')[0] + F('/{pyname}')
70f46a955bSLisandro Dalcintarball = F('{pyname}-%s.tar.gz' % get_version())
71f46a955bSLisandro Dalcindownload = '/'.join([pypiroot, pypislug, tarball])
725808f684SSatish Balay
73fb83dddaSLisandro Dalcinclassifiers = """
74fb83dddaSLisandro DalcinOperating System :: POSIX
75fb83dddaSLisandro DalcinIntended Audience :: Developers
76fb83dddaSLisandro DalcinIntended Audience :: Science/Research
77fb83dddaSLisandro DalcinProgramming Language :: C
78fb83dddaSLisandro DalcinProgramming Language :: C++
79fb83dddaSLisandro DalcinProgramming Language :: Cython
80fb83dddaSLisandro DalcinProgramming Language :: Python
81fb83dddaSLisandro DalcinProgramming Language :: Python :: 3
82fb83dddaSLisandro DalcinProgramming Language :: Python :: Implementation :: CPython
83fb83dddaSLisandro DalcinTopic :: Scientific/Engineering
84fb83dddaSLisandro DalcinTopic :: Software Development :: Libraries :: Python Modules
85fb83dddaSLisandro DalcinDevelopment Status :: 5 - Production/Stable
86f46a955bSLisandro Dalcin""".strip().split('\n')
875808f684SSatish Balay
88fb83dddaSLisandro Dalcinkeywords = """
89fb83dddaSLisandro Dalcinscientific computing
90fb83dddaSLisandro Dalcinparallel computing
91fb83dddaSLisandro DalcinMPI
92f46a955bSLisandro Dalcin""".strip().split('\n')
935808f684SSatish Balay
94fb83dddaSLisandro Dalcinplatforms = """
95fb83dddaSLisandro DalcinPOSIX
96fb83dddaSLisandro DalcinLinux
97fb83dddaSLisandro DalcinmacOS
98fb83dddaSLisandro DalcinFreeBSD
99f46a955bSLisandro Dalcin""".strip().split('\n')
100fb83dddaSLisandro Dalcin
101fb83dddaSLisandro Dalcinmetadata = {
102f46a955bSLisandro Dalcin    'name': get_name(),
103f46a955bSLisandro Dalcin    'version': get_version(),
104fb83dddaSLisandro Dalcin    'description': description(),
105fb83dddaSLisandro Dalcin    'long_description': long_description(),
106fb83dddaSLisandro Dalcin    'url': url,
107fb83dddaSLisandro Dalcin    'download_url': download,
108f46a955bSLisandro Dalcin    'classifiers': classifiers,
109f46a955bSLisandro Dalcin    'keywords': keywords + PLIST,
110fb83dddaSLisandro Dalcin    'license': 'BSD-2-Clause',
111f46a955bSLisandro Dalcin    'platforms': platforms,
112fb83dddaSLisandro Dalcin    'author': 'Lisandro Dalcin',
113fb83dddaSLisandro Dalcin    'author_email': 'dalcinl@gmail.com',
114f46a955bSLisandro Dalcin    'maintainer': F('{Name} Team'),
115f46a955bSLisandro Dalcin    'maintainer_email': EMAIL,
116fb83dddaSLisandro Dalcin}
1176f336411SStefano Zampinimetadata.update(
1186f336411SStefano Zampini    {
119fb83dddaSLisandro Dalcin        'requires': ['numpy'],
1206f336411SStefano Zampini    }
1216f336411SStefano Zampini)
122fb83dddaSLisandro Dalcin
123fb83dddaSLisandro Dalcinmetadata_extra = {
124833126bdSLisandro Dalcin    'long_description_content_type': 'text/x-rst',
125fb83dddaSLisandro Dalcin}
1265808f684SSatish Balay
1277b8d1e39SLisandro Dalcindef get_build_pysabi():
128*30ff0ac2SLisandro Dalcin    abi = os.environ.get("PETSC4PY_BUILD_PYSABI", "").lower()
1297b8d1e39SLisandro Dalcin    if abi and sys.implementation.name == "cpython":
130*30ff0ac2SLisandro Dalcin        if abi in {"false", "no", "off", "n", "0"}:
131*30ff0ac2SLisandro Dalcin            return None
132*30ff0ac2SLisandro Dalcin        if abi in {"true", "yes", "on", "y", "1"} | {"abi3"}:
1337b8d1e39SLisandro Dalcin            return py_limited_api
1347b8d1e39SLisandro Dalcin        if abi.startswith("cp"):
1357b8d1e39SLisandro Dalcin            abi = abi[2:]
1367b8d1e39SLisandro Dalcin        if "." in abi:
1377b8d1e39SLisandro Dalcin            x, y = abi.split(".")
1387b8d1e39SLisandro Dalcin        else:
1397b8d1e39SLisandro Dalcin            x, y = abi[0], abi[1:]
1407b8d1e39SLisandro Dalcin        return (int(x), int(y))
1417b8d1e39SLisandro Dalcin    return None
1427b8d1e39SLisandro Dalcin
1435808f684SSatish Balay# --------------------------------------------------------------------
1445808f684SSatish Balay# Extension modules
1455808f684SSatish Balay# --------------------------------------------------------------------
1465808f684SSatish Balay
1476f336411SStefano Zampini
148f46a955bSLisandro Dalcindef sources():
1496f336411SStefano Zampini    src = {
1506f336411SStefano Zampini        'source': F('{pyname}/{Name}.pyx'),
1516f336411SStefano Zampini        'depends': [
152f46a955bSLisandro Dalcin            F('{pyname}/*.pyx'),
153f46a955bSLisandro Dalcin            F('{pyname}/*.pxd'),
154f46a955bSLisandro Dalcin            F('{pyname}/{Name}/*.pyx'),
155f46a955bSLisandro Dalcin            F('{pyname}/{Name}/*.pxd'),
156f46a955bSLisandro Dalcin            F('{pyname}/{Name}/*.pxi'),
157f46a955bSLisandro Dalcin        ],
1586f336411SStefano Zampini        'workdir': 'src',
1596f336411SStefano Zampini    }
160f46a955bSLisandro Dalcin    return [src]
161f46a955bSLisandro Dalcin
1626f336411SStefano Zampini
163fb83dddaSLisandro Dalcindef extensions():
1648885bf16SLisandro Dalcin    from os import walk
1655808f684SSatish Balay    from glob import glob
1668885bf16SLisandro Dalcin    from os.path import join
1676f336411SStefano Zampini
168f46a955bSLisandro Dalcin    #
1695808f684SSatish Balay    depends = []
170f46a955bSLisandro Dalcin    glob_join = lambda *args: glob(join(*args))
1716f336411SStefano Zampini    for pth, _, _ in walk('src'):
1728885bf16SLisandro Dalcin        depends += glob_join(pth, '*.h')
1738885bf16SLisandro Dalcin        depends += glob_join(pth, '*.c')
174f46a955bSLisandro Dalcin    for pkg in map(str.lower, reversed(PLIST)):
175f46a955bSLisandro Dalcin        if (pkg.upper() + '_DIR') in os.environ:
176f46a955bSLisandro Dalcin            pd = os.environ[pkg.upper() + '_DIR']
1778885bf16SLisandro Dalcin            pa = os.environ.get('PETSC_ARCH', '')
1788885bf16SLisandro Dalcin            depends += glob_join(pd, 'include', '*.h')
179f46a955bSLisandro Dalcin            depends += glob_join(pd, 'include', pkg, 'private', '*.h')
180f46a955bSLisandro Dalcin            depends += glob_join(pd, pa, 'include', '%sconf.h' % pkg)
181f46a955bSLisandro Dalcin    #
182f46a955bSLisandro Dalcin    include_dirs = []
1830f51ee0fSAlexei Colin    numpy_include = os.environ.get('NUMPY_INCLUDE')
1840f51ee0fSAlexei Colin    if numpy_include is not None:
1850f51ee0fSAlexei Colin        numpy_includes = [numpy_include]
1860f51ee0fSAlexei Colin    else:
1875808f684SSatish Balay        try:
1885808f684SSatish Balay            import numpy
1896f336411SStefano Zampini
1905808f684SSatish Balay            numpy_includes = [numpy.get_include()]
1915808f684SSatish Balay        except ImportError:
1925808f684SSatish Balay            numpy_includes = []
193f46a955bSLisandro Dalcin    include_dirs.extend(numpy_includes)
194f46a955bSLisandro Dalcin    if F('{pyname}') != 'petsc4py':
195f46a955bSLisandro Dalcin        try:
196f46a955bSLisandro Dalcin            import petsc4py
1976f336411SStefano Zampini
198f46a955bSLisandro Dalcin            petsc4py_includes = [petsc4py.get_include()]
199f46a955bSLisandro Dalcin        except ImportError:
200f46a955bSLisandro Dalcin            petsc4py_includes = []
201f46a955bSLisandro Dalcin        include_dirs.extend(petsc4py_includes)
202f46a955bSLisandro Dalcin    #
2036f336411SStefano Zampini    ext = {
2046f336411SStefano Zampini        'name': F('{pyname}.lib.{Name}'),
2056f336411SStefano Zampini        'sources': [F('src/{pyname}/{Name}.c')],
2066f336411SStefano Zampini        'depends': depends,
2076f336411SStefano Zampini        'include_dirs': [
208f46a955bSLisandro Dalcin            'src',
209f46a955bSLisandro Dalcin            F('src/{pyname}/include'),
2106f336411SStefano Zampini        ]
2116f336411SStefano Zampini        + include_dirs,
2126f336411SStefano Zampini        'define_macros': [
213fb83dddaSLisandro Dalcin            ('MPICH_SKIP_MPICXX', 1),
214fb83dddaSLisandro Dalcin            ('OMPI_SKIP_MPICXX', 1),
215f46a955bSLisandro Dalcin            ('NPY_NO_DEPRECATED_API', 'NPY_1_7_API_VERSION'),
216fb83dddaSLisandro Dalcin        ],
2176f336411SStefano Zampini    }
218f46a955bSLisandro Dalcin    return [ext]
2195808f684SSatish Balay
2206f336411SStefano Zampini
2215808f684SSatish Balay# --------------------------------------------------------------------
2225808f684SSatish Balay# Setup
2235808f684SSatish Balay# --------------------------------------------------------------------
2245808f684SSatish Balay
2256f336411SStefano Zampini
226f8c80548SLisandro Dalcindef get_release():
227f46a955bSLisandro Dalcin    suffix = os.path.join('src', 'binding', F('{pyname}'))
228f46a955bSLisandro Dalcin    if not topdir.endswith(os.path.join(os.path.sep, suffix)):
229f09792b6SLisandro Dalcin        return True
230f8c80548SLisandro Dalcin    release = 1
231f8c80548SLisandro Dalcin    rootdir = os.path.abspath(os.path.join(topdir, *[os.path.pardir] * 3))
232f46a955bSLisandro Dalcin    version_h = os.path.join(rootdir, 'include', F('{name}version.h'))
233f46a955bSLisandro Dalcin    release_macro = '%s_VERSION_RELEASE' % F('{name}').upper()
2346f336411SStefano Zampini    version_re = re.compile(r'#define\s+%s\s+([-]*\d+)' % release_macro)
235f8c80548SLisandro Dalcin    if os.path.exists(version_h) and os.path.isfile(version_h):
236f8c80548SLisandro Dalcin        with open(version_h, 'r') as f:
237f8c80548SLisandro Dalcin            release = int(version_re.search(f.read()).groups()[0])
238f8c80548SLisandro Dalcin    return bool(release)
239f8c80548SLisandro Dalcin
2406f336411SStefano Zampini
241f8c80548SLisandro Dalcindef requires(pkgname, major, minor, release=True):
242f8c80548SLisandro Dalcin    minor = minor + int(not release)
243f8c80548SLisandro Dalcin    devel = '' if release else '.dev0'
2446f336411SStefano Zampini    vmin = f'{major}.{minor}{devel}'
2456f336411SStefano Zampini    vmax = f'{major}.{minor+1}'
2466f336411SStefano Zampini    return f'{pkgname}>={vmin},<{vmax}'
2476f336411SStefano Zampini
248f8c80548SLisandro Dalcin
2495808f684SSatish Balaydef run_setup():
2502149b0eaSStefano Zampini    is_sdist = 'sdist' in sys.argv
2515808f684SSatish Balay    setup_args = metadata.copy()
252f8c80548SLisandro Dalcin    vstr = setup_args['version'].split('.')[:2]
253f8c80548SLisandro Dalcin    x, y = tuple(map(int, vstr))
254f8c80548SLisandro Dalcin    release = get_release()
255f8c80548SLisandro Dalcin    if not release:
2566f336411SStefano Zampini        setup_args['version'] = '%d.%d.0.dev0' % (x, y + 1)
2575808f684SSatish Balay    if setuptools:
2589fb6d20cSLisandro Dalcin        warnings.filterwarnings(
2599fb6d20cSLisandro Dalcin            'ignore', message=r'.*fetch_build_eggs', module='setuptools'
2609fb6d20cSLisandro Dalcin        )
2615808f684SSatish Balay        setup_args['zip_safe'] = False
2622149b0eaSStefano Zampini        numpy_pin = 'numpy'
2632149b0eaSStefano Zampini        if not is_sdist:
26430eefebbSLisandro Dalcin            try:
26530eefebbSLisandro Dalcin                import numpy
2666f336411SStefano Zampini
26730eefebbSLisandro Dalcin                major = int(numpy.__version__.partition('.')[0])
268f566d949SLisandro Dalcin                numpy_pin = 'numpy>=1.19' if major >= 2 else 'numpy<2'
26930eefebbSLisandro Dalcin            except ImportError:
2702149b0eaSStefano Zampini                pass
271693647e8SLisandro Dalcin        setup_args['setup_requires'] = ['numpy']
27230eefebbSLisandro Dalcin        setup_args['install_requires'] = [numpy_pin]
273f46a955bSLisandro Dalcin        for pkg in map(str.lower, PLIST):
274f46a955bSLisandro Dalcin            PKG_DIR = os.environ.get(pkg.upper() + '_DIR')
275f46a955bSLisandro Dalcin            if not (PKG_DIR and os.path.isdir(PKG_DIR)):
276f46a955bSLisandro Dalcin                package = requires(pkg, x, y, release)
277693647e8SLisandro Dalcin                setup_args['setup_requires'] += [package]
278f46a955bSLisandro Dalcin                setup_args['install_requires'] += [package]
279f46a955bSLisandro Dalcin        if F('{pyname}') != 'petsc4py':
280f46a955bSLisandro Dalcin            package = requires('petsc4py', x, y, release)
281693647e8SLisandro Dalcin            setup_args['setup_requires'] += [package]
282f46a955bSLisandro Dalcin            setup_args['install_requires'] += [package]
283fb83dddaSLisandro Dalcin        setup_args.update(metadata_extra)
2845808f684SSatish Balay    #
285f46a955bSLisandro Dalcin    conf = __import__(F('conf{name}'))
2867b8d1e39SLisandro Dalcin    cython_sources = [src for src in sources()]  # noqa: C416
2877b8d1e39SLisandro Dalcin    ext_modules = [conf.Extension(**ext) for ext in extensions()]
2887b8d1e39SLisandro Dalcin    #
2897b8d1e39SLisandro Dalcin    sabi = get_build_pysabi()
2907b8d1e39SLisandro Dalcin    if sabi and setuptools:
2917b8d1e39SLisandro Dalcin        api_tag = "cp{}{}".format(*sabi)
2927b8d1e39SLisandro Dalcin        options = {"bdist_wheel": {"py_limited_api": api_tag}}
2937b8d1e39SLisandro Dalcin        setup_args["options"] = options
2947b8d1e39SLisandro Dalcin        api_ver = "0x{:02X}{:02X}0000".format(*sabi)
2957b8d1e39SLisandro Dalcin        defines = [("Py_LIMITED_API", api_ver)]
2967b8d1e39SLisandro Dalcin        for ext in ext_modules:
2977b8d1e39SLisandro Dalcin            ext.define_macros.extend(defines)
2987b8d1e39SLisandro Dalcin            ext.py_limited_api = True
2997b8d1e39SLisandro Dalcin    #
300f46a955bSLisandro Dalcin    conf.setup(
301fb83dddaSLisandro Dalcin        packages=[
302f46a955bSLisandro Dalcin            F('{pyname}'),
303f46a955bSLisandro Dalcin            F('{pyname}.lib'),
30422d6dc08SStefano Zampini            F('{pyname}.lib._pytypes'),
30522d6dc08SStefano Zampini            F('{pyname}.lib._pytypes.viewer'),
306fb83dddaSLisandro Dalcin        ],
307f46a955bSLisandro Dalcin        package_dir={'': 'src'},
308fb83dddaSLisandro Dalcin        package_data={
309f46a955bSLisandro Dalcin            F('{pyname}'): [
310f46a955bSLisandro Dalcin                F('{Name}.pxd'),
311f46a955bSLisandro Dalcin                F('{Name}*.h'),
312f46a955bSLisandro Dalcin                F('include/{pyname}/*.h'),
313f46a955bSLisandro Dalcin                F('include/{pyname}/*.i'),
31455a74a43SLisandro Dalcin                'py.typed',
31555a74a43SLisandro Dalcin                '*.pyi',
31655a74a43SLisandro Dalcin                '*/*.pyi',
317fb83dddaSLisandro Dalcin            ],
318f46a955bSLisandro Dalcin            F('{pyname}.lib'): [
319f46a955bSLisandro Dalcin                F('{name}.cfg'),
320fb83dddaSLisandro Dalcin            ],
321fb83dddaSLisandro Dalcin        },
3227b8d1e39SLisandro Dalcin        cython_sources=cython_sources,
3237b8d1e39SLisandro Dalcin        ext_modules=ext_modules,
3246f336411SStefano Zampini        **setup_args,
325fb83dddaSLisandro Dalcin    )
3265808f684SSatish Balay
3276f336411SStefano Zampini
328fb83dddaSLisandro Dalcin# --------------------------------------------------------------------
329fb83dddaSLisandro Dalcin
3306f336411SStefano Zampini
3315808f684SSatish Balaydef main():
3325808f684SSatish Balay    run_setup()
3335808f684SSatish Balay
3346f336411SStefano Zampini
3355808f684SSatish Balayif __name__ == '__main__':
3365808f684SSatish Balay    main()
3375808f684SSatish Balay
3385808f684SSatish Balay# --------------------------------------------------------------------
339