xref: /petsc/src/binding/petsc4py/conf/confpetsc.py (revision dc3b5f91da540d0d3af7bee0d33428ae6c98e163)
1f46a955bSLisandro Dalcin# --------------------------------------------------------------------
2f46a955bSLisandro Dalcin
3*163a2e85Spaul.kuehnerfrom pathlib import Path
4f46a955bSLisandro Dalcinimport re
5f46a955bSLisandro Dalcinimport os
6*163a2e85Spaul.kuehnerimport subprocess
7f46a955bSLisandro Dalcinimport sys
8f46a955bSLisandro Dalcinimport glob
9f46a955bSLisandro Dalcinimport copy
10f46a955bSLisandro Dalcinimport warnings
116f336411SStefano Zampinifrom distutils import log
126f336411SStefano Zampinifrom distutils import sysconfig
136f336411SStefano Zampinifrom distutils.util import execute
146f336411SStefano Zampinifrom distutils.util import split_quoted
156f336411SStefano Zampinifrom distutils.errors import DistutilsError
166f336411SStefano Zampinifrom distutils.text_file import TextFile
176f336411SStefano Zampini
18f46a955bSLisandro Dalcin
19f46a955bSLisandro Dalcintry:
20f46a955bSLisandro Dalcin    from cStringIO import StringIO
21f46a955bSLisandro Dalcinexcept ImportError:
22f46a955bSLisandro Dalcin    from io import StringIO
23f46a955bSLisandro Dalcin
24f46a955bSLisandro Dalcintry:
25f46a955bSLisandro Dalcin    import setuptools
26f46a955bSLisandro Dalcinexcept ImportError:
27f46a955bSLisandro Dalcin    setuptools = None
28f46a955bSLisandro Dalcin
29f46a955bSLisandro Dalcinif setuptools:
30f46a955bSLisandro Dalcin    from setuptools import setup as _setup
31f46a955bSLisandro Dalcin    from setuptools import Extension as _Extension
32f46a955bSLisandro Dalcin    from setuptools import Command
33f46a955bSLisandro Dalcinelse:
34f46a955bSLisandro Dalcin    from distutils.core import setup as _setup
35f46a955bSLisandro Dalcin    from distutils.core import Extension as _Extension
36f46a955bSLisandro Dalcin    from distutils.core import Command
37f46a955bSLisandro Dalcin
386f336411SStefano Zampini
39f46a955bSLisandro Dalcindef import_command(cmd):
40f46a955bSLisandro Dalcin    try:
41f46a955bSLisandro Dalcin        from importlib import import_module
42f46a955bSLisandro Dalcin    except ImportError:
436f336411SStefano Zampini
44f46a955bSLisandro Dalcin        def import_module(n):
45f46a955bSLisandro Dalcin            return __import__(n, fromlist=[None])
466f336411SStefano Zampini
47f46a955bSLisandro Dalcin    try:
486f336411SStefano Zampini        if not setuptools:
496f336411SStefano Zampini            raise ImportError
50f46a955bSLisandro Dalcin        mod = import_module('setuptools.command.' + cmd)
51f46a955bSLisandro Dalcin        return getattr(mod, cmd)
52f46a955bSLisandro Dalcin    except ImportError:
53f46a955bSLisandro Dalcin        mod = import_module('distutils.command.' + cmd)
54f46a955bSLisandro Dalcin        return getattr(mod, cmd)
55f46a955bSLisandro Dalcin
566f336411SStefano Zampini
57f46a955bSLisandro Dalcin_config = import_command('config')
58f46a955bSLisandro Dalcin_build = import_command('build')
59f46a955bSLisandro Dalcin_build_ext = import_command('build_ext')
60f46a955bSLisandro Dalcin_install = import_command('install')
61f46a955bSLisandro Dalcin
62f46a955bSLisandro Dalcintry:
6356478d30SPierre Jolivet    from setuptools import modified
64f46a955bSLisandro Dalcinexcept ImportError:
6556478d30SPierre Jolivet    try:
6656478d30SPierre Jolivet        from setuptools import dep_util as modified
6756478d30SPierre Jolivet    except ImportError:
6856478d30SPierre Jolivet        from distutils import dep_util as modified
69f46a955bSLisandro Dalcin
70f46a955bSLisandro Dalcintry:
71f46a955bSLisandro Dalcin    from packaging.version import Version
72f46a955bSLisandro Dalcinexcept ImportError:
73f46a955bSLisandro Dalcin    try:
74f46a955bSLisandro Dalcin        from setuptools.extern.packaging.version import Version
75f46a955bSLisandro Dalcin    except ImportError:
76f46a955bSLisandro Dalcin        from distutils.version import StrictVersion as Version
77f46a955bSLisandro Dalcin
78f46a955bSLisandro Dalcin# --------------------------------------------------------------------
79f46a955bSLisandro Dalcin
80f46a955bSLisandro Dalcin# Cython
81f46a955bSLisandro Dalcin
82b12f50ebSLisandro DalcinCYTHON = '3.0.0'
83f46a955bSLisandro Dalcin
846f336411SStefano Zampini
85f46a955bSLisandro Dalcindef cython_req():
86f46a955bSLisandro Dalcin    return CYTHON
87f46a955bSLisandro Dalcin
886f336411SStefano Zampini
89f46a955bSLisandro Dalcindef cython_chk(VERSION, verbose=True):
90f46a955bSLisandro Dalcin    #
91f46a955bSLisandro Dalcin    def warn(message):
926f336411SStefano Zampini        if not verbose:
936f336411SStefano Zampini            return
946f336411SStefano Zampini        ruler, ws, nl = '*' * 80, ' ', '\n'
95f46a955bSLisandro Dalcin        pyexe = sys.executable
966f336411SStefano Zampini        advise = '$ %s -m pip install --upgrade cython' % pyexe
976f336411SStefano Zampini
986f336411SStefano Zampini        def printer(*s):
996f336411SStefano Zampini            sys.stderr.write(' '.join(s) + '\n')
1006f336411SStefano Zampini
101f46a955bSLisandro Dalcin        printer(ruler, nl)
102f46a955bSLisandro Dalcin        printer(ws, message, nl)
103f46a955bSLisandro Dalcin        printer(ws, ws, advise, nl)
104f46a955bSLisandro Dalcin        printer(ruler)
1056f336411SStefano Zampini
106f46a955bSLisandro Dalcin    #
107f46a955bSLisandro Dalcin    try:
108f46a955bSLisandro Dalcin        import Cython
109f46a955bSLisandro Dalcin    except ImportError:
1106f336411SStefano Zampini        warn('You need Cython to generate C source files.')
111f46a955bSLisandro Dalcin        return False
112f46a955bSLisandro Dalcin    #
113f46a955bSLisandro Dalcin    CYTHON_VERSION = Cython.__version__
1146f336411SStefano Zampini    m = re.match(r'(\d+\.\d+(?:\.\d+)?).*', CYTHON_VERSION)
115f46a955bSLisandro Dalcin    if not m:
1166f336411SStefano Zampini        warn(f'Cannot parse Cython version string {CYTHON_VERSION!r}')
117f46a955bSLisandro Dalcin        return False
118f46a955bSLisandro Dalcin    REQUIRED = Version(VERSION)
119f46a955bSLisandro Dalcin    PROVIDED = Version(m.groups()[0])
120b12f50ebSLisandro Dalcin    if PROVIDED < REQUIRED:
1216f336411SStefano Zampini        warn(f'You need Cython >= {VERSION} (you have version {CYTHON_VERSION})')
122f46a955bSLisandro Dalcin        return False
123f46a955bSLisandro Dalcin    #
124f46a955bSLisandro Dalcin    if verbose:
1256f336411SStefano Zampini        log.info('using Cython %s' % CYTHON_VERSION)
126f46a955bSLisandro Dalcin    return True
127f46a955bSLisandro Dalcin
1286f336411SStefano Zampini
129f46a955bSLisandro Dalcindef cython_run(
1306f336411SStefano Zampini    source,
1316f336411SStefano Zampini    target=None,
1326f336411SStefano Zampini    depends=(),
1336f336411SStefano Zampini    includes=(),
1346f336411SStefano Zampini    workdir=None,
1356f336411SStefano Zampini    force=False,
1366f336411SStefano Zampini    VERSION='0.0',
137f46a955bSLisandro Dalcin):
138f46a955bSLisandro Dalcin    if target is None:
139f46a955bSLisandro Dalcin        target = os.path.splitext(source)[0] + '.c'
140f46a955bSLisandro Dalcin    cwd = os.getcwd()
141f46a955bSLisandro Dalcin    try:
142f46a955bSLisandro Dalcin        if workdir:
143f46a955bSLisandro Dalcin            os.chdir(workdir)
144f46a955bSLisandro Dalcin        alldeps = [source]
145f46a955bSLisandro Dalcin        for dep in depends:
146f46a955bSLisandro Dalcin            alldeps += glob.glob(dep)
14756478d30SPierre Jolivet        if not (force or modified.newer_group(alldeps, target)):
1486f336411SStefano Zampini            log.debug("skipping '%s' -> '%s' (up-to-date)", source, target)
149f46a955bSLisandro Dalcin            return
150f46a955bSLisandro Dalcin    finally:
151f46a955bSLisandro Dalcin        os.chdir(cwd)
15280e0d92bSLisandro Dalcin    require = 'Cython >= %s' % VERSION
153f46a955bSLisandro Dalcin    if setuptools and not cython_chk(VERSION, verbose=False):
154f46a955bSLisandro Dalcin        if sys.modules.get('Cython'):
155f46a955bSLisandro Dalcin            removed = getattr(sys.modules['Cython'], '__version__', '')
1566f336411SStefano Zampini            log.info('removing Cython %s from sys.modules' % removed)
157f46a955bSLisandro Dalcin            pkgname = re.compile(r'cython(\.|$)', re.IGNORECASE)
158f46a955bSLisandro Dalcin            for modname in list(sys.modules.keys()):
159f46a955bSLisandro Dalcin                if pkgname.match(modname):
160f46a955bSLisandro Dalcin                    del sys.modules[modname]
161f46a955bSLisandro Dalcin        try:
162f46a955bSLisandro Dalcin            install_setup_requires = setuptools._install_setup_requires
163f46a955bSLisandro Dalcin            with warnings.catch_warnings():
164f46a955bSLisandro Dalcin                if hasattr(setuptools, 'SetuptoolsDeprecationWarning'):
165f46a955bSLisandro Dalcin                    category = setuptools.SetuptoolsDeprecationWarning
166f46a955bSLisandro Dalcin                    warnings.simplefilter('ignore', category)
167f46a955bSLisandro Dalcin                log.info("fetching build requirement '%s'" % require)
1686f336411SStefano Zampini                install_setup_requires({'setup_requires': [require]})
169f46a955bSLisandro Dalcin        except Exception:
170f46a955bSLisandro Dalcin            log.info("failed to fetch build requirement '%s'" % require)
171f46a955bSLisandro Dalcin    if not cython_chk(VERSION):
172f46a955bSLisandro Dalcin        raise DistutilsError("unsatisfied build requirement '%s'" % require)
173f46a955bSLisandro Dalcin    #
174f46a955bSLisandro Dalcin    log.info("cythonizing '%s' -> '%s'", source, target)
175f46a955bSLisandro Dalcin    from cythonize import cythonize
1766f336411SStefano Zampini
177f46a955bSLisandro Dalcin    args = []
178f46a955bSLisandro Dalcin    if workdir:
179f46a955bSLisandro Dalcin        args += ['--working', workdir]
180f46a955bSLisandro Dalcin    args += [source]
181f46a955bSLisandro Dalcin    if target:
182f46a955bSLisandro Dalcin        args += ['--output-file', target]
183f46a955bSLisandro Dalcin    err = cythonize(args)
184f46a955bSLisandro Dalcin    if err:
1856f336411SStefano Zampini        raise DistutilsError(f"Cython failure: '{source}' -> '{target}'")
186f46a955bSLisandro Dalcin
187f46a955bSLisandro Dalcin
188f46a955bSLisandro Dalcin# --------------------------------------------------------------------
189f46a955bSLisandro Dalcin
1906f336411SStefano Zampini
191f46a955bSLisandro Dalcindef fix_config_vars(names, values):
192f46a955bSLisandro Dalcin    values = list(values)
193f46a955bSLisandro Dalcin    if 'CONDA_BUILD' in os.environ:
194f46a955bSLisandro Dalcin        return values
195f46a955bSLisandro Dalcin    if sys.platform == 'darwin':
196f46a955bSLisandro Dalcin        if 'ARCHFLAGS' in os.environ:
197f46a955bSLisandro Dalcin            ARCHFLAGS = os.environ['ARCHFLAGS']
198f46a955bSLisandro Dalcin            for i, flag in enumerate(list(values)):
1998acd7bfaSLisandro Dalcin                flag, count = re.subn(r'-arch\s+\w+', ' ', str(flag))
200f46a955bSLisandro Dalcin                if count and ARCHFLAGS:
201f46a955bSLisandro Dalcin                    flag = flag + ' ' + ARCHFLAGS
202f46a955bSLisandro Dalcin                values[i] = flag
203f46a955bSLisandro Dalcin        if 'SDKROOT' in os.environ:
204f46a955bSLisandro Dalcin            SDKROOT = os.environ['SDKROOT']
205f46a955bSLisandro Dalcin            for i, flag in enumerate(list(values)):
2068acd7bfaSLisandro Dalcin                flag, count = re.subn(r'-isysroot [^ \t]*', ' ', str(flag))
207f46a955bSLisandro Dalcin                if count and SDKROOT:
208f46a955bSLisandro Dalcin                    flag = flag + ' ' + '-isysroot ' + SDKROOT
209f46a955bSLisandro Dalcin                values[i] = flag
210f46a955bSLisandro Dalcin    return values
211f46a955bSLisandro Dalcin
2126f336411SStefano Zampini
213f46a955bSLisandro Dalcindef get_config_vars(*names):
214f46a955bSLisandro Dalcin    # Core Python configuration
215f46a955bSLisandro Dalcin    values = sysconfig.get_config_vars(*names)
216f46a955bSLisandro Dalcin    # Do any distutils flags fixup right now
2176f336411SStefano Zampini    return fix_config_vars(names, values)
2186f336411SStefano Zampini
219f46a955bSLisandro Dalcin
220f46a955bSLisandro Dalcin# --------------------------------------------------------------------
221f46a955bSLisandro Dalcin
222f46a955bSLisandro Dalcin
2236f336411SStefano Zampiniclass PetscConfig:
224f46a955bSLisandro Dalcin    def __init__(self, petsc_dir, petsc_arch, dest_dir=None):
225f46a955bSLisandro Dalcin        if dest_dir is None:
226f46a955bSLisandro Dalcin            dest_dir = os.environ.get('DESTDIR')
227f46a955bSLisandro Dalcin        self.configdict = {}
228f46a955bSLisandro Dalcin        if not petsc_dir:
2296f336411SStefano Zampini            raise DistutilsError('PETSc not found')
230f46a955bSLisandro Dalcin        if not os.path.isdir(petsc_dir):
2316f336411SStefano Zampini            raise DistutilsError('invalid PETSC_DIR: %s' % petsc_dir)
232f46a955bSLisandro Dalcin        self.version = self._get_petsc_version(petsc_dir)
233f46a955bSLisandro Dalcin        self.configdict = self._get_petsc_config(petsc_dir, petsc_arch)
234f46a955bSLisandro Dalcin        self.PETSC_DIR = self['PETSC_DIR']
235f46a955bSLisandro Dalcin        self.PETSC_ARCH = self['PETSC_ARCH']
236f46a955bSLisandro Dalcin        self.DESTDIR = dest_dir
237f46a955bSLisandro Dalcin        language_map = {'CONLY': 'c', 'CXXONLY': 'c++'}
238f46a955bSLisandro Dalcin        self.language = language_map[self['PETSC_LANGUAGE']]
239f46a955bSLisandro Dalcin
240f46a955bSLisandro Dalcin    def __getitem__(self, item):
241f46a955bSLisandro Dalcin        return self.configdict[item]
242f46a955bSLisandro Dalcin
243f46a955bSLisandro Dalcin    def get(self, item, default=None):
244f46a955bSLisandro Dalcin        return self.configdict.get(item, default)
245f46a955bSLisandro Dalcin
246f46a955bSLisandro Dalcin    def configure(self, extension, compiler=None):
247f46a955bSLisandro Dalcin        self.configure_extension(extension)
248f46a955bSLisandro Dalcin        if compiler is not None:
249f46a955bSLisandro Dalcin            self.configure_compiler(compiler)
250f46a955bSLisandro Dalcin
251f46a955bSLisandro Dalcin    def _get_petsc_version(self, petsc_dir):
252f46a955bSLisandro Dalcin        import re
2536f336411SStefano Zampini
254f46a955bSLisandro Dalcin        version_re = {
2556f336411SStefano Zampini            'major': re.compile(r'#define\s+PETSC_VERSION_MAJOR\s+(\d+)'),
2566f336411SStefano Zampini            'minor': re.compile(r'#define\s+PETSC_VERSION_MINOR\s+(\d+)'),
2576f336411SStefano Zampini            'micro': re.compile(r'#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)'),
2586f336411SStefano Zampini            'release': re.compile(r'#define\s+PETSC_VERSION_RELEASE\s+(-*\d+)'),
259f46a955bSLisandro Dalcin        }
260f46a955bSLisandro Dalcin        petscversion_h = os.path.join(petsc_dir, 'include', 'petscversion.h')
2616f336411SStefano Zampini        with open(petscversion_h, 'rt') as f:
2626f336411SStefano Zampini            data = f.read()
263f46a955bSLisandro Dalcin        major = int(version_re['major'].search(data).groups()[0])
264f46a955bSLisandro Dalcin        minor = int(version_re['minor'].search(data).groups()[0])
265f46a955bSLisandro Dalcin        micro = int(version_re['micro'].search(data).groups()[0])
266f46a955bSLisandro Dalcin        release = int(version_re['release'].search(data).groups()[0])
267f46a955bSLisandro Dalcin        return (major, minor, micro), (release == 1)
268f46a955bSLisandro Dalcin
269f46a955bSLisandro Dalcin    def _get_petsc_config(self, petsc_dir, petsc_arch):
270f46a955bSLisandro Dalcin        from os.path import join, isdir, exists
2716f336411SStefano Zampini
272f46a955bSLisandro Dalcin        PETSC_DIR = petsc_dir
273f46a955bSLisandro Dalcin        PETSC_ARCH = petsc_arch
274f46a955bSLisandro Dalcin        #
275f46a955bSLisandro Dalcin        confdir = join('lib', 'petsc', 'conf')
276f46a955bSLisandro Dalcin        if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))):
277f46a955bSLisandro Dalcin            petscvars = join(PETSC_DIR, confdir, 'petscvariables')
278f46a955bSLisandro Dalcin            PETSC_ARCH = makefile(open(petscvars, 'rt')).get('PETSC_ARCH')
279f46a955bSLisandro Dalcin        if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))):
280f46a955bSLisandro Dalcin            PETSC_ARCH = ''
281f46a955bSLisandro Dalcin        #
282f46a955bSLisandro Dalcin        variables = join(PETSC_DIR, confdir, 'variables')
283f46a955bSLisandro Dalcin        if not exists(variables):
284f46a955bSLisandro Dalcin            variables = join(PETSC_DIR, PETSC_ARCH, confdir, 'variables')
285f46a955bSLisandro Dalcin        petscvariables = join(PETSC_DIR, PETSC_ARCH, confdir, 'petscvariables')
286f46a955bSLisandro Dalcin        #
287f46a955bSLisandro Dalcin        with open(variables) as f:
288f46a955bSLisandro Dalcin            contents = f.read()
289f46a955bSLisandro Dalcin        with open(petscvariables) as f:
290f46a955bSLisandro Dalcin            contents += f.read()
291f46a955bSLisandro Dalcin        #
292f46a955bSLisandro Dalcin        confstr = 'PETSC_DIR  = %s\n' % PETSC_DIR
293f46a955bSLisandro Dalcin        confstr += 'PETSC_ARCH = %s\n' % PETSC_ARCH
294f46a955bSLisandro Dalcin        confstr += contents
2956f336411SStefano Zampini        return makefile(StringIO(confstr))
296f46a955bSLisandro Dalcin
297ec81f945SPierre Jolivet    def _configure_ext(self, ext, dct, append=False):
298f46a955bSLisandro Dalcin        extdict = ext.__dict__
299f46a955bSLisandro Dalcin        for key, values in dct.items():
300f46a955bSLisandro Dalcin            if key in extdict:
301f46a955bSLisandro Dalcin                for value in values:
302f46a955bSLisandro Dalcin                    if value not in extdict[key]:
303ec81f945SPierre Jolivet                        if not append:
304f46a955bSLisandro Dalcin                            extdict[key].insert(0, value)
305f46a955bSLisandro Dalcin                        else:
306f46a955bSLisandro Dalcin                            extdict[key].append(value)
307f46a955bSLisandro Dalcin
308f46a955bSLisandro Dalcin    def configure_extension(self, extension):
309f46a955bSLisandro Dalcin        # includes and libraries
310f46a955bSLisandro Dalcin        # paths in PETSc config files point to final installation location, but
311f46a955bSLisandro Dalcin        # we might be building against PETSc in staging location (DESTDIR) when
312f46a955bSLisandro Dalcin        # DESTDIR is set, so append DESTDIR (if nonempty) to those paths
313f46a955bSLisandro Dalcin        petsc_inc = flaglist(prepend_to_flags(self.DESTDIR, self['PETSC_CC_INCLUDES']))
3146f336411SStefano Zampini        lib_flags = prepend_to_flags(
3156f336411SStefano Zampini            self.DESTDIR,
3166f336411SStefano Zampini            '-L{} {}'.format(self['PETSC_LIB_DIR'], self['PETSC_LIB_BASIC']),
3176f336411SStefano Zampini        )
318f46a955bSLisandro Dalcin        petsc_lib = flaglist(lib_flags)
319f46a955bSLisandro Dalcin        # runtime_library_dirs is not supported on Windows
320f46a955bSLisandro Dalcin        if sys.platform != 'win32':
321f46a955bSLisandro Dalcin            # if DESTDIR is set, then we're building against PETSc in a staging
322f46a955bSLisandro Dalcin            # directory, but rpath needs to point to final install directory.
323693647e8SLisandro Dalcin            rpath = [strip_prefix(self.DESTDIR, self['PETSC_LIB_DIR'])]
324693647e8SLisandro Dalcin            if sys.modules.get('petsc') is not None:
325693647e8SLisandro Dalcin                if sys.platform == 'darwin':
326693647e8SLisandro Dalcin                    rpath = ['@loader_path/../../petsc/lib']
327693647e8SLisandro Dalcin                else:
328693647e8SLisandro Dalcin                    rpath = ['$ORIGIN/../../petsc/lib']
329693647e8SLisandro Dalcin            petsc_lib['runtime_library_dirs'].extend(rpath)
330f46a955bSLisandro Dalcin        # Link in extra libraries on static builds
331f46a955bSLisandro Dalcin        if self['BUILDSHAREDLIB'] != 'yes':
332f46a955bSLisandro Dalcin            petsc_ext_lib = split_quoted(self['PETSC_EXTERNAL_LIB_BASIC'])
333f46a955bSLisandro Dalcin            petsc_lib['extra_link_args'].extend(petsc_ext_lib)
334ec81f945SPierre Jolivet        self._configure_ext(extension, petsc_inc, append=True)
335f46a955bSLisandro Dalcin        self._configure_ext(extension, petsc_lib)
336f46a955bSLisandro Dalcin
337f46a955bSLisandro Dalcin    def configure_compiler(self, compiler):
3386f336411SStefano Zampini        if compiler.compiler_type != 'unix':
3396f336411SStefano Zampini            return
340f46a955bSLisandro Dalcin        getenv = os.environ.get
341f46a955bSLisandro Dalcin        # distutils C/C++ compiler
3426f336411SStefano Zampini        (cc, cflags, ccshared, cxx) = get_config_vars('CC', 'CFLAGS', 'CCSHARED', 'CXX')
343f46a955bSLisandro Dalcin        ccshared = getenv('CCSHARED', ccshared or '')
344f46a955bSLisandro Dalcin        cflags = getenv('CFLAGS', cflags or '')
345f46a955bSLisandro Dalcin        cflags = cflags.replace('-Wstrict-prototypes', '')
346f46a955bSLisandro Dalcin        # distutils linker
3476f336411SStefano Zampini        (ldflags, ldshared, so_ext) = get_config_vars('LDFLAGS', 'LDSHARED', 'SO')
348f46a955bSLisandro Dalcin        ld = cc
349f46a955bSLisandro Dalcin        ldshared = getenv('LDSHARED', ldshared)
350f46a955bSLisandro Dalcin        ldflags = getenv('LDFLAGS', cflags + ' ' + (ldflags or ''))
351f46a955bSLisandro Dalcin        ldcmd = split_quoted(ld) + split_quoted(ldflags)
3526f336411SStefano Zampini        ldshared = [
3536f336411SStefano Zampini            flg
3546f336411SStefano Zampini            for flg in split_quoted(ldshared)
355ad93f427SSatish Balay            if flg not in ldcmd and (flg.find('/lib/spack/env') < 0) and (flg.find('/libexec/spack/') < 0)
3566f336411SStefano Zampini        ]
357f46a955bSLisandro Dalcin        ldshared = str.join(' ', ldshared)
3586f336411SStefano Zampini
359f46a955bSLisandro Dalcin        #
360f46a955bSLisandro Dalcin        def get_flags(cmd):
3616f336411SStefano Zampini            if not cmd:
3626f336411SStefano Zampini                return ''
363f46a955bSLisandro Dalcin            cmd = split_quoted(cmd)
364f46a955bSLisandro Dalcin            if os.path.basename(cmd[0]) == 'xcrun':
365f46a955bSLisandro Dalcin                del cmd[0]
366f46a955bSLisandro Dalcin                while True:
367f46a955bSLisandro Dalcin                    if cmd[0] == '-sdk':
368f46a955bSLisandro Dalcin                        del cmd[0:2]
369f46a955bSLisandro Dalcin                        continue
370f46a955bSLisandro Dalcin                    if cmd[0] == '-log':
371f46a955bSLisandro Dalcin                        del cmd[0]
372f46a955bSLisandro Dalcin                        continue
373f46a955bSLisandro Dalcin                    break
374f46a955bSLisandro Dalcin            return ' '.join(cmd[1:])
3756f336411SStefano Zampini
376f46a955bSLisandro Dalcin        # PETSc C compiler
377f46a955bSLisandro Dalcin        PCC = self['PCC']
378f46a955bSLisandro Dalcin        PCC_FLAGS = get_flags(cc) + ' ' + self['PCC_FLAGS']
379f46a955bSLisandro Dalcin        PCC_FLAGS = PCC_FLAGS.replace('-fvisibility=hidden', '')
38052c29a0dSSatish Balay        PCC_FLAGS = PCC_FLAGS.replace('-Wpedantic', '-Wno-pedantic')
38152c29a0dSSatish Balay        PCC_FLAGS = PCC_FLAGS.replace('-Wextra-semi-stmt', '-Wno-extra-semi-stmt')
382f46a955bSLisandro Dalcin        PCC = getenv('PCC', PCC) + ' ' + getenv('PCCFLAGS', PCC_FLAGS)
383f46a955bSLisandro Dalcin        PCC_SHARED = str.join(' ', (PCC, ccshared, cflags))
384f46a955bSLisandro Dalcin        # PETSc C++ compiler
385f46a955bSLisandro Dalcin        PCXX = PCC if self.language == 'c++' else self.get('CXX', cxx)
386f46a955bSLisandro Dalcin        # PETSc linker
387f46a955bSLisandro Dalcin        PLD = self['PCC_LINKER']
388f46a955bSLisandro Dalcin        PLD_FLAGS = get_flags(ld) + ' ' + self['PCC_LINKER_FLAGS']
389f46a955bSLisandro Dalcin        PLD_FLAGS = PLD_FLAGS.replace('-fvisibility=hidden', '')
390f46a955bSLisandro Dalcin        PLD = getenv('PLD', PLD) + ' ' + getenv('PLDFLAGS', PLD_FLAGS)
391f46a955bSLisandro Dalcin        PLD_SHARED = str.join(' ', (PLD, ldshared, ldflags))
392f46a955bSLisandro Dalcin        #
393f46a955bSLisandro Dalcin        compiler.set_executables(
394f46a955bSLisandro Dalcin            compiler=PCC,
395f46a955bSLisandro Dalcin            compiler_cxx=PCXX,
396f46a955bSLisandro Dalcin            linker_exe=PLD,
397f46a955bSLisandro Dalcin            compiler_so=PCC_SHARED,
398f46a955bSLisandro Dalcin            linker_so=PLD_SHARED,
399f46a955bSLisandro Dalcin        )
400f46a955bSLisandro Dalcin        compiler.shared_lib_extension = so_ext
401f46a955bSLisandro Dalcin
402f46a955bSLisandro Dalcin    def log_info(self):
403f46a955bSLisandro Dalcin        PETSC_DIR = self['PETSC_DIR']
404f46a955bSLisandro Dalcin        PETSC_ARCH = self['PETSC_ARCH']
4056f336411SStefano Zampini        version = '.'.join([str(i) for i in self.version[0]])
4066f336411SStefano Zampini        release = ('development', 'release')[self.version[1]]
407f46a955bSLisandro Dalcin        version_info = version + ' ' + release
408f46a955bSLisandro Dalcin        integer_size = '%s-bit' % self['PETSC_INDEX_SIZE']
409f46a955bSLisandro Dalcin        scalar_type = self['PETSC_SCALAR']
410f46a955bSLisandro Dalcin        precision = self['PETSC_PRECISION']
411f46a955bSLisandro Dalcin        language = self['PETSC_LANGUAGE']
412f46a955bSLisandro Dalcin        compiler = self['PCC']
413f46a955bSLisandro Dalcin        linker = self['PCC_LINKER']
414f46a955bSLisandro Dalcin        log.info('PETSC_DIR:    %s' % PETSC_DIR)
415f46a955bSLisandro Dalcin        log.info('PETSC_ARCH:   %s' % PETSC_ARCH)
416f46a955bSLisandro Dalcin        log.info('version:      %s' % version_info)
417f46a955bSLisandro Dalcin        log.info('integer-size: %s' % integer_size)
418f46a955bSLisandro Dalcin        log.info('scalar-type:  %s' % scalar_type)
419f46a955bSLisandro Dalcin        log.info('precision:    %s' % precision)
420f46a955bSLisandro Dalcin        log.info('language:     %s' % language)
421f46a955bSLisandro Dalcin        log.info('compiler:     %s' % compiler)
422f46a955bSLisandro Dalcin        log.info('linker:       %s' % linker)
423f46a955bSLisandro Dalcin
4246f336411SStefano Zampini
425f46a955bSLisandro Dalcin# --------------------------------------------------------------------
426f46a955bSLisandro Dalcin
4276f336411SStefano Zampini
428f46a955bSLisandro Dalcinclass Extension(_Extension):
429f46a955bSLisandro Dalcin    pass
430f46a955bSLisandro Dalcin
4316f336411SStefano Zampini
432f46a955bSLisandro Dalcin# --------------------------------------------------------------------
433f46a955bSLisandro Dalcin
434f46a955bSLisandro Dalcincmd_petsc_opts = [
4356f336411SStefano Zampini    ('petsc-dir=', None, 'define PETSC_DIR, overriding environmental variables'),
4366f336411SStefano Zampini    ('petsc-arch=', None, 'define PETSC_ARCH, overriding environmental variables'),
437f46a955bSLisandro Dalcin]
438f46a955bSLisandro Dalcin
439f46a955bSLisandro Dalcin
440f46a955bSLisandro Dalcinclass config(_config):
441f46a955bSLisandro Dalcin    Configure = PetscConfig
442f46a955bSLisandro Dalcin
443f46a955bSLisandro Dalcin    user_options = _config.user_options + cmd_petsc_opts
444f46a955bSLisandro Dalcin
445f46a955bSLisandro Dalcin    def initialize_options(self):
446f46a955bSLisandro Dalcin        _config.initialize_options(self)
447f46a955bSLisandro Dalcin        self.petsc_dir = None
448f46a955bSLisandro Dalcin        self.petsc_arch = None
449f46a955bSLisandro Dalcin
450f46a955bSLisandro Dalcin    def get_config_arch(self, arch):
451f46a955bSLisandro Dalcin        return config.Configure(self.petsc_dir, arch)
452f46a955bSLisandro Dalcin
453f46a955bSLisandro Dalcin    def run(self):
454f46a955bSLisandro Dalcin        _config.run(self)
455f46a955bSLisandro Dalcin        self.petsc_dir = config.get_petsc_dir(self.petsc_dir)
4566f336411SStefano Zampini        if self.petsc_dir is None:
4576f336411SStefano Zampini            return
458f46a955bSLisandro Dalcin        petsc_arch = config.get_petsc_arch(self.petsc_dir, self.petsc_arch)
459f46a955bSLisandro Dalcin        log.info('-' * 70)
460f46a955bSLisandro Dalcin        log.info('PETSC_DIR:   %s' % self.petsc_dir)
461f46a955bSLisandro Dalcin        arch_list = petsc_arch
462f46a955bSLisandro Dalcin        if not arch_list:
463f46a955bSLisandro Dalcin            arch_list = [None]
464f46a955bSLisandro Dalcin        for arch in arch_list:
465f46a955bSLisandro Dalcin            conf = self.get_config_arch(arch)
466f46a955bSLisandro Dalcin            archname = conf.PETSC_ARCH or conf['PETSC_ARCH']
467f46a955bSLisandro Dalcin            scalar_type = conf['PETSC_SCALAR']
468f46a955bSLisandro Dalcin            precision = conf['PETSC_PRECISION']
469f46a955bSLisandro Dalcin            language = conf['PETSC_LANGUAGE']
470f46a955bSLisandro Dalcin            compiler = conf['PCC']
471f46a955bSLisandro Dalcin            linker = conf['PCC_LINKER']
472f46a955bSLisandro Dalcin            log.info('-' * 70)
473f46a955bSLisandro Dalcin            log.info('PETSC_ARCH:  %s' % archname)
474f46a955bSLisandro Dalcin            log.info(' * scalar-type: %s' % scalar_type)
475f46a955bSLisandro Dalcin            log.info(' * precision:   %s' % precision)
476f46a955bSLisandro Dalcin            log.info(' * language:    %s' % language)
477f46a955bSLisandro Dalcin            log.info(' * compiler:    %s' % compiler)
478f46a955bSLisandro Dalcin            log.info(' * linker:      %s' % linker)
479f46a955bSLisandro Dalcin        log.info('-' * 70)
480f46a955bSLisandro Dalcin
481f46a955bSLisandro Dalcin    # @staticmethod
482f46a955bSLisandro Dalcin    def get_petsc_dir(petsc_dir):
4836f336411SStefano Zampini        if not petsc_dir:
4846f336411SStefano Zampini            return None
485f46a955bSLisandro Dalcin        petsc_dir = os.path.expandvars(petsc_dir)
486f46a955bSLisandro Dalcin        if not petsc_dir or '$PETSC_DIR' in petsc_dir:
487f46a955bSLisandro Dalcin            try:
488f46a955bSLisandro Dalcin                import petsc
4896f336411SStefano Zampini
490f46a955bSLisandro Dalcin                petsc_dir = petsc.get_petsc_dir()
491f46a955bSLisandro Dalcin            except ImportError:
4926f336411SStefano Zampini                log.warn('PETSC_DIR not specified')
493f46a955bSLisandro Dalcin                return None
494f46a955bSLisandro Dalcin        petsc_dir = os.path.expanduser(petsc_dir)
495f46a955bSLisandro Dalcin        petsc_dir = os.path.abspath(petsc_dir)
496f46a955bSLisandro Dalcin        return config.chk_petsc_dir(petsc_dir)
4976f336411SStefano Zampini
498f46a955bSLisandro Dalcin    get_petsc_dir = staticmethod(get_petsc_dir)
499f46a955bSLisandro Dalcin
500f46a955bSLisandro Dalcin    # @staticmethod
501f46a955bSLisandro Dalcin    def chk_petsc_dir(petsc_dir):
502f46a955bSLisandro Dalcin        if not os.path.isdir(petsc_dir):
503f46a955bSLisandro Dalcin            log.error('invalid PETSC_DIR: %s (ignored)' % petsc_dir)
504f46a955bSLisandro Dalcin            return None
505f46a955bSLisandro Dalcin        return petsc_dir
5066f336411SStefano Zampini
507f46a955bSLisandro Dalcin    chk_petsc_dir = staticmethod(chk_petsc_dir)
508f46a955bSLisandro Dalcin
509f46a955bSLisandro Dalcin    # @staticmethod
510f46a955bSLisandro Dalcin    def get_petsc_arch(petsc_dir, petsc_arch):
5116f336411SStefano Zampini        if not petsc_dir:
5126f336411SStefano Zampini            return None
513f46a955bSLisandro Dalcin        petsc_arch = os.path.expandvars(petsc_arch)
5146f336411SStefano Zampini        if not petsc_arch or '$PETSC_ARCH' in petsc_arch:
515f46a955bSLisandro Dalcin            petsc_arch = ''
516f46a955bSLisandro Dalcin            petsc_conf = os.path.join(petsc_dir, 'lib', 'petsc', 'conf')
517f46a955bSLisandro Dalcin            if os.path.isdir(petsc_conf):
518f46a955bSLisandro Dalcin                petscvariables = os.path.join(petsc_conf, 'petscvariables')
519f46a955bSLisandro Dalcin                if os.path.exists(petscvariables):
520f46a955bSLisandro Dalcin                    conf = makefile(open(petscvariables, 'rt'))
521f46a955bSLisandro Dalcin                    petsc_arch = conf.get('PETSC_ARCH', '')
522f46a955bSLisandro Dalcin        petsc_arch = petsc_arch.split(os.pathsep)
523f46a955bSLisandro Dalcin        petsc_arch = unique(petsc_arch)
524f46a955bSLisandro Dalcin        petsc_arch = [arch for arch in petsc_arch if arch]
525f46a955bSLisandro Dalcin        return config.chk_petsc_arch(petsc_dir, petsc_arch)
5266f336411SStefano Zampini
527f46a955bSLisandro Dalcin    get_petsc_arch = staticmethod(get_petsc_arch)
528f46a955bSLisandro Dalcin
529f46a955bSLisandro Dalcin    # @staticmethod
530f46a955bSLisandro Dalcin    def chk_petsc_arch(petsc_dir, petsc_arch):
531f46a955bSLisandro Dalcin        valid_archs = []
532f46a955bSLisandro Dalcin        for arch in petsc_arch:
533f46a955bSLisandro Dalcin            arch_path = os.path.join(petsc_dir, arch)
534f46a955bSLisandro Dalcin            if os.path.isdir(arch_path):
535f46a955bSLisandro Dalcin                valid_archs.append(arch)
536f46a955bSLisandro Dalcin            else:
5376f336411SStefano Zampini                log.warn('invalid PETSC_ARCH: %s (ignored)' % arch)
538f46a955bSLisandro Dalcin        return valid_archs
5396f336411SStefano Zampini
540f46a955bSLisandro Dalcin    chk_petsc_arch = staticmethod(chk_petsc_arch)
541f46a955bSLisandro Dalcin
542f46a955bSLisandro Dalcin
543f46a955bSLisandro Dalcinclass build(_build):
54455a74a43SLisandro Dalcin    user_options = _build.user_options
5456f336411SStefano Zampini    user_options += [
5466f336411SStefano Zampini        (
54755a74a43SLisandro Dalcin            'inplace',
54855a74a43SLisandro Dalcin            'i',
5496f336411SStefano Zampini            'ignore build-lib and put compiled extensions into the source '
5506f336411SStefano Zampini            'directory alongside your pure Python modules',
5516f336411SStefano Zampini        )
5526f336411SStefano Zampini    ]
55355a74a43SLisandro Dalcin    user_options += cmd_petsc_opts
55455a74a43SLisandro Dalcin
55555a74a43SLisandro Dalcin    boolean_options = _build.boolean_options
55655a74a43SLisandro Dalcin    boolean_options += ['inplace']
557f46a955bSLisandro Dalcin
558f46a955bSLisandro Dalcin    def initialize_options(self):
559f46a955bSLisandro Dalcin        _build.initialize_options(self)
56055a74a43SLisandro Dalcin        self.inplace = None
561f46a955bSLisandro Dalcin        self.petsc_dir = None
562f46a955bSLisandro Dalcin        self.petsc_arch = None
563f46a955bSLisandro Dalcin
564f46a955bSLisandro Dalcin    def finalize_options(self):
565f46a955bSLisandro Dalcin        _build.finalize_options(self)
56655a74a43SLisandro Dalcin        if self.inplace is None:
56755a74a43SLisandro Dalcin            self.inplace = False
5686f336411SStefano Zampini        self.set_undefined_options(
5696f336411SStefano Zampini            'config', ('petsc_dir', 'petsc_dir'), ('petsc_arch', 'petsc_arch')
5706f336411SStefano Zampini        )
571f46a955bSLisandro Dalcin        self.petsc_dir = config.get_petsc_dir(self.petsc_dir)
5726f336411SStefano Zampini        self.petsc_arch = config.get_petsc_arch(self.petsc_dir, self.petsc_arch)
573f46a955bSLisandro Dalcin
5746f336411SStefano Zampini    sub_commands = [('build_src', lambda *args: True)] + _build.sub_commands
575f46a955bSLisandro Dalcin
576f46a955bSLisandro Dalcin
577f46a955bSLisandro Dalcinclass build_src(Command):
5786f336411SStefano Zampini    description = 'build C sources from Cython files'
579f46a955bSLisandro Dalcin
580f46a955bSLisandro Dalcin    user_options = [
5816f336411SStefano Zampini        ('force', 'f', 'forcibly build everything (ignore file timestamps)'),
582f46a955bSLisandro Dalcin    ]
583f46a955bSLisandro Dalcin
584f46a955bSLisandro Dalcin    boolean_options = ['force']
585f46a955bSLisandro Dalcin
586f46a955bSLisandro Dalcin    def initialize_options(self):
587f46a955bSLisandro Dalcin        self.force = False
588f46a955bSLisandro Dalcin
589f46a955bSLisandro Dalcin    def finalize_options(self):
5906f336411SStefano Zampini        self.set_undefined_options(
5916f336411SStefano Zampini            'build',
592f46a955bSLisandro Dalcin            ('force', 'force'),
593f46a955bSLisandro Dalcin        )
594f46a955bSLisandro Dalcin
595f46a955bSLisandro Dalcin    def run(self):
596f46a955bSLisandro Dalcin        sources = getattr(self, 'sources', [])
597f46a955bSLisandro Dalcin        for source in sources:
5986f336411SStefano Zampini            cython_run(force=self.force, VERSION=cython_req(), **source)
599f46a955bSLisandro Dalcin
600f46a955bSLisandro Dalcin
601f46a955bSLisandro Dalcinclass build_ext(_build_ext):
602f46a955bSLisandro Dalcin    user_options = _build_ext.user_options + cmd_petsc_opts
603f46a955bSLisandro Dalcin
604f46a955bSLisandro Dalcin    def initialize_options(self):
605f46a955bSLisandro Dalcin        _build_ext.initialize_options(self)
60655a74a43SLisandro Dalcin        self.inplace = None
607f46a955bSLisandro Dalcin        self.petsc_dir = None
608f46a955bSLisandro Dalcin        self.petsc_arch = None
609f46a955bSLisandro Dalcin        self._outputs = []
610f46a955bSLisandro Dalcin
611f46a955bSLisandro Dalcin    def finalize_options(self):
612f46a955bSLisandro Dalcin        _build_ext.finalize_options(self)
61355a74a43SLisandro Dalcin        self.set_undefined_options('build', ('inplace', 'inplace'))
6146f336411SStefano Zampini        self.set_undefined_options(
6156f336411SStefano Zampini            'build', ('petsc_dir', 'petsc_dir'), ('petsc_arch', 'petsc_arch')
6166f336411SStefano Zampini        )
617f46a955bSLisandro Dalcin
618f46a955bSLisandro Dalcin    def _copy_ext(self, ext):
619f46a955bSLisandro Dalcin        extclass = ext.__class__
620f46a955bSLisandro Dalcin        fullname = self.get_ext_fullname(ext.name)
621f46a955bSLisandro Dalcin        modpath = str.split(fullname, '.')
622f46a955bSLisandro Dalcin        pkgpath = os.path.join('', *modpath[0:-1])
623f46a955bSLisandro Dalcin        name = modpath[-1]
624f46a955bSLisandro Dalcin        sources = list(ext.sources)
625f46a955bSLisandro Dalcin        newext = extclass(name, sources)
626f46a955bSLisandro Dalcin        newext.__dict__.update(copy.deepcopy(ext.__dict__))
627f46a955bSLisandro Dalcin        newext.name = name
628f46a955bSLisandro Dalcin        return pkgpath, newext
629f46a955bSLisandro Dalcin
630f46a955bSLisandro Dalcin    def _build_ext_arch(self, ext, pkgpath, arch):
631f46a955bSLisandro Dalcin        build_temp = self.build_temp
632f46a955bSLisandro Dalcin        build_lib = self.build_lib
633f46a955bSLisandro Dalcin        try:
634f46a955bSLisandro Dalcin            self.build_temp = os.path.join(build_temp, arch)
635f46a955bSLisandro Dalcin            self.build_lib = os.path.join(build_lib, pkgpath, arch)
636f46a955bSLisandro Dalcin            _build_ext.build_extension(self, ext)
637f46a955bSLisandro Dalcin        finally:
638f46a955bSLisandro Dalcin            self.build_temp = build_temp
639f46a955bSLisandro Dalcin            self.build_lib = build_lib
640f46a955bSLisandro Dalcin
641f46a955bSLisandro Dalcin    def get_config_arch(self, arch):
642f46a955bSLisandro Dalcin        return config.Configure(self.petsc_dir, arch)
643f46a955bSLisandro Dalcin
644f46a955bSLisandro Dalcin    def build_extension(self, ext):
645f46a955bSLisandro Dalcin        if not isinstance(ext, Extension):
646f46a955bSLisandro Dalcin            return _build_ext.build_extension(self, ext)
647f46a955bSLisandro Dalcin        petsc_arch = self.petsc_arch
648f46a955bSLisandro Dalcin        if not petsc_arch:
649f46a955bSLisandro Dalcin            petsc_arch = [None]
650f46a955bSLisandro Dalcin        for arch in petsc_arch:
651f46a955bSLisandro Dalcin            config = self.get_config_arch(arch)
652f46a955bSLisandro Dalcin            ARCH = arch or config['PETSC_ARCH']
653f46a955bSLisandro Dalcin            if ARCH not in self.PETSC_ARCH_LIST:
654f46a955bSLisandro Dalcin                self.PETSC_ARCH_LIST.append(ARCH)
655f46a955bSLisandro Dalcin            self.DESTDIR = config.DESTDIR
656f46a955bSLisandro Dalcin            ext.language = config.language
657f46a955bSLisandro Dalcin            config.log_info()
658f46a955bSLisandro Dalcin            pkgpath, newext = self._copy_ext(ext)
659f46a955bSLisandro Dalcin            config.configure(newext, self.compiler)
660f46a955bSLisandro Dalcin            self._build_ext_arch(newext, pkgpath, ARCH)
6616f336411SStefano Zampini        return None
662f46a955bSLisandro Dalcin
663f46a955bSLisandro Dalcin    def run(self):
664f46a955bSLisandro Dalcin        self.build_sources()
665f46a955bSLisandro Dalcin        _build_ext.run(self)
666*163a2e85Spaul.kuehner        self.build_stubs()
667f46a955bSLisandro Dalcin
668f46a955bSLisandro Dalcin    def build_sources(self):
669f46a955bSLisandro Dalcin        if 'build_src' in self.distribution.cmdclass:
670f46a955bSLisandro Dalcin            self.run_command('build_src')
671f46a955bSLisandro Dalcin
672*163a2e85Spaul.kuehner    def build_stubs(self):
673*163a2e85Spaul.kuehner        pkgname = self.distribution.get_name()
674*163a2e85Spaul.kuehner        modname = self.extensions[0].name.split(".")[-1]
675*163a2e85Spaul.kuehner        srcdir = Path(__file__).parent.parent / 'src' / pkgname
676*163a2e85Spaul.kuehner        blddir = Path(self.build_lib) / pkgname
677*163a2e85Spaul.kuehner
678*163a2e85Spaul.kuehner        alldeps = glob.glob(str(blddir / 'lib' / '*' / f'{modname}.*'))
679*163a2e85Spaul.kuehner        target =  srcdir / f'{modname}.pyi'
680*163a2e85Spaul.kuehner        if not (self.force or modified.newer_group(alldeps, target)):
681*163a2e85Spaul.kuehner            log.debug(f"skipping '{modname}.*.so' -> '{target}' (up-to-date)")
682*163a2e85Spaul.kuehner            return
683*163a2e85Spaul.kuehner
684*163a2e85Spaul.kuehner        env = os.environ.copy()
685*163a2e85Spaul.kuehner        python_path = env.get('PYTHONPATH', "")
686*163a2e85Spaul.kuehner        if python_path != "":
687*163a2e85Spaul.kuehner            python_path += ":"
688*163a2e85Spaul.kuehner        python_path += self.build_lib
689*163a2e85Spaul.kuehner        env['PYTHONPATH'] = python_path
690*163a2e85Spaul.kuehner        env.pop('PETSC_ARCH', None)
691*163a2e85Spaul.kuehner
692*163a2e85Spaul.kuehner        stubgen = Path(__file__).parent / 'stubgen.py'
693*163a2e85Spaul.kuehner        rc = subprocess.call([sys.executable, stubgen], env=env) # noqa S603
694*163a2e85Spaul.kuehner        if rc != 0:
695*163a2e85Spaul.kuehner            log.warn("Stubs could not be generated.")
696*163a2e85Spaul.kuehner            return
697*163a2e85Spaul.kuehner
698*163a2e85Spaul.kuehner        self.copy_file(
699*163a2e85Spaul.kuehner            srcdir / f'{modname}.pyi',
700*163a2e85Spaul.kuehner            blddir / f'{modname}.pyi',
701*163a2e85Spaul.kuehner            level=self.verbose,
702*163a2e85Spaul.kuehner        )
703*163a2e85Spaul.kuehner
704f46a955bSLisandro Dalcin    def build_extensions(self, *args, **kargs):
705f46a955bSLisandro Dalcin        self.PETSC_ARCH_LIST = []
706f46a955bSLisandro Dalcin        _build_ext.build_extensions(self, *args, **kargs)
7076f336411SStefano Zampini        if not self.PETSC_ARCH_LIST:
7086f336411SStefano Zampini            return
709f46a955bSLisandro Dalcin        self.build_configuration(self.PETSC_ARCH_LIST)
710f46a955bSLisandro Dalcin
711f46a955bSLisandro Dalcin    def build_configuration(self, arch_list):
712f46a955bSLisandro Dalcin        #
713f46a955bSLisandro Dalcin        template, variables = self.get_config_data(arch_list)
714f46a955bSLisandro Dalcin        config_data = template % variables
715f46a955bSLisandro Dalcin        #
716f46a955bSLisandro Dalcin        build_lib = self.build_lib
717f46a955bSLisandro Dalcin        dist_name = self.distribution.get_name()
7186f336411SStefano Zampini        config_file = os.path.join(
7196f336411SStefano Zampini            build_lib, dist_name, 'lib', dist_name.replace('4py', '') + '.cfg'
7206f336411SStefano Zampini        )
7216f336411SStefano Zampini
722f46a955bSLisandro Dalcin        #
723f46a955bSLisandro Dalcin        def write_file(filename, data):
724f46a955bSLisandro Dalcin            with open(filename, 'w') as fh:
725f46a955bSLisandro Dalcin                fh.write(config_data)
7266f336411SStefano Zampini
7276f336411SStefano Zampini        execute(
7286f336411SStefano Zampini            write_file,
7296f336411SStefano Zampini            (config_file, config_data),
730f46a955bSLisandro Dalcin            msg='writing %s' % config_file,
7316f336411SStefano Zampini            verbose=self.verbose,
7326f336411SStefano Zampini        )
733f46a955bSLisandro Dalcin
734f46a955bSLisandro Dalcin    def get_config_data(self, arch_list):
735f46a955bSLisandro Dalcin        DESTDIR = self.DESTDIR
7366f336411SStefano Zampini        template = (
7376f336411SStefano Zampini            '\n'.join(
7386f336411SStefano Zampini                [
7396f336411SStefano Zampini                    'PETSC_DIR  = %(PETSC_DIR)s',
7406f336411SStefano Zampini                    'PETSC_ARCH = %(PETSC_ARCH)s',
7416f336411SStefano Zampini                ]
7426f336411SStefano Zampini            )
7436f336411SStefano Zampini            + '\n'
7446f336411SStefano Zampini        )
745f46a955bSLisandro Dalcin        variables = {
746f46a955bSLisandro Dalcin            'PETSC_DIR': strip_prefix(DESTDIR, self.petsc_dir),
747f46a955bSLisandro Dalcin            'PETSC_ARCH': os.path.pathsep.join(arch_list),
748f46a955bSLisandro Dalcin        }
749f46a955bSLisandro Dalcin        return template, variables
750f46a955bSLisandro Dalcin
751f46a955bSLisandro Dalcin    def copy_extensions_to_source(self):
752f46a955bSLisandro Dalcin        build_py = self.get_finalized_command('build_py')
753f46a955bSLisandro Dalcin        for ext in self.extensions:
754f46a955bSLisandro Dalcin            inp_file, reg_file = self._get_inplace_equivalent(build_py, ext)
755f46a955bSLisandro Dalcin
756f46a955bSLisandro Dalcin            arch_list = ['']
757f46a955bSLisandro Dalcin            if isinstance(ext, Extension) and self.petsc_arch:
758f46a955bSLisandro Dalcin                arch_list = self.petsc_arch[:]
759f46a955bSLisandro Dalcin
760f46a955bSLisandro Dalcin            file_pairs = []
761f46a955bSLisandro Dalcin            inp_head, inp_tail = os.path.split(inp_file)
762f46a955bSLisandro Dalcin            reg_head, reg_tail = os.path.split(reg_file)
763f46a955bSLisandro Dalcin            for arch in arch_list:
764f46a955bSLisandro Dalcin                inp_file = os.path.join(inp_head, arch, inp_tail)
765f46a955bSLisandro Dalcin                reg_file = os.path.join(reg_head, arch, reg_tail)
766f46a955bSLisandro Dalcin                file_pairs.append((inp_file, reg_file))
767f46a955bSLisandro Dalcin
768f46a955bSLisandro Dalcin            for inp_file, reg_file in file_pairs:
769f46a955bSLisandro Dalcin                if os.path.exists(reg_file) or not ext.optional:
770f46a955bSLisandro Dalcin                    dest_dir, _ = os.path.split(inp_file)
771f46a955bSLisandro Dalcin                    self.mkpath(dest_dir)
772f46a955bSLisandro Dalcin                    self.copy_file(reg_file, inp_file, level=self.verbose)
773f46a955bSLisandro Dalcin
774f46a955bSLisandro Dalcin    def get_outputs(self):
775f46a955bSLisandro Dalcin        self.check_extensions_list(self.extensions)
776f46a955bSLisandro Dalcin        outputs = []
777f46a955bSLisandro Dalcin        for ext in self.extensions:
778f46a955bSLisandro Dalcin            fullname = self.get_ext_fullname(ext.name)
779f46a955bSLisandro Dalcin            filename = self.get_ext_filename(fullname)
780f46a955bSLisandro Dalcin            if isinstance(ext, Extension) and self.petsc_arch:
781f46a955bSLisandro Dalcin                head, tail = os.path.split(filename)
782f46a955bSLisandro Dalcin                for arch in self.petsc_arch:
783f46a955bSLisandro Dalcin                    outfile = os.path.join(self.build_lib, head, arch, tail)
784f46a955bSLisandro Dalcin                    outputs.append(outfile)
785f46a955bSLisandro Dalcin            else:
786f46a955bSLisandro Dalcin                outfile = os.path.join(self.build_lib, filename)
787f46a955bSLisandro Dalcin                outputs.append(outfile)
788*163a2e85Spaul.kuehner
789*163a2e85Spaul.kuehner        pkgname = self.distribution.get_name()
790*163a2e85Spaul.kuehner        modname = self.extensions[0].name.split(".")[-1]
791*163a2e85Spaul.kuehner        outputs.append(os.path.join(self.build_lib, pkgname, f"{modname}.pyi"))
7926f336411SStefano Zampini        return list(set(outputs))
793f46a955bSLisandro Dalcin
794208c597fSJose E. Roman    def get_source_files(self):
795208c597fSJose E. Roman        orig = log.set_threshold(log.WARN)
796208c597fSJose E. Roman        try:
797208c597fSJose E. Roman            return super().get_source_files()
798208c597fSJose E. Roman        finally:
799208c597fSJose E. Roman            log.set_threshold(orig)
800208c597fSJose E. Roman
801f46a955bSLisandro Dalcin
802f46a955bSLisandro Dalcinclass install(_install):
803f46a955bSLisandro Dalcin    def initialize_options(self):
804f46a955bSLisandro Dalcin        with warnings.catch_warnings():
805f46a955bSLisandro Dalcin            if setuptools:
806f46a955bSLisandro Dalcin                if hasattr(setuptools, 'SetuptoolsDeprecationWarning'):
807f46a955bSLisandro Dalcin                    category = setuptools.SetuptoolsDeprecationWarning
808f46a955bSLisandro Dalcin                    warnings.simplefilter('ignore', category)
809f46a955bSLisandro Dalcin            _install.initialize_options(self)
810f46a955bSLisandro Dalcin        self.old_and_unmanageable = True
811f46a955bSLisandro Dalcin
812f46a955bSLisandro Dalcin
813f46a955bSLisandro Dalcincmdclass_list = [
814f46a955bSLisandro Dalcin    config,
815f46a955bSLisandro Dalcin    build,
816f46a955bSLisandro Dalcin    build_src,
817f46a955bSLisandro Dalcin    build_ext,
818f46a955bSLisandro Dalcin    install,
819f46a955bSLisandro Dalcin]
820f46a955bSLisandro Dalcin
821f46a955bSLisandro Dalcin# --------------------------------------------------------------------
822f46a955bSLisandro Dalcin
8236f336411SStefano Zampini
824f46a955bSLisandro Dalcindef setup(**attrs):
825f46a955bSLisandro Dalcin    cmdclass = attrs.setdefault('cmdclass', {})
826f46a955bSLisandro Dalcin    for cmd in cmdclass_list:
827f46a955bSLisandro Dalcin        cmdclass.setdefault(cmd.__name__, cmd)
828f46a955bSLisandro Dalcin    build_src.sources = attrs.pop('cython_sources', None)
829f46a955bSLisandro Dalcin    use_setup_requires = False  # handle Cython requirement ourselves
830f46a955bSLisandro Dalcin    if setuptools and build_src.sources and use_setup_requires:
831f46a955bSLisandro Dalcin        version = cython_req()
832f46a955bSLisandro Dalcin        if not cython_chk(version, verbose=False):
833f46a955bSLisandro Dalcin            reqs = attrs.setdefault('setup_requires', [])
834693647e8SLisandro Dalcin            reqs += ['Cython>=' + version]
835f46a955bSLisandro Dalcin    return _setup(**attrs)
836f46a955bSLisandro Dalcin
8376f336411SStefano Zampini
838f46a955bSLisandro Dalcin# --------------------------------------------------------------------
839f46a955bSLisandro Dalcin
840f46a955bSLisandro Dalcinif setuptools:
841f46a955bSLisandro Dalcin    try:
842f46a955bSLisandro Dalcin        from setuptools.command import egg_info as mod_egg_info
8436f336411SStefano Zampini
844f46a955bSLisandro Dalcin        _FileList = mod_egg_info.FileList
8456f336411SStefano Zampini
846f46a955bSLisandro Dalcin        class FileList(_FileList):
847f46a955bSLisandro Dalcin            def process_template_line(self, line):
848f46a955bSLisandro Dalcin                level = log.set_threshold(log.ERROR)
849f46a955bSLisandro Dalcin                try:
850f46a955bSLisandro Dalcin                    _FileList.process_template_line(self, line)
851f46a955bSLisandro Dalcin                finally:
852f46a955bSLisandro Dalcin                    log.set_threshold(level)
8536f336411SStefano Zampini
854f46a955bSLisandro Dalcin        mod_egg_info.FileList = FileList
855f46a955bSLisandro Dalcin    except (ImportError, AttributeError):
856f46a955bSLisandro Dalcin        pass
857f46a955bSLisandro Dalcin
858f46a955bSLisandro Dalcin# --------------------------------------------------------------------
859f46a955bSLisandro Dalcin
8606f336411SStefano Zampini
861f46a955bSLisandro Dalcindef append(seq, item):
862f46a955bSLisandro Dalcin    if item not in seq:
863f46a955bSLisandro Dalcin        seq.append(item)
864f46a955bSLisandro Dalcin
8656f336411SStefano Zampini
866f46a955bSLisandro Dalcindef append_dict(conf, dct):
867f46a955bSLisandro Dalcin    for key, values in dct.items():
868f46a955bSLisandro Dalcin        if key in conf:
869f46a955bSLisandro Dalcin            for value in values:
870f46a955bSLisandro Dalcin                if value not in conf[key]:
871f46a955bSLisandro Dalcin                    conf[key].append(value)
8726f336411SStefano Zampini
8736f336411SStefano Zampini
874f46a955bSLisandro Dalcindef unique(seq):
875f46a955bSLisandro Dalcin    res = []
876f46a955bSLisandro Dalcin    for item in seq:
877f46a955bSLisandro Dalcin        if item not in res:
878f46a955bSLisandro Dalcin            res.append(item)
879f46a955bSLisandro Dalcin    return res
880f46a955bSLisandro Dalcin
881f46a955bSLisandro Dalcin
8826f336411SStefano Zampinidef flaglist(flags):
883f46a955bSLisandro Dalcin    conf = {
884f46a955bSLisandro Dalcin        'define_macros': [],
885f46a955bSLisandro Dalcin        'undef_macros': [],
886f46a955bSLisandro Dalcin        'include_dirs': [],
887f46a955bSLisandro Dalcin        'libraries': [],
888f46a955bSLisandro Dalcin        'library_dirs': [],
889f46a955bSLisandro Dalcin        'runtime_library_dirs': [],
890f46a955bSLisandro Dalcin        'extra_compile_args': [],
891f46a955bSLisandro Dalcin        'extra_link_args': [],
892f46a955bSLisandro Dalcin    }
893f46a955bSLisandro Dalcin
8946f336411SStefano Zampini    if isinstance(flags, str):
895f46a955bSLisandro Dalcin        flags = flags.split()
896f46a955bSLisandro Dalcin
897f46a955bSLisandro Dalcin    switch = '-Wl,'
898f46a955bSLisandro Dalcin    newflags = []
899f46a955bSLisandro Dalcin    linkopts = []
900f46a955bSLisandro Dalcin    for f in flags:
901f46a955bSLisandro Dalcin        if f.startswith(switch):
902f46a955bSLisandro Dalcin            if len(f) > 4:
903f46a955bSLisandro Dalcin                append(linkopts, f[4:])
904f46a955bSLisandro Dalcin        else:
905f46a955bSLisandro Dalcin            append(newflags, f)
906f46a955bSLisandro Dalcin    if linkopts:
907f46a955bSLisandro Dalcin        newflags.append(switch + ','.join(linkopts))
908f46a955bSLisandro Dalcin    flags = newflags
909f46a955bSLisandro Dalcin
910f46a955bSLisandro Dalcin    append_next_word = None
911f46a955bSLisandro Dalcin
912f46a955bSLisandro Dalcin    for word in flags:
913f46a955bSLisandro Dalcin        if append_next_word is not None:
914f46a955bSLisandro Dalcin            append(append_next_word, word)
915f46a955bSLisandro Dalcin            append_next_word = None
916f46a955bSLisandro Dalcin            continue
917f46a955bSLisandro Dalcin
918f46a955bSLisandro Dalcin        switch, value = word[0:2], word[2:]
919f46a955bSLisandro Dalcin
9206f336411SStefano Zampini        if switch == '-I':
921f46a955bSLisandro Dalcin            append(conf['include_dirs'], value)
9226f336411SStefano Zampini        elif switch == '-D':
923f46a955bSLisandro Dalcin            try:
9246f336411SStefano Zampini                idx = value.index('=')
925f46a955bSLisandro Dalcin                macro = (value[:idx], value[idx + 1 :])
926f46a955bSLisandro Dalcin            except ValueError:
927f46a955bSLisandro Dalcin                macro = (value, None)
928f46a955bSLisandro Dalcin            append(conf['define_macros'], macro)
9296f336411SStefano Zampini        elif switch == '-U':
930f46a955bSLisandro Dalcin            append(conf['undef_macros'], value)
9316f336411SStefano Zampini        elif switch == '-l':
932f46a955bSLisandro Dalcin            append(conf['libraries'], value)
9336f336411SStefano Zampini        elif switch == '-L':
934f46a955bSLisandro Dalcin            append(conf['library_dirs'], value)
9356f336411SStefano Zampini        elif switch == '-R':
936f46a955bSLisandro Dalcin            append(conf['runtime_library_dirs'], value)
9376f336411SStefano Zampini        elif word.startswith('-Wl'):
938f46a955bSLisandro Dalcin            linkopts = word.split(',')
939f46a955bSLisandro Dalcin            append_dict(conf, flaglist(linkopts[1:]))
9406f336411SStefano Zampini        elif word == '-rpath':
941f46a955bSLisandro Dalcin            append_next_word = conf['runtime_library_dirs']
9426f336411SStefano Zampini        elif word == '-Xlinker':
943f46a955bSLisandro Dalcin            append_next_word = conf['extra_link_args']
944f46a955bSLisandro Dalcin        else:
945f46a955bSLisandro Dalcin            # log.warn("unrecognized flag '%s'" % word)
946f46a955bSLisandro Dalcin            pass
947f46a955bSLisandro Dalcin    return conf
948f46a955bSLisandro Dalcin
9496f336411SStefano Zampini
950f46a955bSLisandro Dalcindef prepend_to_flags(path, flags):
951f46a955bSLisandro Dalcin    """Prepend a path to compiler flags with absolute paths"""
952f46a955bSLisandro Dalcin    if not path:
953f46a955bSLisandro Dalcin        return flags
9546f336411SStefano Zampini
955f46a955bSLisandro Dalcin    def append_path(m):
956f46a955bSLisandro Dalcin        switch = m.group(1)
957f46a955bSLisandro Dalcin        open_quote = m.group(4)
958f46a955bSLisandro Dalcin        old_path = m.group(5)
959f46a955bSLisandro Dalcin        close_quote = m.group(6)
960f46a955bSLisandro Dalcin        if os.path.isabs(old_path):
961f46a955bSLisandro Dalcin            moded_path = os.path.normpath(path + os.path.sep + old_path)
962f46a955bSLisandro Dalcin            return switch + open_quote + moded_path + close_quote
963f46a955bSLisandro Dalcin        return m.group(0)
9646f336411SStefano Zampini
9656f336411SStefano Zampini    return re.sub(r'((^|\s+)(-I|-L))(\s*["\']?)(\S+)(["\']?)', append_path, flags)
9666f336411SStefano Zampini
967f46a955bSLisandro Dalcin
968f46a955bSLisandro Dalcindef strip_prefix(prefix, string):
969f46a955bSLisandro Dalcin    if not prefix:
970f46a955bSLisandro Dalcin        return string
971f46a955bSLisandro Dalcin    return re.sub(r'^' + prefix, '', string)
972f46a955bSLisandro Dalcin
9736f336411SStefano Zampini
974f46a955bSLisandro Dalcin# --------------------------------------------------------------------
975f46a955bSLisandro Dalcin
976f46a955bSLisandro Dalcin# Regexes needed for parsing Makefile-like syntaxes
9776f336411SStefano Zampini_variable_rx = re.compile(r'([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)')
9786f336411SStefano Zampini_findvar1_rx = re.compile(r'\$\(([A-Za-z][A-Za-z0-9_]*)\)')
9796f336411SStefano Zampini_findvar2_rx = re.compile(r'\${([A-Za-z][A-Za-z0-9_]*)}')
9806f336411SStefano Zampini
981f46a955bSLisandro Dalcin
982f46a955bSLisandro Dalcindef makefile(fileobj, dct=None):
983f46a955bSLisandro Dalcin    """Parse a Makefile-style file.
984f46a955bSLisandro Dalcin
985f46a955bSLisandro Dalcin    A dictionary containing name/value pairs is returned.  If an
986f46a955bSLisandro Dalcin    optional dictionary is passed in as the second argument, it is
987f46a955bSLisandro Dalcin    used instead of a new dictionary.
988f46a955bSLisandro Dalcin    """
9896f336411SStefano Zampini    fp = TextFile(file=fileobj, strip_comments=1, skip_blanks=1, join_lines=1)
990f46a955bSLisandro Dalcin
991f46a955bSLisandro Dalcin    if dct is None:
992f46a955bSLisandro Dalcin        dct = {}
993f46a955bSLisandro Dalcin    done = {}
994f46a955bSLisandro Dalcin    notdone = {}
995f46a955bSLisandro Dalcin
996f46a955bSLisandro Dalcin    while 1:
997f46a955bSLisandro Dalcin        line = fp.readline()
998f46a955bSLisandro Dalcin        if line is None:  # eof
999f46a955bSLisandro Dalcin            break
1000f46a955bSLisandro Dalcin        m = _variable_rx.match(line)
1001f46a955bSLisandro Dalcin        if m:
1002f46a955bSLisandro Dalcin            n, v = m.group(1, 2)
1003f46a955bSLisandro Dalcin            v = str.strip(v)
10046f336411SStefano Zampini            if '$' in v:
1005f46a955bSLisandro Dalcin                notdone[n] = v
1006f46a955bSLisandro Dalcin            else:
10076f336411SStefano Zampini                try:
10086f336411SStefano Zampini                    v = int(v)
10096f336411SStefano Zampini                except ValueError:
10106f336411SStefano Zampini                    pass
1011f46a955bSLisandro Dalcin                done[n] = v
10126f336411SStefano Zampini                try:
10136f336411SStefano Zampini                    del notdone[n]
10146f336411SStefano Zampini                except KeyError:
10156f336411SStefano Zampini                    pass
1016f46a955bSLisandro Dalcin    fp.close()
1017f46a955bSLisandro Dalcin
1018f46a955bSLisandro Dalcin    # do variable interpolation here
1019f46a955bSLisandro Dalcin    while notdone:
1020f46a955bSLisandro Dalcin        for name in list(notdone.keys()):
1021f46a955bSLisandro Dalcin            value = notdone[name]
1022f46a955bSLisandro Dalcin            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
1023f46a955bSLisandro Dalcin            if m:
1024f46a955bSLisandro Dalcin                n = m.group(1)
1025f46a955bSLisandro Dalcin                found = True
1026f46a955bSLisandro Dalcin                if n in done:
1027f46a955bSLisandro Dalcin                    item = str(done[n])
1028f46a955bSLisandro Dalcin                elif n in notdone:
1029f46a955bSLisandro Dalcin                    # get it on a subsequent round
1030f46a955bSLisandro Dalcin                    found = False
1031f46a955bSLisandro Dalcin                else:
10326f336411SStefano Zampini                    done[n] = item = ''
1033f46a955bSLisandro Dalcin                if found:
1034f46a955bSLisandro Dalcin                    after = value[m.end() :]
1035f46a955bSLisandro Dalcin                    value = value[: m.start()] + item + after
10366f336411SStefano Zampini                    if '$' in after:
1037f46a955bSLisandro Dalcin                        notdone[name] = value
1038f46a955bSLisandro Dalcin                    else:
10396f336411SStefano Zampini                        try:
10406f336411SStefano Zampini                            value = int(value)
1041f46a955bSLisandro Dalcin                        except ValueError:
1042f46a955bSLisandro Dalcin                            done[name] = str.strip(value)
1043f46a955bSLisandro Dalcin                        else:
1044f46a955bSLisandro Dalcin                            done[name] = value
1045f46a955bSLisandro Dalcin                        del notdone[name]
1046f46a955bSLisandro Dalcin            else:
1047f46a955bSLisandro Dalcin                # bogus variable reference;
1048f46a955bSLisandro Dalcin                # just drop it since we can't deal
1049f46a955bSLisandro Dalcin                del notdone[name]
1050f46a955bSLisandro Dalcin    # save the results in the global dictionary
1051f46a955bSLisandro Dalcin    dct.update(done)
1052f46a955bSLisandro Dalcin    return dct
1053f46a955bSLisandro Dalcin
10546f336411SStefano Zampini
1055f46a955bSLisandro Dalcin# --------------------------------------------------------------------
1056