【问题标题】:How to identify compiler before defining cython extensions?如何在定义 cython 扩展之前识别编译器?
【发布时间】:2015-09-08 06:05:04
【问题描述】:

我正在构建一个独立于平台的 cython 项目,我想根据正在使用的编译器传递编译器参数。我可以猜测基于平台的编译器,或者假设它与用于 Python 的编译器相同,但不能保证匹配。通常我将cmdclass arg 注入到 setuptool 的 setup 方法中,并包装 install 或 build_ext 命令以检查内部状态。但在这种情况下,我必须在到达包装器之前对扩展模块进行 cythonize。

在对扩展模块进行 cythonizing 之前,有什么方法可以确定 setup.py 中的编译器吗?

【问题讨论】:

  • 你不能将编译器作为参数传递给 setup.py:python setup.py build --compiler=mingw32?
  • 也可以使用cmake跨平台编译cython代码:github.com/thewtex/cython-cmake-example
  • @denfromufa 您可以传递--compiler=mingw32,但存储库的其他接收者不一定知道将编译器参数设置为什么,或者它是否是另一个存储库的依赖项。而pip install 绝对不会为 setuptools 创建这样的论点。如果我自己只使用python setup.py install,我可以阅读这个论点——这是真的。
  • 我之前也没有见过 cython-cmake -- 我会调查一下。如果我可以避免它,我宁愿不将复杂的 CMake 模式引入存储库,而是希望根据选择的编译器(由用户或 setuptools 或 python)在 setuptools 中设置适当的编译器标志。但它似乎允许在定义 cythonization 后使用编译器特定的参数。

标签: cython setuptools


【解决方案1】:

在 cython 论坛上发帖并在 distutils 中搜索相关问题后,我发现 this post 展示了如何将编译器参数移动到 build_ext 分配中。如果我随后从扩展类中删除所有编译器参数,我现在可以按照我的预期在命令类中延迟分配它们。我还可以获得installegg_info 命令类来调用我的新版本的build_ext。

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 })

【讨论】:

  • from setuptools.command.build_ext import build_ext 似乎也有同样的效果(如果我没记错的话,之前的链接有 distutils,人们认为现在已弃用或完全集成到 setuptools 中;如果我错了,请纠正我)跨度>
  • 没错。 setuptool 导入的工作方式完全相同,应该用来代替 distutils 导入;虽然我不知道 distutils 什么时候会真正消失或应该日落,因为我只看到它在 python 板上被提及为一个愿望而不是一个要求。
【解决方案2】:

第一个答案的简单变体:

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 ],
    )

【讨论】:

    【解决方案3】:

    (抱歉,由于缺少信用,无法发表评论)

    不幸的是,https://stackoverflow.com/a/32192172/7379507 的答案有点误导,因为 build_ext 实例的 self.compiler.compiler_type 是“unix”,而不是“gcc”(“distutils compiler_class”)。

    即从此 defaultdict 字典中查找

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

    通常不会到达“gcc”条目,而是总是回退到 defaultdict 的默认值(lambda 函数)。

    也就是说,只要默认选项与“gcc”选项保持一致,在大多数情况下您可能不会注意到这一点。例如。 clang 似乎理解与 gcc 相同的选项。

    看起来您可以通过self.compiler.compiler[0] 获得实际调用的编译器名称,但我尚未检查这是否可靠或可移植。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-09
      • 2018-04-25
      • 1970-01-01
      • 2013-02-07
      • 2023-03-16
      • 1970-01-01
      相关资源
      最近更新 更多