xref: /petsc/src/binding/petsc4py/conf/confpetsc.py (revision 52c29a0d759b1922e7baf4f48548d20f97d09446)
1f46a955bSLisandro Dalcin# --------------------------------------------------------------------
2f46a955bSLisandro Dalcin
3f46a955bSLisandro Dalcinimport re
4f46a955bSLisandro Dalcinimport os
5f46a955bSLisandro Dalcinimport sys
6f46a955bSLisandro Dalcinimport glob
7f46a955bSLisandro Dalcinimport copy
8f46a955bSLisandro Dalcinimport warnings
96f336411SStefano Zampinifrom distutils import log
106f336411SStefano Zampinifrom distutils import sysconfig
116f336411SStefano Zampinifrom distutils.util import execute
126f336411SStefano Zampinifrom distutils.util import split_quoted
136f336411SStefano Zampinifrom distutils.errors import DistutilsError
146f336411SStefano Zampinifrom distutils.text_file import TextFile
156f336411SStefano Zampini
16f46a955bSLisandro Dalcin
17f46a955bSLisandro Dalcintry:
18f46a955bSLisandro Dalcin    from cStringIO import StringIO
19f46a955bSLisandro Dalcinexcept ImportError:
20f46a955bSLisandro Dalcin    from io import StringIO
21f46a955bSLisandro Dalcin
22f46a955bSLisandro Dalcintry:
23f46a955bSLisandro Dalcin    import setuptools
24f46a955bSLisandro Dalcinexcept ImportError:
25f46a955bSLisandro Dalcin    setuptools = None
26f46a955bSLisandro Dalcin
27f46a955bSLisandro Dalcinif setuptools:
28f46a955bSLisandro Dalcin    from setuptools import setup as _setup
29f46a955bSLisandro Dalcin    from setuptools import Extension as _Extension
30f46a955bSLisandro Dalcin    from setuptools import Command
31f46a955bSLisandro Dalcinelse:
32f46a955bSLisandro Dalcin    from distutils.core import setup as _setup
33f46a955bSLisandro Dalcin    from distutils.core import Extension as _Extension
34f46a955bSLisandro Dalcin    from distutils.core import Command
35f46a955bSLisandro Dalcin
366f336411SStefano Zampini
37f46a955bSLisandro Dalcindef import_command(cmd):
38f46a955bSLisandro Dalcin    try:
39f46a955bSLisandro Dalcin        from importlib import import_module
40f46a955bSLisandro Dalcin    except ImportError:
416f336411SStefano Zampini
42f46a955bSLisandro Dalcin        def import_module(n):
43f46a955bSLisandro Dalcin            return __import__(n, fromlist=[None])
446f336411SStefano Zampini
45f46a955bSLisandro Dalcin    try:
466f336411SStefano Zampini        if not setuptools:
476f336411SStefano Zampini            raise ImportError
48f46a955bSLisandro Dalcin        mod = import_module('setuptools.command.' + cmd)
49f46a955bSLisandro Dalcin        return getattr(mod, cmd)
50f46a955bSLisandro Dalcin    except ImportError:
51f46a955bSLisandro Dalcin        mod = import_module('distutils.command.' + cmd)
52f46a955bSLisandro Dalcin        return getattr(mod, cmd)
53f46a955bSLisandro Dalcin
546f336411SStefano Zampini
55f46a955bSLisandro Dalcin_config = import_command('config')
56f46a955bSLisandro Dalcin_build = import_command('build')
57f46a955bSLisandro Dalcin_build_ext = import_command('build_ext')
58f46a955bSLisandro Dalcin_install = import_command('install')
59f46a955bSLisandro Dalcin
60f46a955bSLisandro Dalcintry:
6156478d30SPierre Jolivet    from setuptools import modified
62f46a955bSLisandro Dalcinexcept ImportError:
6356478d30SPierre Jolivet    try:
6456478d30SPierre Jolivet        from setuptools import dep_util as modified
6556478d30SPierre Jolivet    except ImportError:
6656478d30SPierre Jolivet        from distutils import dep_util as modified
67f46a955bSLisandro Dalcin
68f46a955bSLisandro Dalcintry:
69f46a955bSLisandro Dalcin    from packaging.version import Version
70f46a955bSLisandro Dalcinexcept ImportError:
71f46a955bSLisandro Dalcin    try:
72f46a955bSLisandro Dalcin        from setuptools.extern.packaging.version import Version
73f46a955bSLisandro Dalcin    except ImportError:
74f46a955bSLisandro Dalcin        from distutils.version import StrictVersion as Version
75f46a955bSLisandro Dalcin
76f46a955bSLisandro Dalcin# --------------------------------------------------------------------
77f46a955bSLisandro Dalcin
78f46a955bSLisandro Dalcin# Cython
79f46a955bSLisandro Dalcin
80b12f50ebSLisandro DalcinCYTHON = '3.0.0'
81f46a955bSLisandro Dalcin
826f336411SStefano Zampini
83f46a955bSLisandro Dalcindef cython_req():
84f46a955bSLisandro Dalcin    return CYTHON
85f46a955bSLisandro Dalcin
866f336411SStefano Zampini
87f46a955bSLisandro Dalcindef cython_chk(VERSION, verbose=True):
88f46a955bSLisandro Dalcin    #
89f46a955bSLisandro Dalcin    def warn(message):
906f336411SStefano Zampini        if not verbose:
916f336411SStefano Zampini            return
926f336411SStefano Zampini        ruler, ws, nl = '*' * 80, ' ', '\n'
93f46a955bSLisandro Dalcin        pyexe = sys.executable
946f336411SStefano Zampini        advise = '$ %s -m pip install --upgrade cython' % pyexe
956f336411SStefano Zampini
966f336411SStefano Zampini        def printer(*s):
976f336411SStefano Zampini            sys.stderr.write(' '.join(s) + '\n')
986f336411SStefano Zampini
99f46a955bSLisandro Dalcin        printer(ruler, nl)
100f46a955bSLisandro Dalcin        printer(ws, message, nl)
101f46a955bSLisandro Dalcin        printer(ws, ws, advise, nl)
102f46a955bSLisandro Dalcin        printer(ruler)
1036f336411SStefano Zampini
104f46a955bSLisandro Dalcin    #
105f46a955bSLisandro Dalcin    try:
106f46a955bSLisandro Dalcin        import Cython
107f46a955bSLisandro Dalcin    except ImportError:
1086f336411SStefano Zampini        warn('You need Cython to generate C source files.')
109f46a955bSLisandro Dalcin        return False
110f46a955bSLisandro Dalcin    #
111f46a955bSLisandro Dalcin    CYTHON_VERSION = Cython.__version__
1126f336411SStefano Zampini    m = re.match(r'(\d+\.\d+(?:\.\d+)?).*', CYTHON_VERSION)
113f46a955bSLisandro Dalcin    if not m:
1146f336411SStefano Zampini        warn(f'Cannot parse Cython version string {CYTHON_VERSION!r}')
115f46a955bSLisandro Dalcin        return False
116f46a955bSLisandro Dalcin    REQUIRED = Version(VERSION)
117f46a955bSLisandro Dalcin    PROVIDED = Version(m.groups()[0])
118b12f50ebSLisandro Dalcin    if PROVIDED < REQUIRED:
1196f336411SStefano Zampini        warn(f'You need Cython >= {VERSION} (you have version {CYTHON_VERSION})')
120f46a955bSLisandro Dalcin        return False
121f46a955bSLisandro Dalcin    #
122f46a955bSLisandro Dalcin    if verbose:
1236f336411SStefano Zampini        log.info('using Cython %s' % CYTHON_VERSION)
124f46a955bSLisandro Dalcin    return True
125f46a955bSLisandro Dalcin
1266f336411SStefano Zampini
127f46a955bSLisandro Dalcindef cython_run(
1286f336411SStefano Zampini    source,
1296f336411SStefano Zampini    target=None,
1306f336411SStefano Zampini    depends=(),
1316f336411SStefano Zampini    includes=(),
1326f336411SStefano Zampini    workdir=None,
1336f336411SStefano Zampini    force=False,
1346f336411SStefano Zampini    VERSION='0.0',
135f46a955bSLisandro Dalcin):
136f46a955bSLisandro Dalcin    if target is None:
137f46a955bSLisandro Dalcin        target = os.path.splitext(source)[0] + '.c'
138f46a955bSLisandro Dalcin    cwd = os.getcwd()
139f46a955bSLisandro Dalcin    try:
140f46a955bSLisandro Dalcin        if workdir:
141f46a955bSLisandro Dalcin            os.chdir(workdir)
142f46a955bSLisandro Dalcin        alldeps = [source]
143f46a955bSLisandro Dalcin        for dep in depends:
144f46a955bSLisandro Dalcin            alldeps += glob.glob(dep)
14556478d30SPierre Jolivet        if not (force or modified.newer_group(alldeps, target)):
1466f336411SStefano Zampini            log.debug("skipping '%s' -> '%s' (up-to-date)", source, target)
147f46a955bSLisandro Dalcin            return
148f46a955bSLisandro Dalcin    finally:
149f46a955bSLisandro Dalcin        os.chdir(cwd)
15080e0d92bSLisandro Dalcin    require = 'Cython >= %s' % VERSION
151f46a955bSLisandro Dalcin    if setuptools and not cython_chk(VERSION, verbose=False):
152f46a955bSLisandro Dalcin        if sys.modules.get('Cython'):
153f46a955bSLisandro Dalcin            removed = getattr(sys.modules['Cython'], '__version__', '')
1546f336411SStefano Zampini            log.info('removing Cython %s from sys.modules' % removed)
155f46a955bSLisandro Dalcin            pkgname = re.compile(r'cython(\.|$)', re.IGNORECASE)
156f46a955bSLisandro Dalcin            for modname in list(sys.modules.keys()):
157f46a955bSLisandro Dalcin                if pkgname.match(modname):
158f46a955bSLisandro Dalcin                    del sys.modules[modname]
159f46a955bSLisandro Dalcin        try:
160f46a955bSLisandro Dalcin            install_setup_requires = setuptools._install_setup_requires
161f46a955bSLisandro Dalcin            with warnings.catch_warnings():
162f46a955bSLisandro Dalcin                if hasattr(setuptools, 'SetuptoolsDeprecationWarning'):
163f46a955bSLisandro Dalcin                    category = setuptools.SetuptoolsDeprecationWarning
164f46a955bSLisandro Dalcin                    warnings.simplefilter('ignore', category)
165f46a955bSLisandro Dalcin                log.info("fetching build requirement '%s'" % require)
1666f336411SStefano Zampini                install_setup_requires({'setup_requires': [require]})
167f46a955bSLisandro Dalcin        except Exception:
168f46a955bSLisandro Dalcin            log.info("failed to fetch build requirement '%s'" % require)
169f46a955bSLisandro Dalcin    if not cython_chk(VERSION):
170f46a955bSLisandro Dalcin        raise DistutilsError("unsatisfied build requirement '%s'" % require)
171f46a955bSLisandro Dalcin    #
172f46a955bSLisandro Dalcin    log.info("cythonizing '%s' -> '%s'", source, target)
173f46a955bSLisandro Dalcin    from cythonize import cythonize
1746f336411SStefano Zampini
175f46a955bSLisandro Dalcin    args = []
176f46a955bSLisandro Dalcin    if workdir:
177f46a955bSLisandro Dalcin        args += ['--working', workdir]
178f46a955bSLisandro Dalcin    args += [source]
179f46a955bSLisandro Dalcin    if target:
180f46a955bSLisandro Dalcin        args += ['--output-file', target]
181f46a955bSLisandro Dalcin    err = cythonize(args)
182f46a955bSLisandro Dalcin    if err:
1836f336411SStefano Zampini        raise DistutilsError(f"Cython failure: '{source}' -> '{target}'")
184f46a955bSLisandro Dalcin
185f46a955bSLisandro Dalcin
186f46a955bSLisandro Dalcin# --------------------------------------------------------------------
187f46a955bSLisandro Dalcin
1886f336411SStefano Zampini
189f46a955bSLisandro Dalcindef fix_config_vars(names, values):
190f46a955bSLisandro Dalcin    values = list(values)
191f46a955bSLisandro Dalcin    if 'CONDA_BUILD' in os.environ:
192f46a955bSLisandro Dalcin        return values
193f46a955bSLisandro Dalcin    if sys.platform == 'darwin':
194f46a955bSLisandro Dalcin        if 'ARCHFLAGS' in os.environ:
195f46a955bSLisandro Dalcin            ARCHFLAGS = os.environ['ARCHFLAGS']
196f46a955bSLisandro Dalcin            for i, flag in enumerate(list(values)):
1978acd7bfaSLisandro Dalcin                flag, count = re.subn(r'-arch\s+\w+', ' ', str(flag))
198f46a955bSLisandro Dalcin                if count and ARCHFLAGS:
199f46a955bSLisandro Dalcin                    flag = flag + ' ' + ARCHFLAGS
200f46a955bSLisandro Dalcin                values[i] = flag
201f46a955bSLisandro Dalcin        if 'SDKROOT' in os.environ:
202f46a955bSLisandro Dalcin            SDKROOT = os.environ['SDKROOT']
203f46a955bSLisandro Dalcin            for i, flag in enumerate(list(values)):
2048acd7bfaSLisandro Dalcin                flag, count = re.subn(r'-isysroot [^ \t]*', ' ', str(flag))
205f46a955bSLisandro Dalcin                if count and SDKROOT:
206f46a955bSLisandro Dalcin                    flag = flag + ' ' + '-isysroot ' + SDKROOT
207f46a955bSLisandro Dalcin                values[i] = flag
208f46a955bSLisandro Dalcin    return values
209f46a955bSLisandro Dalcin
2106f336411SStefano Zampini
211f46a955bSLisandro Dalcindef get_config_vars(*names):
212f46a955bSLisandro Dalcin    # Core Python configuration
213f46a955bSLisandro Dalcin    values = sysconfig.get_config_vars(*names)
214f46a955bSLisandro Dalcin    # Do any distutils flags fixup right now
2156f336411SStefano Zampini    return fix_config_vars(names, values)
2166f336411SStefano Zampini
217f46a955bSLisandro Dalcin
218f46a955bSLisandro Dalcin# --------------------------------------------------------------------
219f46a955bSLisandro Dalcin
220f46a955bSLisandro Dalcin
2216f336411SStefano Zampiniclass PetscConfig:
222f46a955bSLisandro Dalcin    def __init__(self, petsc_dir, petsc_arch, dest_dir=None):
223f46a955bSLisandro Dalcin        if dest_dir is None:
224f46a955bSLisandro Dalcin            dest_dir = os.environ.get('DESTDIR')
225f46a955bSLisandro Dalcin        self.configdict = {}
226f46a955bSLisandro Dalcin        if not petsc_dir:
2276f336411SStefano Zampini            raise DistutilsError('PETSc not found')
228f46a955bSLisandro Dalcin        if not os.path.isdir(petsc_dir):
2296f336411SStefano Zampini            raise DistutilsError('invalid PETSC_DIR: %s' % petsc_dir)
230f46a955bSLisandro Dalcin        self.version = self._get_petsc_version(petsc_dir)
231f46a955bSLisandro Dalcin        self.configdict = self._get_petsc_config(petsc_dir, petsc_arch)
232f46a955bSLisandro Dalcin        self.PETSC_DIR = self['PETSC_DIR']
233f46a955bSLisandro Dalcin        self.PETSC_ARCH = self['PETSC_ARCH']
234f46a955bSLisandro Dalcin        self.DESTDIR = dest_dir
235f46a955bSLisandro Dalcin        language_map = {'CONLY': 'c', 'CXXONLY': 'c++'}
236f46a955bSLisandro Dalcin        self.language = language_map[self['PETSC_LANGUAGE']]
237f46a955bSLisandro Dalcin
238f46a955bSLisandro Dalcin    def __getitem__(self, item):
239f46a955bSLisandro Dalcin        return self.configdict[item]
240f46a955bSLisandro Dalcin
241f46a955bSLisandro Dalcin    def get(self, item, default=None):
242f46a955bSLisandro Dalcin        return self.configdict.get(item, default)
243f46a955bSLisandro Dalcin
244f46a955bSLisandro Dalcin    def configure(self, extension, compiler=None):
245f46a955bSLisandro Dalcin        self.configure_extension(extension)
246f46a955bSLisandro Dalcin        if compiler is not None:
247f46a955bSLisandro Dalcin            self.configure_compiler(compiler)
248f46a955bSLisandro Dalcin
249f46a955bSLisandro Dalcin    def _get_petsc_version(self, petsc_dir):
250f46a955bSLisandro Dalcin        import re
2516f336411SStefano Zampini
252f46a955bSLisandro Dalcin        version_re = {
2536f336411SStefano Zampini            'major': re.compile(r'#define\s+PETSC_VERSION_MAJOR\s+(\d+)'),
2546f336411SStefano Zampini            'minor': re.compile(r'#define\s+PETSC_VERSION_MINOR\s+(\d+)'),
2556f336411SStefano Zampini            'micro': re.compile(r'#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)'),
2566f336411SStefano Zampini            'release': re.compile(r'#define\s+PETSC_VERSION_RELEASE\s+(-*\d+)'),
257f46a955bSLisandro Dalcin        }
258f46a955bSLisandro Dalcin        petscversion_h = os.path.join(petsc_dir, 'include', 'petscversion.h')
2596f336411SStefano Zampini        with open(petscversion_h, 'rt') as f:
2606f336411SStefano Zampini            data = f.read()
261f46a955bSLisandro Dalcin        major = int(version_re['major'].search(data).groups()[0])
262f46a955bSLisandro Dalcin        minor = int(version_re['minor'].search(data).groups()[0])
263f46a955bSLisandro Dalcin        micro = int(version_re['micro'].search(data).groups()[0])
264f46a955bSLisandro Dalcin        release = int(version_re['release'].search(data).groups()[0])
265f46a955bSLisandro Dalcin        return (major, minor, micro), (release == 1)
266f46a955bSLisandro Dalcin
267f46a955bSLisandro Dalcin    def _get_petsc_config(self, petsc_dir, petsc_arch):
268f46a955bSLisandro Dalcin        from os.path import join, isdir, exists
2696f336411SStefano Zampini
270f46a955bSLisandro Dalcin        PETSC_DIR = petsc_dir
271f46a955bSLisandro Dalcin        PETSC_ARCH = petsc_arch
272f46a955bSLisandro Dalcin        #
273f46a955bSLisandro Dalcin        confdir = join('lib', 'petsc', 'conf')
274f46a955bSLisandro Dalcin        if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))):
275f46a955bSLisandro Dalcin            petscvars = join(PETSC_DIR, confdir, 'petscvariables')
276f46a955bSLisandro Dalcin            PETSC_ARCH = makefile(open(petscvars, 'rt')).get('PETSC_ARCH')
277f46a955bSLisandro Dalcin        if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))):
278f46a955bSLisandro Dalcin            PETSC_ARCH = ''
279f46a955bSLisandro Dalcin        #
280f46a955bSLisandro Dalcin        variables = join(PETSC_DIR, confdir, 'variables')
281f46a955bSLisandro Dalcin        if not exists(variables):
282f46a955bSLisandro Dalcin            variables = join(PETSC_DIR, PETSC_ARCH, confdir, 'variables')
283f46a955bSLisandro Dalcin        petscvariables = join(PETSC_DIR, PETSC_ARCH, confdir, 'petscvariables')
284f46a955bSLisandro Dalcin        #
285f46a955bSLisandro Dalcin        with open(variables) as f:
286f46a955bSLisandro Dalcin            contents = f.read()
287f46a955bSLisandro Dalcin        with open(petscvariables) as f:
288f46a955bSLisandro Dalcin            contents += f.read()
289f46a955bSLisandro Dalcin        #
290f46a955bSLisandro Dalcin        confstr = 'PETSC_DIR  = %s\n' % PETSC_DIR
291f46a955bSLisandro Dalcin        confstr += 'PETSC_ARCH = %s\n' % PETSC_ARCH
292f46a955bSLisandro Dalcin        confstr += contents
2936f336411SStefano Zampini        return makefile(StringIO(confstr))
294f46a955bSLisandro Dalcin
295ec81f945SPierre Jolivet    def _configure_ext(self, ext, dct, append=False):
296f46a955bSLisandro Dalcin        extdict = ext.__dict__
297f46a955bSLisandro Dalcin        for key, values in dct.items():
298f46a955bSLisandro Dalcin            if key in extdict:
299f46a955bSLisandro Dalcin                for value in values:
300f46a955bSLisandro Dalcin                    if value not in extdict[key]:
301ec81f945SPierre Jolivet                        if not append:
302f46a955bSLisandro Dalcin                            extdict[key].insert(0, value)
303f46a955bSLisandro Dalcin                        else:
304f46a955bSLisandro Dalcin                            extdict[key].append(value)
305f46a955bSLisandro Dalcin
306f46a955bSLisandro Dalcin    def configure_extension(self, extension):
307f46a955bSLisandro Dalcin        # includes and libraries
308f46a955bSLisandro Dalcin        # paths in PETSc config files point to final installation location, but
309f46a955bSLisandro Dalcin        # we might be building against PETSc in staging location (DESTDIR) when
310f46a955bSLisandro Dalcin        # DESTDIR is set, so append DESTDIR (if nonempty) to those paths
311f46a955bSLisandro Dalcin        petsc_inc = flaglist(prepend_to_flags(self.DESTDIR, self['PETSC_CC_INCLUDES']))
3126f336411SStefano Zampini        lib_flags = prepend_to_flags(
3136f336411SStefano Zampini            self.DESTDIR,
3146f336411SStefano Zampini            '-L{} {}'.format(self['PETSC_LIB_DIR'], self['PETSC_LIB_BASIC']),
3156f336411SStefano Zampini        )
316f46a955bSLisandro Dalcin        petsc_lib = flaglist(lib_flags)
317f46a955bSLisandro Dalcin        # runtime_library_dirs is not supported on Windows
318f46a955bSLisandro Dalcin        if sys.platform != 'win32':
319f46a955bSLisandro Dalcin            # if DESTDIR is set, then we're building against PETSc in a staging
320f46a955bSLisandro Dalcin            # directory, but rpath needs to point to final install directory.
321693647e8SLisandro Dalcin            rpath = [strip_prefix(self.DESTDIR, self['PETSC_LIB_DIR'])]
322693647e8SLisandro Dalcin            if sys.modules.get('petsc') is not None:
323693647e8SLisandro Dalcin                if sys.platform == 'darwin':
324693647e8SLisandro Dalcin                    rpath = ['@loader_path/../../petsc/lib']
325693647e8SLisandro Dalcin                else:
326693647e8SLisandro Dalcin                    rpath = ['$ORIGIN/../../petsc/lib']
327693647e8SLisandro Dalcin            petsc_lib['runtime_library_dirs'].extend(rpath)
328f46a955bSLisandro Dalcin        # Link in extra libraries on static builds
329f46a955bSLisandro Dalcin        if self['BUILDSHAREDLIB'] != 'yes':
330f46a955bSLisandro Dalcin            petsc_ext_lib = split_quoted(self['PETSC_EXTERNAL_LIB_BASIC'])
331f46a955bSLisandro Dalcin            petsc_lib['extra_link_args'].extend(petsc_ext_lib)
332ec81f945SPierre Jolivet        self._configure_ext(extension, petsc_inc, append=True)
333f46a955bSLisandro Dalcin        self._configure_ext(extension, petsc_lib)
334f46a955bSLisandro Dalcin
335f46a955bSLisandro Dalcin    def configure_compiler(self, compiler):
3366f336411SStefano Zampini        if compiler.compiler_type != 'unix':
3376f336411SStefano Zampini            return
338f46a955bSLisandro Dalcin        getenv = os.environ.get
339f46a955bSLisandro Dalcin        # distutils C/C++ compiler
3406f336411SStefano Zampini        (cc, cflags, ccshared, cxx) = get_config_vars('CC', 'CFLAGS', 'CCSHARED', 'CXX')
341f46a955bSLisandro Dalcin        ccshared = getenv('CCSHARED', ccshared or '')
342f46a955bSLisandro Dalcin        cflags = getenv('CFLAGS', cflags or '')
343f46a955bSLisandro Dalcin        cflags = cflags.replace('-Wstrict-prototypes', '')
344f46a955bSLisandro Dalcin        # distutils linker
3456f336411SStefano Zampini        (ldflags, ldshared, so_ext) = get_config_vars('LDFLAGS', 'LDSHARED', 'SO')
346f46a955bSLisandro Dalcin        ld = cc
347f46a955bSLisandro Dalcin        ldshared = getenv('LDSHARED', ldshared)
348f46a955bSLisandro Dalcin        ldflags = getenv('LDFLAGS', cflags + ' ' + (ldflags or ''))
349f46a955bSLisandro Dalcin        ldcmd = split_quoted(ld) + split_quoted(ldflags)
3506f336411SStefano Zampini        ldshared = [
3516f336411SStefano Zampini            flg
3526f336411SStefano Zampini            for flg in split_quoted(ldshared)
3536f336411SStefano Zampini            if flg not in ldcmd and (flg.find('/lib/spack/env') < 0)
3546f336411SStefano Zampini        ]
355f46a955bSLisandro Dalcin        ldshared = str.join(' ', ldshared)
3566f336411SStefano Zampini
357f46a955bSLisandro Dalcin        #
358f46a955bSLisandro Dalcin        def get_flags(cmd):
3596f336411SStefano Zampini            if not cmd:
3606f336411SStefano Zampini                return ''
361f46a955bSLisandro Dalcin            cmd = split_quoted(cmd)
362f46a955bSLisandro Dalcin            if os.path.basename(cmd[0]) == 'xcrun':
363f46a955bSLisandro Dalcin                del cmd[0]
364f46a955bSLisandro Dalcin                while True:
365f46a955bSLisandro Dalcin                    if cmd[0] == '-sdk':
366f46a955bSLisandro Dalcin                        del cmd[0:2]
367f46a955bSLisandro Dalcin                        continue
368f46a955bSLisandro Dalcin                    if cmd[0] == '-log':
369f46a955bSLisandro Dalcin                        del cmd[0]
370f46a955bSLisandro Dalcin                        continue
371f46a955bSLisandro Dalcin                    break
372f46a955bSLisandro Dalcin            return ' '.join(cmd[1:])
3736f336411SStefano Zampini
374f46a955bSLisandro Dalcin        # PETSc C compiler
375f46a955bSLisandro Dalcin        PCC = self['PCC']
376f46a955bSLisandro Dalcin        PCC_FLAGS = get_flags(cc) + ' ' + self['PCC_FLAGS']
377f46a955bSLisandro Dalcin        PCC_FLAGS = PCC_FLAGS.replace('-fvisibility=hidden', '')
378*52c29a0dSSatish Balay        PCC_FLAGS = PCC_FLAGS.replace('-Wpedantic', '-Wno-pedantic')
379*52c29a0dSSatish Balay        PCC_FLAGS = PCC_FLAGS.replace('-Wextra-semi-stmt', '-Wno-extra-semi-stmt')
380f46a955bSLisandro Dalcin        PCC = getenv('PCC', PCC) + ' ' + getenv('PCCFLAGS', PCC_FLAGS)
381f46a955bSLisandro Dalcin        PCC_SHARED = str.join(' ', (PCC, ccshared, cflags))
382f46a955bSLisandro Dalcin        # PETSc C++ compiler
383f46a955bSLisandro Dalcin        PCXX = PCC if self.language == 'c++' else self.get('CXX', cxx)
384f46a955bSLisandro Dalcin        # PETSc linker
385f46a955bSLisandro Dalcin        PLD = self['PCC_LINKER']
386f46a955bSLisandro Dalcin        PLD_FLAGS = get_flags(ld) + ' ' + self['PCC_LINKER_FLAGS']
387f46a955bSLisandro Dalcin        PLD_FLAGS = PLD_FLAGS.replace('-fvisibility=hidden', '')
388f46a955bSLisandro Dalcin        PLD = getenv('PLD', PLD) + ' ' + getenv('PLDFLAGS', PLD_FLAGS)
389f46a955bSLisandro Dalcin        PLD_SHARED = str.join(' ', (PLD, ldshared, ldflags))
390f46a955bSLisandro Dalcin        #
391f46a955bSLisandro Dalcin        compiler.set_executables(
392f46a955bSLisandro Dalcin            compiler=PCC,
393f46a955bSLisandro Dalcin            compiler_cxx=PCXX,
394f46a955bSLisandro Dalcin            linker_exe=PLD,
395f46a955bSLisandro Dalcin            compiler_so=PCC_SHARED,
396f46a955bSLisandro Dalcin            linker_so=PLD_SHARED,
397f46a955bSLisandro Dalcin        )
398f46a955bSLisandro Dalcin        compiler.shared_lib_extension = so_ext
399f46a955bSLisandro Dalcin
400f46a955bSLisandro Dalcin    def log_info(self):
401f46a955bSLisandro Dalcin        PETSC_DIR = self['PETSC_DIR']
402f46a955bSLisandro Dalcin        PETSC_ARCH = self['PETSC_ARCH']
4036f336411SStefano Zampini        version = '.'.join([str(i) for i in self.version[0]])
4046f336411SStefano Zampini        release = ('development', 'release')[self.version[1]]
405f46a955bSLisandro Dalcin        version_info = version + ' ' + release
406f46a955bSLisandro Dalcin        integer_size = '%s-bit' % self['PETSC_INDEX_SIZE']
407f46a955bSLisandro Dalcin        scalar_type = self['PETSC_SCALAR']
408f46a955bSLisandro Dalcin        precision = self['PETSC_PRECISION']
409f46a955bSLisandro Dalcin        language = self['PETSC_LANGUAGE']
410f46a955bSLisandro Dalcin        compiler = self['PCC']
411f46a955bSLisandro Dalcin        linker = self['PCC_LINKER']
412f46a955bSLisandro Dalcin        log.info('PETSC_DIR:    %s' % PETSC_DIR)
413f46a955bSLisandro Dalcin        log.info('PETSC_ARCH:   %s' % PETSC_ARCH)
414f46a955bSLisandro Dalcin        log.info('version:      %s' % version_info)
415f46a955bSLisandro Dalcin        log.info('integer-size: %s' % integer_size)
416f46a955bSLisandro Dalcin        log.info('scalar-type:  %s' % scalar_type)
417f46a955bSLisandro Dalcin        log.info('precision:    %s' % precision)
418f46a955bSLisandro Dalcin        log.info('language:     %s' % language)
419f46a955bSLisandro Dalcin        log.info('compiler:     %s' % compiler)
420f46a955bSLisandro Dalcin        log.info('linker:       %s' % linker)
421f46a955bSLisandro Dalcin
4226f336411SStefano Zampini
423f46a955bSLisandro Dalcin# --------------------------------------------------------------------
424f46a955bSLisandro Dalcin
4256f336411SStefano Zampini
426f46a955bSLisandro Dalcinclass Extension(_Extension):
427f46a955bSLisandro Dalcin    pass
428f46a955bSLisandro Dalcin
4296f336411SStefano Zampini
430f46a955bSLisandro Dalcin# --------------------------------------------------------------------
431f46a955bSLisandro Dalcin
432f46a955bSLisandro Dalcincmd_petsc_opts = [
4336f336411SStefano Zampini    ('petsc-dir=', None, 'define PETSC_DIR, overriding environmental variables'),
4346f336411SStefano Zampini    ('petsc-arch=', None, 'define PETSC_ARCH, overriding environmental variables'),
435f46a955bSLisandro Dalcin]
436f46a955bSLisandro Dalcin
437f46a955bSLisandro Dalcin
438f46a955bSLisandro Dalcinclass config(_config):
439f46a955bSLisandro Dalcin    Configure = PetscConfig
440f46a955bSLisandro Dalcin
441f46a955bSLisandro Dalcin    user_options = _config.user_options + cmd_petsc_opts
442f46a955bSLisandro Dalcin
443f46a955bSLisandro Dalcin    def initialize_options(self):
444f46a955bSLisandro Dalcin        _config.initialize_options(self)
445f46a955bSLisandro Dalcin        self.petsc_dir = None
446f46a955bSLisandro Dalcin        self.petsc_arch = None
447f46a955bSLisandro Dalcin
448f46a955bSLisandro Dalcin    def get_config_arch(self, arch):
449f46a955bSLisandro Dalcin        return config.Configure(self.petsc_dir, arch)
450f46a955bSLisandro Dalcin
451f46a955bSLisandro Dalcin    def run(self):
452f46a955bSLisandro Dalcin        _config.run(self)
453f46a955bSLisandro Dalcin        self.petsc_dir = config.get_petsc_dir(self.petsc_dir)
4546f336411SStefano Zampini        if self.petsc_dir is None:
4556f336411SStefano Zampini            return
456f46a955bSLisandro Dalcin        petsc_arch = config.get_petsc_arch(self.petsc_dir, self.petsc_arch)
457f46a955bSLisandro Dalcin        log.info('-' * 70)
458f46a955bSLisandro Dalcin        log.info('PETSC_DIR:   %s' % self.petsc_dir)
459f46a955bSLisandro Dalcin        arch_list = petsc_arch
460f46a955bSLisandro Dalcin        if not arch_list:
461f46a955bSLisandro Dalcin            arch_list = [None]
462f46a955bSLisandro Dalcin        for arch in arch_list:
463f46a955bSLisandro Dalcin            conf = self.get_config_arch(arch)
464f46a955bSLisandro Dalcin            archname = conf.PETSC_ARCH or conf['PETSC_ARCH']
465f46a955bSLisandro Dalcin            scalar_type = conf['PETSC_SCALAR']
466f46a955bSLisandro Dalcin            precision = conf['PETSC_PRECISION']
467f46a955bSLisandro Dalcin            language = conf['PETSC_LANGUAGE']
468f46a955bSLisandro Dalcin            compiler = conf['PCC']
469f46a955bSLisandro Dalcin            linker = conf['PCC_LINKER']
470f46a955bSLisandro Dalcin            log.info('-' * 70)
471f46a955bSLisandro Dalcin            log.info('PETSC_ARCH:  %s' % archname)
472f46a955bSLisandro Dalcin            log.info(' * scalar-type: %s' % scalar_type)
473f46a955bSLisandro Dalcin            log.info(' * precision:   %s' % precision)
474f46a955bSLisandro Dalcin            log.info(' * language:    %s' % language)
475f46a955bSLisandro Dalcin            log.info(' * compiler:    %s' % compiler)
476f46a955bSLisandro Dalcin            log.info(' * linker:      %s' % linker)
477f46a955bSLisandro Dalcin        log.info('-' * 70)
478f46a955bSLisandro Dalcin
479f46a955bSLisandro Dalcin    # @staticmethod
480f46a955bSLisandro Dalcin    def get_petsc_dir(petsc_dir):
4816f336411SStefano Zampini        if not petsc_dir:
4826f336411SStefano Zampini            return None
483f46a955bSLisandro Dalcin        petsc_dir = os.path.expandvars(petsc_dir)
484f46a955bSLisandro Dalcin        if not petsc_dir or '$PETSC_DIR' in petsc_dir:
485f46a955bSLisandro Dalcin            try:
486f46a955bSLisandro Dalcin                import petsc
4876f336411SStefano Zampini
488f46a955bSLisandro Dalcin                petsc_dir = petsc.get_petsc_dir()
489f46a955bSLisandro Dalcin            except ImportError:
4906f336411SStefano Zampini                log.warn('PETSC_DIR not specified')
491f46a955bSLisandro Dalcin                return None
492f46a955bSLisandro Dalcin        petsc_dir = os.path.expanduser(petsc_dir)
493f46a955bSLisandro Dalcin        petsc_dir = os.path.abspath(petsc_dir)
494f46a955bSLisandro Dalcin        return config.chk_petsc_dir(petsc_dir)
4956f336411SStefano Zampini
496f46a955bSLisandro Dalcin    get_petsc_dir = staticmethod(get_petsc_dir)
497f46a955bSLisandro Dalcin
498f46a955bSLisandro Dalcin    # @staticmethod
499f46a955bSLisandro Dalcin    def chk_petsc_dir(petsc_dir):
500f46a955bSLisandro Dalcin        if not os.path.isdir(petsc_dir):
501f46a955bSLisandro Dalcin            log.error('invalid PETSC_DIR: %s (ignored)' % petsc_dir)
502f46a955bSLisandro Dalcin            return None
503f46a955bSLisandro Dalcin        return petsc_dir
5046f336411SStefano Zampini
505f46a955bSLisandro Dalcin    chk_petsc_dir = staticmethod(chk_petsc_dir)
506f46a955bSLisandro Dalcin
507f46a955bSLisandro Dalcin    # @staticmethod
508f46a955bSLisandro Dalcin    def get_petsc_arch(petsc_dir, petsc_arch):
5096f336411SStefano Zampini        if not petsc_dir:
5106f336411SStefano Zampini            return None
511f46a955bSLisandro Dalcin        petsc_arch = os.path.expandvars(petsc_arch)
5126f336411SStefano Zampini        if not petsc_arch or '$PETSC_ARCH' in petsc_arch:
513f46a955bSLisandro Dalcin            petsc_arch = ''
514f46a955bSLisandro Dalcin            petsc_conf = os.path.join(petsc_dir, 'lib', 'petsc', 'conf')
515f46a955bSLisandro Dalcin            if os.path.isdir(petsc_conf):
516f46a955bSLisandro Dalcin                petscvariables = os.path.join(petsc_conf, 'petscvariables')
517f46a955bSLisandro Dalcin                if os.path.exists(petscvariables):
518f46a955bSLisandro Dalcin                    conf = makefile(open(petscvariables, 'rt'))
519f46a955bSLisandro Dalcin                    petsc_arch = conf.get('PETSC_ARCH', '')
520f46a955bSLisandro Dalcin        petsc_arch = petsc_arch.split(os.pathsep)
521f46a955bSLisandro Dalcin        petsc_arch = unique(petsc_arch)
522f46a955bSLisandro Dalcin        petsc_arch = [arch for arch in petsc_arch if arch]
523f46a955bSLisandro Dalcin        return config.chk_petsc_arch(petsc_dir, petsc_arch)
5246f336411SStefano Zampini
525f46a955bSLisandro Dalcin    get_petsc_arch = staticmethod(get_petsc_arch)
526f46a955bSLisandro Dalcin
527f46a955bSLisandro Dalcin    # @staticmethod
528f46a955bSLisandro Dalcin    def chk_petsc_arch(petsc_dir, petsc_arch):
529f46a955bSLisandro Dalcin        valid_archs = []
530f46a955bSLisandro Dalcin        for arch in petsc_arch:
531f46a955bSLisandro Dalcin            arch_path = os.path.join(petsc_dir, arch)
532f46a955bSLisandro Dalcin            if os.path.isdir(arch_path):
533f46a955bSLisandro Dalcin                valid_archs.append(arch)
534f46a955bSLisandro Dalcin            else:
5356f336411SStefano Zampini                log.warn('invalid PETSC_ARCH: %s (ignored)' % arch)
536f46a955bSLisandro Dalcin        return valid_archs
5376f336411SStefano Zampini
538f46a955bSLisandro Dalcin    chk_petsc_arch = staticmethod(chk_petsc_arch)
539f46a955bSLisandro Dalcin
540f46a955bSLisandro Dalcin
541f46a955bSLisandro Dalcinclass build(_build):
54255a74a43SLisandro Dalcin    user_options = _build.user_options
5436f336411SStefano Zampini    user_options += [
5446f336411SStefano Zampini        (
54555a74a43SLisandro Dalcin            'inplace',
54655a74a43SLisandro Dalcin            'i',
5476f336411SStefano Zampini            'ignore build-lib and put compiled extensions into the source '
5486f336411SStefano Zampini            'directory alongside your pure Python modules',
5496f336411SStefano Zampini        )
5506f336411SStefano Zampini    ]
55155a74a43SLisandro Dalcin    user_options += cmd_petsc_opts
55255a74a43SLisandro Dalcin
55355a74a43SLisandro Dalcin    boolean_options = _build.boolean_options
55455a74a43SLisandro Dalcin    boolean_options += ['inplace']
555f46a955bSLisandro Dalcin
556f46a955bSLisandro Dalcin    def initialize_options(self):
557f46a955bSLisandro Dalcin        _build.initialize_options(self)
55855a74a43SLisandro Dalcin        self.inplace = None
559f46a955bSLisandro Dalcin        self.petsc_dir = None
560f46a955bSLisandro Dalcin        self.petsc_arch = None
561f46a955bSLisandro Dalcin
562f46a955bSLisandro Dalcin    def finalize_options(self):
563f46a955bSLisandro Dalcin        _build.finalize_options(self)
56455a74a43SLisandro Dalcin        if self.inplace is None:
56555a74a43SLisandro Dalcin            self.inplace = False
5666f336411SStefano Zampini        self.set_undefined_options(
5676f336411SStefano Zampini            'config', ('petsc_dir', 'petsc_dir'), ('petsc_arch', 'petsc_arch')
5686f336411SStefano Zampini        )
569f46a955bSLisandro Dalcin        self.petsc_dir = config.get_petsc_dir(self.petsc_dir)
5706f336411SStefano Zampini        self.petsc_arch = config.get_petsc_arch(self.petsc_dir, self.petsc_arch)
571f46a955bSLisandro Dalcin
5726f336411SStefano Zampini    sub_commands = [('build_src', lambda *args: True)] + _build.sub_commands
573f46a955bSLisandro Dalcin
574f46a955bSLisandro Dalcin
575f46a955bSLisandro Dalcinclass build_src(Command):
5766f336411SStefano Zampini    description = 'build C sources from Cython files'
577f46a955bSLisandro Dalcin
578f46a955bSLisandro Dalcin    user_options = [
5796f336411SStefano Zampini        ('force', 'f', 'forcibly build everything (ignore file timestamps)'),
580f46a955bSLisandro Dalcin    ]
581f46a955bSLisandro Dalcin
582f46a955bSLisandro Dalcin    boolean_options = ['force']
583f46a955bSLisandro Dalcin
584f46a955bSLisandro Dalcin    def initialize_options(self):
585f46a955bSLisandro Dalcin        self.force = False
586f46a955bSLisandro Dalcin
587f46a955bSLisandro Dalcin    def finalize_options(self):
5886f336411SStefano Zampini        self.set_undefined_options(
5896f336411SStefano Zampini            'build',
590f46a955bSLisandro Dalcin            ('force', 'force'),
591f46a955bSLisandro Dalcin        )
592f46a955bSLisandro Dalcin
593f46a955bSLisandro Dalcin    def run(self):
594f46a955bSLisandro Dalcin        sources = getattr(self, 'sources', [])
595f46a955bSLisandro Dalcin        for source in sources:
5966f336411SStefano Zampini            cython_run(force=self.force, VERSION=cython_req(), **source)
597f46a955bSLisandro Dalcin
598f46a955bSLisandro Dalcin
599f46a955bSLisandro Dalcinclass build_ext(_build_ext):
600f46a955bSLisandro Dalcin    user_options = _build_ext.user_options + cmd_petsc_opts
601f46a955bSLisandro Dalcin
602f46a955bSLisandro Dalcin    def initialize_options(self):
603f46a955bSLisandro Dalcin        _build_ext.initialize_options(self)
60455a74a43SLisandro Dalcin        self.inplace = None
605f46a955bSLisandro Dalcin        self.petsc_dir = None
606f46a955bSLisandro Dalcin        self.petsc_arch = None
607f46a955bSLisandro Dalcin        self._outputs = []
608f46a955bSLisandro Dalcin
609f46a955bSLisandro Dalcin    def finalize_options(self):
610f46a955bSLisandro Dalcin        _build_ext.finalize_options(self)
61155a74a43SLisandro Dalcin        self.set_undefined_options('build', ('inplace', 'inplace'))
6126f336411SStefano Zampini        self.set_undefined_options(
6136f336411SStefano Zampini            'build', ('petsc_dir', 'petsc_dir'), ('petsc_arch', 'petsc_arch')
6146f336411SStefano Zampini        )
615f46a955bSLisandro Dalcin
616f46a955bSLisandro Dalcin    def _copy_ext(self, ext):
617f46a955bSLisandro Dalcin        extclass = ext.__class__
618f46a955bSLisandro Dalcin        fullname = self.get_ext_fullname(ext.name)
619f46a955bSLisandro Dalcin        modpath = str.split(fullname, '.')
620f46a955bSLisandro Dalcin        pkgpath = os.path.join('', *modpath[0:-1])
621f46a955bSLisandro Dalcin        name = modpath[-1]
622f46a955bSLisandro Dalcin        sources = list(ext.sources)
623f46a955bSLisandro Dalcin        newext = extclass(name, sources)
624f46a955bSLisandro Dalcin        newext.__dict__.update(copy.deepcopy(ext.__dict__))
625f46a955bSLisandro Dalcin        newext.name = name
626f46a955bSLisandro Dalcin        return pkgpath, newext
627f46a955bSLisandro Dalcin
628f46a955bSLisandro Dalcin    def _build_ext_arch(self, ext, pkgpath, arch):
629f46a955bSLisandro Dalcin        build_temp = self.build_temp
630f46a955bSLisandro Dalcin        build_lib = self.build_lib
631f46a955bSLisandro Dalcin        try:
632f46a955bSLisandro Dalcin            self.build_temp = os.path.join(build_temp, arch)
633f46a955bSLisandro Dalcin            self.build_lib = os.path.join(build_lib, pkgpath, arch)
634f46a955bSLisandro Dalcin            _build_ext.build_extension(self, ext)
635f46a955bSLisandro Dalcin        finally:
636f46a955bSLisandro Dalcin            self.build_temp = build_temp
637f46a955bSLisandro Dalcin            self.build_lib = build_lib
638f46a955bSLisandro Dalcin
639f46a955bSLisandro Dalcin    def get_config_arch(self, arch):
640f46a955bSLisandro Dalcin        return config.Configure(self.petsc_dir, arch)
641f46a955bSLisandro Dalcin
642f46a955bSLisandro Dalcin    def build_extension(self, ext):
643f46a955bSLisandro Dalcin        if not isinstance(ext, Extension):
644f46a955bSLisandro Dalcin            return _build_ext.build_extension(self, ext)
645f46a955bSLisandro Dalcin        petsc_arch = self.petsc_arch
646f46a955bSLisandro Dalcin        if not petsc_arch:
647f46a955bSLisandro Dalcin            petsc_arch = [None]
648f46a955bSLisandro Dalcin        for arch in petsc_arch:
649f46a955bSLisandro Dalcin            config = self.get_config_arch(arch)
650f46a955bSLisandro Dalcin            ARCH = arch or config['PETSC_ARCH']
651f46a955bSLisandro Dalcin            if ARCH not in self.PETSC_ARCH_LIST:
652f46a955bSLisandro Dalcin                self.PETSC_ARCH_LIST.append(ARCH)
653f46a955bSLisandro Dalcin            self.DESTDIR = config.DESTDIR
654f46a955bSLisandro Dalcin            ext.language = config.language
655f46a955bSLisandro Dalcin            config.log_info()
656f46a955bSLisandro Dalcin            pkgpath, newext = self._copy_ext(ext)
657f46a955bSLisandro Dalcin            config.configure(newext, self.compiler)
658f46a955bSLisandro Dalcin            self._build_ext_arch(newext, pkgpath, ARCH)
6596f336411SStefano Zampini        return None
660f46a955bSLisandro Dalcin
661f46a955bSLisandro Dalcin    def run(self):
662f46a955bSLisandro Dalcin        self.build_sources()
663f46a955bSLisandro Dalcin        _build_ext.run(self)
664f46a955bSLisandro Dalcin
665f46a955bSLisandro Dalcin    def build_sources(self):
666f46a955bSLisandro Dalcin        if 'build_src' in self.distribution.cmdclass:
667f46a955bSLisandro Dalcin            self.run_command('build_src')
668f46a955bSLisandro Dalcin
669f46a955bSLisandro Dalcin    def build_extensions(self, *args, **kargs):
670f46a955bSLisandro Dalcin        self.PETSC_ARCH_LIST = []
671f46a955bSLisandro Dalcin        _build_ext.build_extensions(self, *args, **kargs)
6726f336411SStefano Zampini        if not self.PETSC_ARCH_LIST:
6736f336411SStefano Zampini            return
674f46a955bSLisandro Dalcin        self.build_configuration(self.PETSC_ARCH_LIST)
675f46a955bSLisandro Dalcin
676f46a955bSLisandro Dalcin    def build_configuration(self, arch_list):
677f46a955bSLisandro Dalcin        #
678f46a955bSLisandro Dalcin        template, variables = self.get_config_data(arch_list)
679f46a955bSLisandro Dalcin        config_data = template % variables
680f46a955bSLisandro Dalcin        #
681f46a955bSLisandro Dalcin        build_lib = self.build_lib
682f46a955bSLisandro Dalcin        dist_name = self.distribution.get_name()
6836f336411SStefano Zampini        config_file = os.path.join(
6846f336411SStefano Zampini            build_lib, dist_name, 'lib', dist_name.replace('4py', '') + '.cfg'
6856f336411SStefano Zampini        )
6866f336411SStefano Zampini
687f46a955bSLisandro Dalcin        #
688f46a955bSLisandro Dalcin        def write_file(filename, data):
689f46a955bSLisandro Dalcin            with open(filename, 'w') as fh:
690f46a955bSLisandro Dalcin                fh.write(config_data)
6916f336411SStefano Zampini
6926f336411SStefano Zampini        execute(
6936f336411SStefano Zampini            write_file,
6946f336411SStefano Zampini            (config_file, config_data),
695f46a955bSLisandro Dalcin            msg='writing %s' % config_file,
6966f336411SStefano Zampini            verbose=self.verbose,
6976f336411SStefano Zampini            dry_run=self.dry_run,
6986f336411SStefano Zampini        )
699f46a955bSLisandro Dalcin
700f46a955bSLisandro Dalcin    def get_config_data(self, arch_list):
701f46a955bSLisandro Dalcin        DESTDIR = self.DESTDIR
7026f336411SStefano Zampini        template = (
7036f336411SStefano Zampini            '\n'.join(
7046f336411SStefano Zampini                [
7056f336411SStefano Zampini                    'PETSC_DIR  = %(PETSC_DIR)s',
7066f336411SStefano Zampini                    'PETSC_ARCH = %(PETSC_ARCH)s',
7076f336411SStefano Zampini                ]
7086f336411SStefano Zampini            )
7096f336411SStefano Zampini            + '\n'
7106f336411SStefano Zampini        )
711f46a955bSLisandro Dalcin        variables = {
712f46a955bSLisandro Dalcin            'PETSC_DIR': strip_prefix(DESTDIR, self.petsc_dir),
713f46a955bSLisandro Dalcin            'PETSC_ARCH': os.path.pathsep.join(arch_list),
714f46a955bSLisandro Dalcin        }
715f46a955bSLisandro Dalcin        return template, variables
716f46a955bSLisandro Dalcin
717f46a955bSLisandro Dalcin    def copy_extensions_to_source(self):
718f46a955bSLisandro Dalcin        build_py = self.get_finalized_command('build_py')
719f46a955bSLisandro Dalcin        for ext in self.extensions:
720f46a955bSLisandro Dalcin            inp_file, reg_file = self._get_inplace_equivalent(build_py, ext)
721f46a955bSLisandro Dalcin
722f46a955bSLisandro Dalcin            arch_list = ['']
723f46a955bSLisandro Dalcin            if isinstance(ext, Extension) and self.petsc_arch:
724f46a955bSLisandro Dalcin                arch_list = self.petsc_arch[:]
725f46a955bSLisandro Dalcin
726f46a955bSLisandro Dalcin            file_pairs = []
727f46a955bSLisandro Dalcin            inp_head, inp_tail = os.path.split(inp_file)
728f46a955bSLisandro Dalcin            reg_head, reg_tail = os.path.split(reg_file)
729f46a955bSLisandro Dalcin            for arch in arch_list:
730f46a955bSLisandro Dalcin                inp_file = os.path.join(inp_head, arch, inp_tail)
731f46a955bSLisandro Dalcin                reg_file = os.path.join(reg_head, arch, reg_tail)
732f46a955bSLisandro Dalcin                file_pairs.append((inp_file, reg_file))
733f46a955bSLisandro Dalcin
734f46a955bSLisandro Dalcin            for inp_file, reg_file in file_pairs:
735f46a955bSLisandro Dalcin                if os.path.exists(reg_file) or not ext.optional:
736f46a955bSLisandro Dalcin                    dest_dir, _ = os.path.split(inp_file)
737f46a955bSLisandro Dalcin                    self.mkpath(dest_dir)
738f46a955bSLisandro Dalcin                    self.copy_file(reg_file, inp_file, level=self.verbose)
739f46a955bSLisandro Dalcin
740f46a955bSLisandro Dalcin    def get_outputs(self):
741f46a955bSLisandro Dalcin        self.check_extensions_list(self.extensions)
742f46a955bSLisandro Dalcin        outputs = []
743f46a955bSLisandro Dalcin        for ext in self.extensions:
744f46a955bSLisandro Dalcin            fullname = self.get_ext_fullname(ext.name)
745f46a955bSLisandro Dalcin            filename = self.get_ext_filename(fullname)
746f46a955bSLisandro Dalcin            if isinstance(ext, Extension) and self.petsc_arch:
747f46a955bSLisandro Dalcin                head, tail = os.path.split(filename)
748f46a955bSLisandro Dalcin                for arch in self.petsc_arch:
749f46a955bSLisandro Dalcin                    outfile = os.path.join(self.build_lib, head, arch, tail)
750f46a955bSLisandro Dalcin                    outputs.append(outfile)
751f46a955bSLisandro Dalcin            else:
752f46a955bSLisandro Dalcin                outfile = os.path.join(self.build_lib, filename)
753f46a955bSLisandro Dalcin                outputs.append(outfile)
7546f336411SStefano Zampini        return list(set(outputs))
755f46a955bSLisandro Dalcin
756f46a955bSLisandro Dalcin
757f46a955bSLisandro Dalcinclass install(_install):
758f46a955bSLisandro Dalcin    def initialize_options(self):
759f46a955bSLisandro Dalcin        with warnings.catch_warnings():
760f46a955bSLisandro Dalcin            if setuptools:
761f46a955bSLisandro Dalcin                if hasattr(setuptools, 'SetuptoolsDeprecationWarning'):
762f46a955bSLisandro Dalcin                    category = setuptools.SetuptoolsDeprecationWarning
763f46a955bSLisandro Dalcin                    warnings.simplefilter('ignore', category)
764f46a955bSLisandro Dalcin            _install.initialize_options(self)
765f46a955bSLisandro Dalcin        self.old_and_unmanageable = True
766f46a955bSLisandro Dalcin
767f46a955bSLisandro Dalcin
768f46a955bSLisandro Dalcincmdclass_list = [
769f46a955bSLisandro Dalcin    config,
770f46a955bSLisandro Dalcin    build,
771f46a955bSLisandro Dalcin    build_src,
772f46a955bSLisandro Dalcin    build_ext,
773f46a955bSLisandro Dalcin    install,
774f46a955bSLisandro Dalcin]
775f46a955bSLisandro Dalcin
776f46a955bSLisandro Dalcin# --------------------------------------------------------------------
777f46a955bSLisandro Dalcin
7786f336411SStefano Zampini
779f46a955bSLisandro Dalcindef setup(**attrs):
780f46a955bSLisandro Dalcin    cmdclass = attrs.setdefault('cmdclass', {})
781f46a955bSLisandro Dalcin    for cmd in cmdclass_list:
782f46a955bSLisandro Dalcin        cmdclass.setdefault(cmd.__name__, cmd)
783f46a955bSLisandro Dalcin    build_src.sources = attrs.pop('cython_sources', None)
784f46a955bSLisandro Dalcin    use_setup_requires = False  # handle Cython requirement ourselves
785f46a955bSLisandro Dalcin    if setuptools and build_src.sources and use_setup_requires:
786f46a955bSLisandro Dalcin        version = cython_req()
787f46a955bSLisandro Dalcin        if not cython_chk(version, verbose=False):
788f46a955bSLisandro Dalcin            reqs = attrs.setdefault('setup_requires', [])
789693647e8SLisandro Dalcin            reqs += ['Cython>=' + version]
790f46a955bSLisandro Dalcin    return _setup(**attrs)
791f46a955bSLisandro Dalcin
7926f336411SStefano Zampini
793f46a955bSLisandro Dalcin# --------------------------------------------------------------------
794f46a955bSLisandro Dalcin
795f46a955bSLisandro Dalcinif setuptools:
796f46a955bSLisandro Dalcin    try:
797f46a955bSLisandro Dalcin        from setuptools.command import egg_info as mod_egg_info
7986f336411SStefano Zampini
799f46a955bSLisandro Dalcin        _FileList = mod_egg_info.FileList
8006f336411SStefano Zampini
801f46a955bSLisandro Dalcin        class FileList(_FileList):
802f46a955bSLisandro Dalcin            def process_template_line(self, line):
803f46a955bSLisandro Dalcin                level = log.set_threshold(log.ERROR)
804f46a955bSLisandro Dalcin                try:
805f46a955bSLisandro Dalcin                    _FileList.process_template_line(self, line)
806f46a955bSLisandro Dalcin                finally:
807f46a955bSLisandro Dalcin                    log.set_threshold(level)
8086f336411SStefano Zampini
809f46a955bSLisandro Dalcin        mod_egg_info.FileList = FileList
810f46a955bSLisandro Dalcin    except (ImportError, AttributeError):
811f46a955bSLisandro Dalcin        pass
812f46a955bSLisandro Dalcin
813f46a955bSLisandro Dalcin# --------------------------------------------------------------------
814f46a955bSLisandro Dalcin
8156f336411SStefano Zampini
816f46a955bSLisandro Dalcindef append(seq, item):
817f46a955bSLisandro Dalcin    if item not in seq:
818f46a955bSLisandro Dalcin        seq.append(item)
819f46a955bSLisandro Dalcin
8206f336411SStefano Zampini
821f46a955bSLisandro Dalcindef append_dict(conf, dct):
822f46a955bSLisandro Dalcin    for key, values in dct.items():
823f46a955bSLisandro Dalcin        if key in conf:
824f46a955bSLisandro Dalcin            for value in values:
825f46a955bSLisandro Dalcin                if value not in conf[key]:
826f46a955bSLisandro Dalcin                    conf[key].append(value)
8276f336411SStefano Zampini
8286f336411SStefano Zampini
829f46a955bSLisandro Dalcindef unique(seq):
830f46a955bSLisandro Dalcin    res = []
831f46a955bSLisandro Dalcin    for item in seq:
832f46a955bSLisandro Dalcin        if item not in res:
833f46a955bSLisandro Dalcin            res.append(item)
834f46a955bSLisandro Dalcin    return res
835f46a955bSLisandro Dalcin
836f46a955bSLisandro Dalcin
8376f336411SStefano Zampinidef flaglist(flags):
838f46a955bSLisandro Dalcin    conf = {
839f46a955bSLisandro Dalcin        'define_macros': [],
840f46a955bSLisandro Dalcin        'undef_macros': [],
841f46a955bSLisandro Dalcin        'include_dirs': [],
842f46a955bSLisandro Dalcin        'libraries': [],
843f46a955bSLisandro Dalcin        'library_dirs': [],
844f46a955bSLisandro Dalcin        'runtime_library_dirs': [],
845f46a955bSLisandro Dalcin        'extra_compile_args': [],
846f46a955bSLisandro Dalcin        'extra_link_args': [],
847f46a955bSLisandro Dalcin    }
848f46a955bSLisandro Dalcin
8496f336411SStefano Zampini    if isinstance(flags, str):
850f46a955bSLisandro Dalcin        flags = flags.split()
851f46a955bSLisandro Dalcin
852f46a955bSLisandro Dalcin    switch = '-Wl,'
853f46a955bSLisandro Dalcin    newflags = []
854f46a955bSLisandro Dalcin    linkopts = []
855f46a955bSLisandro Dalcin    for f in flags:
856f46a955bSLisandro Dalcin        if f.startswith(switch):
857f46a955bSLisandro Dalcin            if len(f) > 4:
858f46a955bSLisandro Dalcin                append(linkopts, f[4:])
859f46a955bSLisandro Dalcin        else:
860f46a955bSLisandro Dalcin            append(newflags, f)
861f46a955bSLisandro Dalcin    if linkopts:
862f46a955bSLisandro Dalcin        newflags.append(switch + ','.join(linkopts))
863f46a955bSLisandro Dalcin    flags = newflags
864f46a955bSLisandro Dalcin
865f46a955bSLisandro Dalcin    append_next_word = None
866f46a955bSLisandro Dalcin
867f46a955bSLisandro Dalcin    for word in flags:
868f46a955bSLisandro Dalcin        if append_next_word is not None:
869f46a955bSLisandro Dalcin            append(append_next_word, word)
870f46a955bSLisandro Dalcin            append_next_word = None
871f46a955bSLisandro Dalcin            continue
872f46a955bSLisandro Dalcin
873f46a955bSLisandro Dalcin        switch, value = word[0:2], word[2:]
874f46a955bSLisandro Dalcin
8756f336411SStefano Zampini        if switch == '-I':
876f46a955bSLisandro Dalcin            append(conf['include_dirs'], value)
8776f336411SStefano Zampini        elif switch == '-D':
878f46a955bSLisandro Dalcin            try:
8796f336411SStefano Zampini                idx = value.index('=')
880f46a955bSLisandro Dalcin                macro = (value[:idx], value[idx + 1 :])
881f46a955bSLisandro Dalcin            except ValueError:
882f46a955bSLisandro Dalcin                macro = (value, None)
883f46a955bSLisandro Dalcin            append(conf['define_macros'], macro)
8846f336411SStefano Zampini        elif switch == '-U':
885f46a955bSLisandro Dalcin            append(conf['undef_macros'], value)
8866f336411SStefano Zampini        elif switch == '-l':
887f46a955bSLisandro Dalcin            append(conf['libraries'], value)
8886f336411SStefano Zampini        elif switch == '-L':
889f46a955bSLisandro Dalcin            append(conf['library_dirs'], value)
8906f336411SStefano Zampini        elif switch == '-R':
891f46a955bSLisandro Dalcin            append(conf['runtime_library_dirs'], value)
8926f336411SStefano Zampini        elif word.startswith('-Wl'):
893f46a955bSLisandro Dalcin            linkopts = word.split(',')
894f46a955bSLisandro Dalcin            append_dict(conf, flaglist(linkopts[1:]))
8956f336411SStefano Zampini        elif word == '-rpath':
896f46a955bSLisandro Dalcin            append_next_word = conf['runtime_library_dirs']
8976f336411SStefano Zampini        elif word == '-Xlinker':
898f46a955bSLisandro Dalcin            append_next_word = conf['extra_link_args']
899f46a955bSLisandro Dalcin        else:
900f46a955bSLisandro Dalcin            # log.warn("unrecognized flag '%s'" % word)
901f46a955bSLisandro Dalcin            pass
902f46a955bSLisandro Dalcin    return conf
903f46a955bSLisandro Dalcin
9046f336411SStefano Zampini
905f46a955bSLisandro Dalcindef prepend_to_flags(path, flags):
906f46a955bSLisandro Dalcin    """Prepend a path to compiler flags with absolute paths"""
907f46a955bSLisandro Dalcin    if not path:
908f46a955bSLisandro Dalcin        return flags
9096f336411SStefano Zampini
910f46a955bSLisandro Dalcin    def append_path(m):
911f46a955bSLisandro Dalcin        switch = m.group(1)
912f46a955bSLisandro Dalcin        open_quote = m.group(4)
913f46a955bSLisandro Dalcin        old_path = m.group(5)
914f46a955bSLisandro Dalcin        close_quote = m.group(6)
915f46a955bSLisandro Dalcin        if os.path.isabs(old_path):
916f46a955bSLisandro Dalcin            moded_path = os.path.normpath(path + os.path.sep + old_path)
917f46a955bSLisandro Dalcin            return switch + open_quote + moded_path + close_quote
918f46a955bSLisandro Dalcin        return m.group(0)
9196f336411SStefano Zampini
9206f336411SStefano Zampini    return re.sub(r'((^|\s+)(-I|-L))(\s*["\']?)(\S+)(["\']?)', append_path, flags)
9216f336411SStefano Zampini
922f46a955bSLisandro Dalcin
923f46a955bSLisandro Dalcindef strip_prefix(prefix, string):
924f46a955bSLisandro Dalcin    if not prefix:
925f46a955bSLisandro Dalcin        return string
926f46a955bSLisandro Dalcin    return re.sub(r'^' + prefix, '', string)
927f46a955bSLisandro Dalcin
9286f336411SStefano Zampini
929f46a955bSLisandro Dalcin# --------------------------------------------------------------------
930f46a955bSLisandro Dalcin
931f46a955bSLisandro Dalcin# Regexes needed for parsing Makefile-like syntaxes
9326f336411SStefano Zampini_variable_rx = re.compile(r'([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)')
9336f336411SStefano Zampini_findvar1_rx = re.compile(r'\$\(([A-Za-z][A-Za-z0-9_]*)\)')
9346f336411SStefano Zampini_findvar2_rx = re.compile(r'\${([A-Za-z][A-Za-z0-9_]*)}')
9356f336411SStefano Zampini
936f46a955bSLisandro Dalcin
937f46a955bSLisandro Dalcindef makefile(fileobj, dct=None):
938f46a955bSLisandro Dalcin    """Parse a Makefile-style file.
939f46a955bSLisandro Dalcin
940f46a955bSLisandro Dalcin    A dictionary containing name/value pairs is returned.  If an
941f46a955bSLisandro Dalcin    optional dictionary is passed in as the second argument, it is
942f46a955bSLisandro Dalcin    used instead of a new dictionary.
943f46a955bSLisandro Dalcin    """
9446f336411SStefano Zampini    fp = TextFile(file=fileobj, strip_comments=1, skip_blanks=1, join_lines=1)
945f46a955bSLisandro Dalcin
946f46a955bSLisandro Dalcin    if dct is None:
947f46a955bSLisandro Dalcin        dct = {}
948f46a955bSLisandro Dalcin    done = {}
949f46a955bSLisandro Dalcin    notdone = {}
950f46a955bSLisandro Dalcin
951f46a955bSLisandro Dalcin    while 1:
952f46a955bSLisandro Dalcin        line = fp.readline()
953f46a955bSLisandro Dalcin        if line is None:  # eof
954f46a955bSLisandro Dalcin            break
955f46a955bSLisandro Dalcin        m = _variable_rx.match(line)
956f46a955bSLisandro Dalcin        if m:
957f46a955bSLisandro Dalcin            n, v = m.group(1, 2)
958f46a955bSLisandro Dalcin            v = str.strip(v)
9596f336411SStefano Zampini            if '$' in v:
960f46a955bSLisandro Dalcin                notdone[n] = v
961f46a955bSLisandro Dalcin            else:
9626f336411SStefano Zampini                try:
9636f336411SStefano Zampini                    v = int(v)
9646f336411SStefano Zampini                except ValueError:
9656f336411SStefano Zampini                    pass
966f46a955bSLisandro Dalcin                done[n] = v
9676f336411SStefano Zampini                try:
9686f336411SStefano Zampini                    del notdone[n]
9696f336411SStefano Zampini                except KeyError:
9706f336411SStefano Zampini                    pass
971f46a955bSLisandro Dalcin    fp.close()
972f46a955bSLisandro Dalcin
973f46a955bSLisandro Dalcin    # do variable interpolation here
974f46a955bSLisandro Dalcin    while notdone:
975f46a955bSLisandro Dalcin        for name in list(notdone.keys()):
976f46a955bSLisandro Dalcin            value = notdone[name]
977f46a955bSLisandro Dalcin            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
978f46a955bSLisandro Dalcin            if m:
979f46a955bSLisandro Dalcin                n = m.group(1)
980f46a955bSLisandro Dalcin                found = True
981f46a955bSLisandro Dalcin                if n in done:
982f46a955bSLisandro Dalcin                    item = str(done[n])
983f46a955bSLisandro Dalcin                elif n in notdone:
984f46a955bSLisandro Dalcin                    # get it on a subsequent round
985f46a955bSLisandro Dalcin                    found = False
986f46a955bSLisandro Dalcin                else:
9876f336411SStefano Zampini                    done[n] = item = ''
988f46a955bSLisandro Dalcin                if found:
989f46a955bSLisandro Dalcin                    after = value[m.end() :]
990f46a955bSLisandro Dalcin                    value = value[: m.start()] + item + after
9916f336411SStefano Zampini                    if '$' in after:
992f46a955bSLisandro Dalcin                        notdone[name] = value
993f46a955bSLisandro Dalcin                    else:
9946f336411SStefano Zampini                        try:
9956f336411SStefano Zampini                            value = int(value)
996f46a955bSLisandro Dalcin                        except ValueError:
997f46a955bSLisandro Dalcin                            done[name] = str.strip(value)
998f46a955bSLisandro Dalcin                        else:
999f46a955bSLisandro Dalcin                            done[name] = value
1000f46a955bSLisandro Dalcin                        del notdone[name]
1001f46a955bSLisandro Dalcin            else:
1002f46a955bSLisandro Dalcin                # bogus variable reference;
1003f46a955bSLisandro Dalcin                # just drop it since we can't deal
1004f46a955bSLisandro Dalcin                del notdone[name]
1005f46a955bSLisandro Dalcin    # save the results in the global dictionary
1006f46a955bSLisandro Dalcin    dct.update(done)
1007f46a955bSLisandro Dalcin    return dct
1008f46a955bSLisandro Dalcin
10096f336411SStefano Zampini
1010f46a955bSLisandro Dalcin# --------------------------------------------------------------------
1011