How to identify compiler before defining cython extensions?

2.1k views Asked by At

I'm building a platform independent cython project where I want to pass compiler args based on the compiler being used. I can guess the compiler based on platform or assume it's the same compiler used for Python but it's not guaranteed to match. Normally I inject into the cmdclass arg to setuptool's setup method and wrap the install or build_ext commands to check internal state. But in this case I have to cythonize the extension modules before I reach the wrappers.

Is there any way to determine the compiler inside setup.py before cythonizing the extension modules?

3

There are 3 answers

2
Pyrce On BEST ANSWER

After posting on the cython forums and searching for related issues in distutils I found this post showing how to move the compiler arguments into the build_ext assignment. If I subsequently remove all compiler arguments from the extension class I can now lazy assign them inside the command class as I expected. I can also get install and egg_info command classes to call my new version of the build_ext as well.

from setuptools.command.build_ext import build_ext

BUILD_ARGS = defaultdict(lambda: ['-O3', '-g0'])
for compiler, args in [
        ('msvc', ['/EHsc', '/DHUNSPELL_STATIC']),
        ('gcc', ['-O3', '-g0'])]:
    BUILD_ARGS[compiler] = args
    
class build_ext_compiler_check(build_ext):
    def build_extensions(self):
        compiler = self.compiler.compiler_type
        args = BUILD_ARGS[compiler]
        for ext in self.extensions:
            ext.extra_compile_args = args
        build_ext.build_extensions(self)

...
setup(
    ...
    cmdclass={ 'build_ext': build_ext_compiler_check })
0
Holger Joukl On

(Sorry can't comment due to missing credit)

Unfortunately, the answer https://stackoverflow.com/a/32192172/7379507 is slightly misleading as a build_ext instance's self.compiler.compiler_type is 'unix', not 'gcc' (the "distutils compiler_class").

I.e. the lookup from this defaultdict dictionary

BUILD_ARGS = defaultdict(lambda: ['-O3', '-g0'])
for compiler, args in [
    ('msvc', ['/EHsc', '/DHUNSPELL_STATIC']),
    ('gcc', ['-O3', '-g0'])]:
BUILD_ARGS[compiler] = args

wouldn't normally reach the 'gcc' entry but instead always fall back to the defaultdict's default (the lambda function).

That said you probably wouldn't notice this in a majority of cases as long as the default options remain the same as the 'gcc' options. E.g. clang seems to understand the same options as gcc.

It looks like you might be able to get at the actual invoked compiler name through self.compiler.compiler[0], though I haven't checked if this is reliable or portable.

0
Evandro Coan On

A simple variation of the first answer:

from setuptools import setup, Extension
from distutils.command.build_ext import build_ext

myextension = Extension(
    name = 'packagename',
    sources = [
        'source/debugger.cpp',
    ],
    include_dirs = [ 'source' ],
)

class build_ext_compiler_check(build_ext):
    def build_extensions(self):
        compiler = self.compiler.compiler_type

        # print('\n\ncompiler', compiler)
        if not 'msvc' in compiler:

            for extension in self.extensions:

                if extension == myextension:
                    extension.extra_compile_args.append( '-O0' )
                    extension.extra_compile_args.append( '-std=c++11' )

        super().build_extensions()

setup(
        name = 'packagename',
        version = __version__,
        ext_modules= [ myextension ],
    )