1# -*- coding: utf-8 -*-
2# Licensed under a 3-clause BSD style license - see LICENSE.rst
3
4import os
5import re
6import sys
7import functools
8import setuptools
9import subprocess
10from warnings import warn
11from distutils.dep_util import newer
12import packaging.version
13
14
15LIBERFADIR = os.path.join('liberfa', 'erfa')
16ERFA_SRC = os.path.join(LIBERFADIR, 'src')
17GEN_FILES = [
18    os.path.join('erfa', 'core.py'),
19    os.path.join('erfa', 'ufunc.c'),
20]
21
22
23# https://mail.python.org/pipermail/distutils-sig/2007-September/008253.html
24class NumpyExtension(setuptools.Extension):
25    """Extension type that adds the NumPy include directory to include_dirs."""
26
27    @property
28    def include_dirs(self):
29        from numpy import get_include
30        return self._include_dirs + [get_include()]
31
32    @include_dirs.setter
33    def include_dirs(self, include_dirs):
34        self._include_dirs = include_dirs
35
36
37def get_liberfa_versions(path=os.path.join(LIBERFADIR, 'configure.ac')):
38    with open(path) as fd:
39        s = fd.read()
40
41    mobj = re.search(r'AC_INIT\(\[erfa\],\[(?P<version>[0-9.]+)\]\)', s)
42    if not mobj:
43        warn('unable to detect liberfa version')
44        return []
45
46    version = packaging.version.parse(mobj.group('version'))
47
48    mobj = re.search(
49        r'AC_DEFINE\(\[SOFA_VERSION\], \["(?P<version>\d{8}(_\w)?)"\],', s)
50    if not mobj:
51        warn('unable to detect SOFA version')
52        return []
53    sofa_version = mobj.group('version')
54
55    return [
56        ('PACKAGE_VERSION', version.base_version),
57        ('PACKAGE_VERSION_MAJOR', version.major),
58        ('PACKAGE_VERSION_MINOR', version.minor),
59        ('PACKAGE_VERSION_MICRO', version.micro),
60        ('SOFA_VERSION', sofa_version),
61    ]
62
63
64def get_extensions():
65    gen_files_exist = all(os.path.isfile(fn) for fn in GEN_FILES)
66    gen_files_outdated = False
67    if os.path.isdir(ERFA_SRC):
68        # assume that 'erfaversion.c' is updated at each release at least
69        src = os.path.join(ERFA_SRC, 'erfaversion.c')
70        gen_files_outdated = any(newer(src, fn) for fn in GEN_FILES)
71    elif not gen_files_exist:
72        raise RuntimeError(
73            'Missing "liberfa" source files, unable to generate '
74            '"erfa/ufunc.c" and "erfa/core.py". '
75            'Please check your source tree. '
76            'Maybe "git submodule update" could help.')
77
78    if not gen_files_exist or gen_files_outdated:
79        print('Run "erfa_generator.py"')
80        cmd = [sys.executable, 'erfa_generator.py', ERFA_SRC, '--quiet']
81        subprocess.run(cmd, check=True)
82
83    sources = [os.path.join('erfa', 'ufunc.c')]
84    include_dirs = []
85    libraries = []
86    define_macros = []
87
88    if int(os.environ.get('PYERFA_USE_SYSTEM_LIBERFA', 0)):
89        print('Using system liberfa')
90        libraries.append('erfa')
91    else:
92        # get all of the .c files in the liberfa/erfa/src directory
93        erfafns = os.listdir(ERFA_SRC)
94        sources.extend([os.path.join(ERFA_SRC, fn)
95                        for fn in erfafns
96                        if fn.endswith('.c') and not fn.startswith('t_')])
97
98        include_dirs.append(ERFA_SRC)
99
100        # liberfa configuration
101        config_h = os.path.join(LIBERFADIR, 'config.h')
102        if not os.path.exists(config_h):
103            print('Configure liberfa')
104            configure = os.path.join(LIBERFADIR, 'configure')
105            try:
106                if not os.path.exists(configure):
107                    subprocess.run(
108                        ['./bootstrap.sh'], check=True, cwd=LIBERFADIR)
109                subprocess.run(['./configure'], check=True, cwd=LIBERFADIR)
110            except (subprocess.SubprocessError, OSError) as exc:
111                warn(f'unable to configure liberfa: {exc}')
112
113        if not os.path.exists(config_h):
114            liberfa_versions = get_liberfa_versions()
115            if liberfa_versions:
116                print('Configure liberfa ("configure.ac" scan)')
117                lines = []
118                for name, value in get_liberfa_versions():
119                    lines.append(f'#define {name} "{value}"')
120                with open(config_h, 'w') as fd:
121                    fd.write('\n'.join(lines))
122            else:
123                warn('unable to get liberfa version')
124
125        if os.path.exists(config_h):
126            include_dirs.append(LIBERFADIR)
127            define_macros.append(('HAVE_CONFIG_H', '1'))
128        elif 'sdist' in sys.argv:
129            raise RuntimeError('missing "configure" script in "liberfa/erfa"')
130
131    erfa_ext = NumpyExtension(
132        name="erfa.ufunc",
133        sources=sources,
134        include_dirs=include_dirs,
135        libraries=libraries,
136        define_macros=define_macros,
137        language="c")
138
139    return [erfa_ext]
140
141
142try:
143    with open('erfa/_dev/scm_version.py') as fd:
144        source = fd.read()
145except FileNotFoundError:
146    guess_next_dev = None
147else:
148    import types
149    scm_version = types.ModuleType('scm_version')
150    scm_version.__file__ = 'erfa/_dev/scm_version.py'
151    code = compile(source, scm_version.__file__, 'exec')
152    try:
153        exec(code, scm_version.__dict__)
154    except ImportError:
155        guess_next_dev = None
156    else:
157        guess_next_dev = functools.partial(scm_version._guess_next_dev,
158                                           liberfadir=LIBERFADIR)
159
160use_scm_version = {
161    'write_to': os.path.join('erfa', '_version.py'),
162    'version_scheme': guess_next_dev,
163}
164
165setuptools.setup(use_scm_version=use_scm_version, ext_modules=get_extensions())
166