Как да разберете кой компилатор ще бъде извикан за разширение на Python C в setuptools?

Имам разширение на Python C++, което изисква следните флагове за компилация, когато се компилира с clang на OSX:

CPPFLAGS='-std=c++11 -stdlib=libc++ -mmacosx-version-min=10.8'
LDFLAGS='-lc++'

Откриването на OSX в моя setup.py е достатъчно лесно. Мога да го направя:

if sys.prefix == 'darwin':
    compile_args.append(['-mmacosx-version-min=10.8', '-stdlib=libc++'])
    link_args.append('-lc++')

(Вижте https://github.com/honnibal/spaCy/blob/ba1d3ddd7f527d2e6e41b86da0f2887cc4dec83a/setup.py#L70 за пълен контекст)

В GCC обаче този флаг за компилиране е невалиден. Така че компилирането ще се провали, ако някой се опитва да използва GCC на OSX, ако напиша setup.py по този начин.

GCC и clang поддържат различни флагове на компилатора. Така че трябва да знам кой компилатор ще бъде извикан, за да мога да изпращам различни флагове. Какъв е правилният начин за откриване на компилатора в setup.py?

Редактиране 1:

Имайте предвид, че не се създава изключение на Python за грешки при компилация:

$ python setup.py build_ext --inplace
running build_ext
building 'spacy.strings' extension
gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -c spacy/strings.cpp -o build/temp.linux-x86_64-2.7/spacy/strings.o -O3 -mmacosx-version-min=10.8 -stdlib=libc++
gcc: error: unrecognized command line option ‘-mmacosx-version-min=10.8’
gcc: error: unrecognized command line option ‘-stdlib=libc++’
error: command 'gcc' failed with exit status 1
$

person syllogism_    schedule 21.02.2015    source източник
comment
Какво изключение се случва (във вашия скрипт на Python), когато подадете на GCC лош флаг? Правилният начин би бил try: send_flags_for_clang() except ThatException: send_flags_for_gcc().   -  person Two-Bit Alchemist    schedule 21.02.2015
comment
Вижте Редактиране --- не се създава изключение.   -  person syllogism_    schedule 21.02.2015


Отговори (2)


Попаднах на въпроса ви, тъй като имам нужда от същия вид превключвател. Освен това в моя случай sys.prefix не е страхотен, тъй като флаговете са за clang независимо от платформата.

Не съм сигурен, че е перфектно, но ето какво работи най-добре за мен. И така, проверявам дали е зададена променлива CC; ако не, проверявам това, което предполагам, че distutils гледа.

Всяко по-добро решение е добре дошло!

import os
import distutils

try:
    if os.environ['CC'] == "clang":
        clang = True
except KeyError:
    clang = False

if clang or distutils.sysconfig_get_config_vars()['CC'] == 'clang':
    try:
        _ = os.environ['CFLAGS']
    except KeyError:
        os.environ['CFLAGS'] = ""
    os.environ['CFLAGS'] += " -Wno-unused-function"
    os.environ['CFLAGS'] += " -Wno-int-conversion"
    os.environ['CFLAGS'] += " -Wno-incompatible-pointer-types

Забележка за сърдитите момчета: С удоволствие бих използвал опцията extra_compile_args, но тя поставя флаговете на грешна позиция в командата за компилиране clang.

person xoolive    schedule 11.06.2016

Добавете следния код към вашия setup.py. Той изрично открива кои флагове се приемат от компилатора и след това се добавят само те.

# check whether compiler supports a flag
def has_flag(compiler, flagname):
    import tempfile
    from distutils.errors import CompileError
    with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f:
        f.write('int main (int argc, char **argv) { return 0; }')
        try:
            compiler.compile([f.name], extra_postargs=[flagname])
        except CompileError:
            return False
    return True


# filter flags, returns list of accepted flags
def flag_filter(compiler, *flags):
    result = []
    for flag in flags:
        if has_flag(compiler, flag):
            result.append(flag)
    return result


class BuildExt(build_ext):
    # these flags are not checked and always added
    compile_flags = {"msvc": ['/EHsc'], "unix": ["-std=c++11"]}

    def build_extensions(self):
        ct = self.compiler.compiler_type
        opts = self.compile_flags.get(ct, [])
        if ct == 'unix':
            # only add flags which pass the flag_filter
            opts += flag_filter(self.compiler,
                                '-fvisibility=hidden', '-stdlib=libc++', '-std=c++14')
        for ext in self.extensions:
            ext.extra_compile_args = opts
        build_ext.build_extensions(self)

setup(
   cmdclass=dict(build_ext=BuildExt),
   # other options...
)

Методът has_flag е взет от този пример на pybind11. https://github.com/pybind/python_example

person olq_plo    schedule 04.02.2019