1# --------------------------------------------------------------------
2
3__all__ = ['PetscConfig',
4           'setup', 'Extension',
5           'config', 'build', 'build_src', 'build_ext',
6           'clean', 'test', 'sdist',
7           'log',
8           ]
9
10# --------------------------------------------------------------------
11
12import sys, os
13try:
14    import setuptools
15except ImportError:
16    setuptools = None
17
18def import_command(cmd):
19    try:
20        from importlib import import_module
21    except ImportError:
22        import_module = lambda n:  __import__(n, fromlist=[None])
23    try:
24        if not setuptools: raise ImportError
25        mod = import_module('setuptools.command.' + cmd)
26        return getattr(mod, cmd)
27    except ImportError:
28        mod = import_module('distutils.command.' + cmd)
29        return getattr(mod, cmd)
30
31if setuptools:
32    from setuptools import setup
33    from setuptools import Extension as _Extension
34    from setuptools import Command
35else:
36    from distutils.core import setup
37    from distutils.core import Extension as _Extension
38    from distutils.core import Command
39
40_config    = import_command('config')
41_build     = import_command('build')
42_build_ext = import_command('build_ext')
43_install   = import_command('install')
44_clean     = import_command('clean')
45_sdist     = import_command('sdist')
46
47from distutils import sysconfig
48from distutils import log
49from distutils.util import split_quoted, execute
50from distutils.errors import DistutilsError
51
52# --------------------------------------------------------------------
53
54def fix_config_vars(names, values):
55    import os, re
56    values = list(values)
57    if sys.platform == 'darwin':
58        if 'ARCHFLAGS' in os.environ:
59            ARCHFLAGS = os.environ['ARCHFLAGS']
60            for i, flag in enumerate(list(values)):
61                flag, count = re.subn('-arch\s+\w+', ' ', flag)
62                if count and ARCHFLAGS:
63                    flag = flag + ' ' + ARCHFLAGS
64                values[i] = flag
65        if 'SDKROOT' in os.environ:
66            SDKROOT = os.environ['SDKROOT']
67            for i, flag in enumerate(list(values)):
68                flag, count = re.subn('-isysroot [^ \t]*', ' ', flag)
69                if count and SDKROOT:
70                    flag = flag + ' ' + '-isysroot ' + SDKROOT
71                values[i] = flag
72    return values
73
74def get_config_vars(*names):
75    # Core Python configuration
76    values = sysconfig.get_config_vars(*names)
77    # Do any distutils flags fixup right now
78    values = fix_config_vars(names, values)
79    return values
80
81from distutils.unixccompiler import UnixCCompiler
82rpath_option_orig = UnixCCompiler.runtime_library_dir_option
83def rpath_option(compiler, dir):
84    option = rpath_option_orig(compiler, dir)
85    if sys.platform[:5] == 'linux':
86        if option.startswith('-R'):
87            option =  option.replace('-R', '-Wl,-rpath,', 1)
88        elif option.startswith('-Wl,-R'):
89            option =  option.replace('-Wl,-R', '-Wl,-rpath,', 1)
90    return option
91UnixCCompiler.runtime_library_dir_option = rpath_option
92
93# --------------------------------------------------------------------
94
95class PetscConfig:
96
97    def __init__(self, petsc_dir, petsc_arch):
98        self.configdict = { }
99        if not petsc_dir:
100            raise DistutilsError("PETSc not found")
101        if not os.path.isdir(petsc_dir):
102            raise DistutilsError("invalid PETSC_DIR: %s" % petsc_dir)
103        self.version    = self._get_petsc_version(petsc_dir)
104        self.configdict = self._get_petsc_config(petsc_dir, petsc_arch)
105        self.PETSC_DIR  = self['PETSC_DIR']
106        self.PETSC_ARCH = self['PETSC_ARCH']
107        language_map = {'CONLY':'c', 'CXXONLY':'c++'}
108        self.language = language_map[self['PETSC_LANGUAGE']]
109
110    def __getitem__(self, item):
111        return self.configdict[item]
112
113    def get(self, item, default=None):
114        return self.configdict.get(item, default)
115
116    def configure(self, extension, compiler=None):
117        self.configure_extension(extension)
118        if compiler is not None:
119            self.configure_compiler(compiler)
120
121    def _get_petsc_version(self, petsc_dir):
122        import re
123        version_re = {
124            'major'  : re.compile(r"#define\s+PETSC_VERSION_MAJOR\s+(\d+)"),
125            'minor'  : re.compile(r"#define\s+PETSC_VERSION_MINOR\s+(\d+)"),
126            'micro'  : re.compile(r"#define\s+PETSC_VERSION_SUBMINOR\s+(\d+)"),
127            'patch'  : re.compile(r"#define\s+PETSC_VERSION_PATCH\s+(\d+)"),
128            'release': re.compile(r"#define\s+PETSC_VERSION_RELEASE\s+(-*\d+)"),
129            }
130        petscversion_h = os.path.join(petsc_dir, 'include', 'petscversion.h')
131        with open(petscversion_h, 'rt') as f: data = f.read()
132        major = int(version_re['major'].search(data).groups()[0])
133        minor = int(version_re['minor'].search(data).groups()[0])
134        micro = int(version_re['micro'].search(data).groups()[0])
135        release = int(version_re['release'].search(data).groups()[0])
136        return  (major, minor, micro), (release == 1)
137
138    def _get_petsc_config(self, petsc_dir, petsc_arch):
139        from os.path import join, isdir, exists
140        PETSC_DIR  = petsc_dir
141        PETSC_ARCH = petsc_arch
142        #
143        confdir = join('lib', 'petsc', 'conf')
144        if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))):
145            petscvars = join(PETSC_DIR, confdir, 'petscvariables')
146            PETSC_ARCH = makefile(open(petscvars, 'rt')).get('PETSC_ARCH')
147        if not (PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH))):
148            PETSC_ARCH = ''
149        #
150        variables = join(PETSC_DIR, confdir, 'variables')
151        if not exists(variables):
152            variables  = join(PETSC_DIR, PETSC_ARCH, confdir, 'variables')
153        petscvariables = join(PETSC_DIR, PETSC_ARCH, confdir, 'petscvariables')
154        #
155        with open(variables) as f:
156            contents = f.read()
157        with open(petscvariables) as f:
158            contents += f.read()
159        #
160        try:
161            from cStringIO import StringIO
162        except ImportError:
163            from io import StringIO
164        confstr  = 'PETSC_DIR  = %s\n' % PETSC_DIR
165        confstr += 'PETSC_ARCH = %s\n' % PETSC_ARCH
166        confstr += contents
167        confdict = makefile(StringIO(confstr))
168        return confdict
169
170    def _configure_ext(self, ext, dct, preppend=False):
171        extdict = ext.__dict__
172        for key, values in dct.items():
173            if key in extdict:
174                for value in values:
175                    if value not in extdict[key]:
176                        if preppend:
177                            extdict[key].insert(0, value)
178                        else:
179                            extdict[key].append(value)
180
181    def configure_extension(self, extension):
182        # includes and libraries
183        petsc_inc = flaglist(self['PETSC_CC_INCLUDES'])
184        petsc_lib = flaglist(
185            '-L%s %s' % (self['PETSC_LIB_DIR'], self['PETSC_LIB_BASIC']))
186        # runtime_library_dirs is not supported on Windows
187        if sys.platform != 'win32':
188            petsc_lib['runtime_library_dirs'].append(self['PETSC_LIB_DIR'])
189
190        # Link in extra libraries on static builds
191        if self['BUILDSHAREDLIB'] != 'yes':
192            petsc_ext_lib = split_quoted(self['PETSC_EXTERNAL_LIB_BASIC'])
193            petsc_lib['extra_link_args'].extend(petsc_ext_lib)
194
195        self._configure_ext(extension, petsc_inc, preppend=True)
196        self._configure_ext(extension, petsc_lib)
197
198    def configure_compiler(self, compiler):
199        if compiler.compiler_type != 'unix': return
200        getenv = os.environ.get
201        # distutils C/C++ compiler
202        (cc, cflags, ccshared, cxx) = get_config_vars(
203            'CC', 'CFLAGS',  'CCSHARED', 'CXX')
204        ccshared = getenv('CCSHARED', ccshared or '')
205        cflags = getenv('CFLAGS', cflags or '')
206        cflags = cflags.replace('-Wstrict-prototypes', '')
207        # distutils linker
208        (ldflags, ldshared, so_ext) = get_config_vars(
209            'LDFLAGS', 'LDSHARED', 'SO')
210        ld = cc
211        ldshared = getenv('LDSHARED', ldshared)
212        ldflags = getenv('LDFLAGS', cflags + ' ' + (ldflags or ''))
213        ldcmd = split_quoted(ld) + split_quoted(ldflags)
214        ldshared = [flg for flg in split_quoted(ldshared) if flg not in ldcmd]
215        ldshared = str.join(' ', ldshared)
216        #
217        def get_flags(cmd):
218            try: return ' '.join(split_quoted(cmd)[1:])
219            except: return ''
220        # PETSc C compiler
221        PCC = self['PCC']
222        PCC_FLAGS = get_flags(cc) + ' ' + self['PCC_FLAGS']
223        PCC_FLAGS = PCC_FLAGS.replace('-fvisibility=hidden', '')
224        PCC = getenv('PCC', PCC) + ' ' +  getenv('PCCFLAGS', PCC_FLAGS)
225        PCC_SHARED = str.join(' ', (PCC, ccshared, cflags))
226        # PETSc C++ compiler
227        PCXX = PCC if self.language == 'c++' else self.get('CXX', cxx)
228        # PETSc linker
229        PLD = self['PCC_LINKER']
230        PLD_FLAGS = get_flags(ld) + ' ' + self['PCC_LINKER_FLAGS']
231        PLD_FLAGS = PLD_FLAGS.replace('-fvisibility=hidden', '')
232        PLD = getenv('PLD', PLD) + ' ' + getenv('PLDFLAGS', PLD_FLAGS)
233        PLD_SHARED = str.join(' ', (PLD, ldshared, ldflags))
234        #
235        compiler.set_executables(
236            compiler     = PCC,
237            compiler_cxx = PCXX,
238            linker_exe   = PLD,
239            compiler_so  = PCC_SHARED,
240            linker_so    = PLD_SHARED,
241            )
242        compiler.shared_lib_extension = so_ext
243        #
244        if sys.platform == 'darwin':
245            for attr in ('preprocessor',
246                         'compiler', 'compiler_cxx', 'compiler_so',
247                         'linker_so', 'linker_exe'):
248                compiler_cmd = getattr(compiler, attr, [])
249                while '-mno-fused-madd' in compiler_cmd:
250                    compiler_cmd.remove('-mno-fused-madd')
251
252    def log_info(self):
253        PETSC_DIR  = self['PETSC_DIR']
254        PETSC_ARCH = self['PETSC_ARCH']
255        version = ".".join([str(i) for i in self.version[0]])
256        release = ("development", "release")[self.version[1]]
257        version_info = version + ' ' + release
258        integer_size = '%s-bit' % self['PETSC_INDEX_SIZE']
259        scalar_type  = self['PETSC_SCALAR']
260        precision    = self['PETSC_PRECISION']
261        language     = self['PETSC_LANGUAGE']
262        compiler     = self['PCC']
263        linker       = self['PCC_LINKER']
264        log.info('PETSC_DIR:    %s' % PETSC_DIR )
265        log.info('PETSC_ARCH:   %s' % PETSC_ARCH )
266        log.info('version:      %s' % version_info)
267        log.info('integer-size: %s' % integer_size)
268        log.info('scalar-type:  %s' % scalar_type)
269        log.info('precision:    %s' % precision)
270        log.info('language:     %s' % language)
271        log.info('compiler:     %s' % compiler)
272        log.info('linker:       %s' % linker)
273
274# --------------------------------------------------------------------
275
276class Extension(_Extension):
277    pass
278
279# --------------------------------------------------------------------
280
281cmd_petsc_opts = [
282    ('petsc-dir=', None,
283     "define PETSC_DIR, overriding environmental variables"),
284    ('petsc-arch=', None,
285     "define PETSC_ARCH, overriding environmental variables"),
286    ]
287
288class config(_config):
289
290    Configure = PetscConfig
291
292    user_options = _config.user_options + cmd_petsc_opts
293
294    def initialize_options(self):
295        _config.initialize_options(self)
296        self.petsc_dir  = None
297        self.petsc_arch = None
298
299    def get_config_arch(self, arch):
300        return config.Configure(self.petsc_dir, arch)
301
302    def run(self):
303        _config.run(self)
304        self.petsc_dir = config.get_petsc_dir(self.petsc_dir)
305        if self.petsc_dir is None: return
306        petsc_arch = config.get_petsc_arch(self.petsc_dir, self.petsc_arch)
307        log.info('-' * 70)
308        log.info('PETSC_DIR:   %s' % self.petsc_dir)
309        arch_list = petsc_arch
310        if not arch_list :
311            arch_list = [ None ]
312        for arch in arch_list:
313            conf = self.get_config_arch(arch)
314            archname    = conf.PETSC_ARCH or conf['PETSC_ARCH']
315            scalar_type = conf['PETSC_SCALAR']
316            precision   = conf['PETSC_PRECISION']
317            language    = conf['PETSC_LANGUAGE']
318            compiler    = conf['PCC']
319            linker      = conf['PCC_LINKER']
320            log.info('-'*70)
321            log.info('PETSC_ARCH:  %s' % archname)
322            log.info(' * scalar-type: %s' % scalar_type)
323            log.info(' * precision:   %s' % precision)
324            log.info(' * language:    %s' % language)
325            log.info(' * compiler:    %s' % compiler)
326            log.info(' * linker:      %s' % linker)
327        log.info('-' * 70)
328
329    #@staticmethod
330    def get_petsc_dir(petsc_dir):
331        if not petsc_dir: return None
332        petsc_dir = os.path.expandvars(petsc_dir)
333        if not petsc_dir or '$PETSC_DIR' in petsc_dir:
334            try:
335                import petsc
336                petsc_dir = petsc.get_petsc_dir()
337            except ImportError:
338                log.warn("PETSC_DIR not specified")
339                return None
340        petsc_dir = os.path.expanduser(petsc_dir)
341        petsc_dir = os.path.abspath(petsc_dir)
342        return config.chk_petsc_dir(petsc_dir)
343    get_petsc_dir = staticmethod(get_petsc_dir)
344
345    #@staticmethod
346    def chk_petsc_dir(petsc_dir):
347        if not os.path.isdir(petsc_dir):
348            log.error('invalid PETSC_DIR: %s (ignored)' % petsc_dir)
349            return None
350        return petsc_dir
351    chk_petsc_dir = staticmethod(chk_petsc_dir)
352
353    #@staticmethod
354    def get_petsc_arch(petsc_dir, petsc_arch):
355        if not petsc_dir: return None
356        petsc_arch = os.path.expandvars(petsc_arch)
357        if (not petsc_arch or '$PETSC_ARCH' in petsc_arch):
358            petsc_arch = ''
359            petsc_conf = os.path.join(petsc_dir, 'lib', 'petsc', 'conf')
360            if os.path.isdir(petsc_conf):
361                petscvariables = os.path.join(petsc_conf, 'petscvariables')
362                if os.path.exists(petscvariables):
363                    conf = makefile(open(petscvariables, 'rt'))
364                    petsc_arch = conf.get('PETSC_ARCH', '')
365        petsc_arch = petsc_arch.split(os.pathsep)
366        petsc_arch = unique(petsc_arch)
367        petsc_arch = [arch for arch in petsc_arch if arch]
368        return config.chk_petsc_arch(petsc_dir, petsc_arch)
369    get_petsc_arch = staticmethod(get_petsc_arch)
370
371    #@staticmethod
372    def chk_petsc_arch(petsc_dir, petsc_arch):
373        valid_archs = []
374        for arch in petsc_arch:
375            arch_path = os.path.join(petsc_dir, arch)
376            if os.path.isdir(arch_path):
377                valid_archs.append(arch)
378            else:
379                log.warn("invalid PETSC_ARCH: %s (ignored)" % arch)
380        return valid_archs
381    chk_petsc_arch = staticmethod(chk_petsc_arch)
382
383
384class build(_build):
385
386    user_options = _build.user_options + cmd_petsc_opts
387
388    def initialize_options(self):
389        _build.initialize_options(self)
390        self.petsc_dir  = None
391        self.petsc_arch = None
392
393    def finalize_options(self):
394        _build.finalize_options(self)
395        self.set_undefined_options('config',
396                                   ('petsc_dir',  'petsc_dir'),
397                                   ('petsc_arch', 'petsc_arch'))
398        self.petsc_dir  = config.get_petsc_dir(self.petsc_dir)
399        self.petsc_arch = config.get_petsc_arch(self.petsc_dir,
400                                                self.petsc_arch)
401
402    sub_commands = \
403        [('build_src', lambda *args: True)] + \
404        _build.sub_commands
405
406class build_src(Command):
407    description = "build C sources from Cython files"
408    user_options = [
409        ('force', 'f',
410         "forcibly build everything (ignore file timestamps)"),
411        ]
412    boolean_options = ['force']
413    def initialize_options(self):
414        self.force = False
415    def finalize_options(self):
416        self.set_undefined_options('build',
417                                   ('force', 'force'),
418                                   )
419    def run(self):
420        pass
421
422class build_ext(_build_ext):
423
424    user_options = _build_ext.user_options + cmd_petsc_opts
425
426    def initialize_options(self):
427        _build_ext.initialize_options(self)
428        self.petsc_dir  = None
429        self.petsc_arch = None
430        self._outputs = []
431
432    def finalize_options(self):
433        _build_ext.finalize_options(self)
434        self.set_undefined_options('build',
435                                   ('petsc_dir',  'petsc_dir'),
436                                   ('petsc_arch', 'petsc_arch'))
437        if ((sys.platform.startswith('linux') or
438             sys.platform.startswith('gnu') or
439             sys.platform.startswith('sunos')) and
440            sysconfig.get_config_var('Py_ENABLE_SHARED')):
441            py_version = sysconfig.get_python_version()
442            bad_pylib_dir = os.path.join(sys.prefix, "lib",
443                                         "python" + py_version,
444                                         "config")
445            try:
446                self.library_dirs.remove(bad_pylib_dir)
447            except ValueError:
448                pass
449            pylib_dir = sysconfig.get_config_var("LIBDIR")
450            if pylib_dir not in self.library_dirs:
451                self.library_dirs.append(pylib_dir)
452            if pylib_dir not in self.rpath:
453                self.rpath.append(pylib_dir)
454            if sys.exec_prefix == '/usr':
455                self.library_dirs.remove(pylib_dir)
456                self.rpath.remove(pylib_dir)
457
458    def _copy_ext(self, ext):
459        from copy import deepcopy
460        extclass = ext.__class__
461        fullname = self.get_ext_fullname(ext.name)
462        modpath = str.split(fullname, '.')
463        pkgpath = os.path.join('', *modpath[0:-1])
464        name = modpath[-1]
465        sources = list(ext.sources)
466        newext = extclass(name, sources)
467        newext.__dict__.update(deepcopy(ext.__dict__))
468        newext.name = name
469        return pkgpath, newext
470
471    def _build_ext_arch(self, ext, pkgpath, arch):
472        build_temp = self.build_temp
473        build_lib  = self.build_lib
474        try:
475            self.build_temp = os.path.join(build_temp, arch)
476            self.build_lib  = os.path.join(build_lib, pkgpath, arch)
477            _build_ext.build_extension(self, ext)
478        finally:
479            self.build_temp = build_temp
480            self.build_lib  = build_lib
481
482    def get_config_arch(self, arch):
483        return config.Configure(self.petsc_dir, arch)
484
485    def build_extension(self, ext):
486        if not isinstance(ext, Extension):
487            return _build_ext.build_extension(self, ext)
488        petsc_arch = self.petsc_arch
489        if not petsc_arch:
490            petsc_arch = [ None ]
491        for arch in petsc_arch:
492            config = self.get_config_arch(arch)
493            ARCH = arch or config['PETSC_ARCH']
494            if ARCH not in self.PETSC_ARCH_LIST:
495                self.PETSC_ARCH_LIST.append(ARCH)
496            ext.language = config.language
497            config.log_info()
498            pkgpath, newext = self._copy_ext(ext)
499            config.configure(newext, self.compiler)
500            name =  self.distribution.get_name()
501            version = self.distribution.get_version()
502            distdir = "%s-%s/" % (name, version)
503            self._build_ext_arch(newext, pkgpath, ARCH)
504
505    def build_extensions(self, *args, **kargs):
506        self.PETSC_ARCH_LIST = []
507        _build_ext.build_extensions(self, *args,**kargs)
508        if not self.PETSC_ARCH_LIST: return
509        self.build_configuration(self.PETSC_ARCH_LIST)
510
511
512    def build_configuration(self, arch_list):
513        #
514        template, variables = self.get_config_data(arch_list)
515        config_data = template % variables
516        #
517        build_lib   = self.build_lib
518        dist_name   = self.distribution.get_name()
519        config_file = os.path.join(build_lib, dist_name, 'lib',
520                                   dist_name.replace('4py', '') + '.cfg')
521        #
522        def write_file(filename, data):
523            with open(filename, 'w') as fh:
524                fh.write(config_data)
525        execute(write_file, (config_file, config_data),
526                msg='writing %s' % config_file,
527                verbose=self.verbose, dry_run=self.dry_run)
528
529    def get_config_data(self, arch_list):
530        template = """\
531PETSC_DIR  = %(PETSC_DIR)s
532PETSC_ARCH = %(PETSC_ARCH)s
533"""
534        variables = {'PETSC_DIR'  : self.petsc_dir,
535                     'PETSC_ARCH' : os.path.pathsep.join(arch_list)}
536        return template, variables
537
538    def get_outputs(self):
539        self.check_extensions_list(self.extensions)
540        outputs = []
541        for ext in self.extensions:
542            fullname = self.get_ext_fullname(ext.name)
543            filename = self.get_ext_filename(fullname)
544            if isinstance(ext, Extension) and self.petsc_arch:
545                head, tail = os.path.split(filename)
546                for arch in self.petsc_arch:
547                    outfile = os.path.join(self.build_lib,
548                                           head, arch, tail)
549                    outputs.append(outfile)
550            else:
551                outfile = os.path.join(self.build_lib, filename)
552                outputs.append(outfile)
553        outputs = list(set(outputs))
554        return outputs
555
556class install(_install):
557    def run(self):
558        _install.run(self)
559
560class clean(_clean):
561    def run(self):
562        _clean.run(self)
563        from distutils.dir_util import remove_tree
564        if self.all:
565            # remove the <package>.egg_info directory
566            try:
567                egg_info = self.get_finalized_command('egg_info').egg_info
568                if os.path.exists(egg_info):
569                    remove_tree(egg_info, dry_run=self.dry_run)
570                else:
571                    log.debug("'%s' does not exist -- can't clean it",
572                              egg_info)
573            except DistutilsError:
574                pass
575
576class test(Command):
577    description = "run the test suite"
578    user_options = [('args=', None, "options")]
579    def initialize_options(self):
580        self.args = None
581    def finalize_options(self):
582        if self.args:
583            self.args = split_quoted(self.args)
584        else:
585            self.args = []
586    def run(self):
587        pass
588
589class sdist(_sdist):
590    def run(self):
591        build_src = self.get_finalized_command('build_src')
592        build_src.run()
593        _sdist.run(self)
594
595# --------------------------------------------------------------------
596
597if setuptools:
598    try:
599        from setuptools.command import egg_info as mod_egg_info
600        _FileList = mod_egg_info.FileList
601        class FileList(_FileList):
602            def process_template_line(self, line):
603                level = log.set_threshold(log.ERROR)
604                try:
605                    _FileList.process_template_line(self, line)
606                finally:
607                    log.set_threshold(level)
608        mod_egg_info.FileList = FileList
609    except:
610        pass
611
612# --------------------------------------------------------------------
613
614def append(seq, item):
615    if item not in seq:
616        seq.append(item)
617
618def append_dict(conf, dct):
619    for key, values in dct.items():
620        if key in conf:
621            for value in values:
622                if value not in conf[key]:
623                    conf[key].append(value)
624def unique(seq):
625    res = []
626    for item in seq:
627        if item not in res:
628            res.append(item)
629    return res
630
631def flaglist(flags):
632
633    conf = {
634        'define_macros'       : [],
635        'undef_macros'        : [],
636        'include_dirs'        : [],
637
638        'libraries'           : [],
639        'library_dirs'        : [],
640        'runtime_library_dirs': [],
641
642        'extra_compile_args'  : [],
643        'extra_link_args'     : [],
644        }
645
646    if type(flags) is str:
647        flags = flags.split()
648
649    switch = '-Wl,'
650    newflags = []
651    linkopts = []
652    for f in flags:
653        if f.startswith(switch):
654            if len(f) > 4:
655                append(linkopts, f[4:])
656        else:
657            append(newflags, f)
658    if linkopts:
659        newflags.append(switch + ','.join(linkopts))
660    flags = newflags
661
662    append_next_word = None
663
664    for word in flags:
665
666        if append_next_word is not None:
667            append(append_next_word, word)
668            append_next_word = None
669            continue
670
671        switch, value = word[0:2], word[2:]
672
673        if switch == "-I":
674            append(conf['include_dirs'], value)
675        elif switch == "-D":
676            try:
677                idx = value.index("=")
678                macro = (value[:idx], value[idx+1:])
679            except ValueError:
680                macro = (value, None)
681            append(conf['define_macros'], macro)
682        elif switch == "-U":
683            append(conf['undef_macros'], value)
684        elif switch == "-l":
685            append(conf['libraries'], value)
686        elif switch == "-L":
687            append(conf['library_dirs'], value)
688        elif switch == "-R":
689            append(conf['runtime_library_dirs'], value)
690        elif word.startswith("-Wl"):
691            linkopts = word.split(',')
692            append_dict(conf, flaglist(linkopts[1:]))
693        elif word == "-rpath":
694            append_next_word = conf['runtime_library_dirs']
695        elif word == "-Xlinker":
696            append_next_word = conf['extra_link_args']
697        else:
698            #log.warn("unrecognized flag '%s'" % word)
699            pass
700    return conf
701
702# --------------------------------------------------------------------
703
704from distutils.text_file import TextFile
705
706# Regexes needed for parsing Makefile-like syntaxes
707import re as _re
708_variable_rx = _re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
709_findvar1_rx = _re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
710_findvar2_rx = _re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
711
712def makefile(fileobj, dct=None):
713    """Parse a Makefile-style file.
714
715    A dictionary containing name/value pairs is returned.  If an
716    optional dictionary is passed in as the second argument, it is
717    used instead of a new dictionary.
718    """
719    fp = TextFile(file=fileobj,
720                  strip_comments=1,
721                  skip_blanks=1,
722                  join_lines=1)
723
724    if dct is None:
725        dct = {}
726    done = {}
727    notdone = {}
728
729    while 1:
730        line = fp.readline()
731        if line is None: # eof
732            break
733        m = _variable_rx.match(line)
734        if m:
735            n, v = m.group(1, 2)
736            v = str.strip(v)
737            if "$" in v:
738                notdone[n] = v
739            else:
740                try: v = int(v)
741                except ValueError: pass
742                done[n] = v
743                try: del notdone[n]
744                except KeyError: pass
745    fp.close()
746
747    # do variable interpolation here
748    while notdone:
749        for name in list(notdone.keys()):
750            value = notdone[name]
751            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
752            if m:
753                n = m.group(1)
754                found = True
755                if n in done:
756                    item = str(done[n])
757                elif n in notdone:
758                    # get it on a subsequent round
759                    found = False
760                else:
761                    done[n] = item = ""
762                if found:
763                    after = value[m.end():]
764                    value = value[:m.start()] + item + after
765                    if "$" in after:
766                        notdone[name] = value
767                    else:
768                        try: value = int(value)
769                        except ValueError:
770                            done[name] = str.strip(value)
771                        else:
772                            done[name] = value
773                        del notdone[name]
774            else:
775                # bogus variable reference;
776                # just drop it since we can't deal
777                del notdone[name]
778    # save the results in the global dictionary
779    dct.update(done)
780    return dct
781
782# --------------------------------------------------------------------
783