Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

numpy distutils -- Try to compile something and set flags if you fail

One common way to build source code on *NIX platforms is to use a configure script. Under the hood, configure tries to build a bunch of test programs to determine which libraries you have accessible. It then generates a header file which gets included in the project that defines a bunch of macros conditionally so that the programmer can supply an alternative or build a stripped down version of a library/program if a particular "dependency" is missing. Is there anything functionally equivalent using numpy.distutils?

As an example, here is my setup.py:

from numpy.distutils.misc_util import Configuration

def configuration(parent_package='',top_path=None):
    config = Configuration('pyggcm',parent_package,top_path)

    #TODO: Currently, I have some macros to conditionally build the seek-code
    #Unfortunately, that's not the best solution (by far).  Perhaps if we
    #changed to using stream access it would work better, without the need
    #for these silly macros.
    config.add_extension('_fortfile',sources=['_fortfile/_fortfile.F90'],
                         define_macros=[
                             ('FSEEKABLE',1),  #compiler provides fseek and ftell
                             ('HAVE_STREAM',1) #compiler provides access='stream' for opening files. (f2003 standard)
                             ])  

    config.add_extension('jrrle',sources=['jrrle/jrrle.f90'])
    config.add_scripts(['scripts/ggcm_timehist',
                        'scripts/ggcm_plasmasheet',
                        'scripts/ggcm_plot'])
    return config


from numpy.distutils.core import setup    
setup(configuration=configuration)

This is unconditionally building the FSEEKABLE code and would need to be manually edited if the users Fortran compiler doesn't support that (The macros wrap the fseek and ftell GNU intrinsic function). Is there a way to determine whether the Fortran compiler supplies those intrinsic functions?

like image 763
mgilson Avatar asked Nov 12 '22 06:11

mgilson


1 Answers

Try this:

import os
import shutil
import tempfile
from distutils.ccompiler import new_compiler

def hasfunction(cc, funcname, include=None, extra_postargs=None):
    tmpdir = tempfile.mkdtemp(prefix='hasfunction-')
    devnull = oldstderr = None
    try:
        try:
            fname = os.path.join(tmpdir, 'funcname.c')
            f = open(fname, 'w')
            if include is not None:
                f.write('#include %s\n' % include)
            f.write('int main(void) {\n')
            f.write('    %s;\n' % funcname)
            f.write('}\n')
            f.close()
            devnull = open(os.devnull, 'w')
            oldstderr = os.dup(sys.stderr.fileno())
            os.dup2(devnull.fileno(), sys.stderr.fileno())
            objects = cc.compile([fname], output_dir=tmpdir,
                                 extra_postargs=extra_postargs)
            cc.link_executable(objects, os.path.join(tmpdir, 'a.out'))
        except Exception as e:
            return False
        return True
    finally:
        if oldstderr is not None:
            os.dup2(oldstderr, sys.stderr.fileno())
        if devnull is not None:
            devnull.close()
        shutil.rmtree(tmpdir)

Example:

def detect_sse3():
    "Does this compiler support SSE3 intrinsics?"
    compiler = new_compiler()
    return hasfunction(compiler, '__m128 v; _mm_hadd_ps(v,v)',
                       include='<pmmintrin.h>',
                       extra_postargs=['-msse3'])
like image 134
Robert T. McGibbon Avatar answered Nov 14 '22 23:11

Robert T. McGibbon