【问题标题】:Compiling both C and C++ sources in Cython在 Cython 中编译 C 和 C++ 源代码
【发布时间】:2021-10-30 12:41:15
【问题描述】:

我正在尝试在 Cython 中同时编译 C 和 C++ 源代码。这是我目前的设置:

-setup.py

from distutils.core import setup
from Cython.Build import cythonize
from distutils.extension import Extension
import os

language = "c++"
extra_compile_flags = ["-std=c++17"]
os.environ["CC"] = "clang++"

ext_modules = [
    Extension(
        name="Dummy",
        sources=["mydummy.pyx", "source1.cpp","source2.c"],
        language=language,
        extra_compile_args=extra_compile_flags,
   )
]

ext_modules = cythonize(ext_modules)

setup(
    name="myapp",
    ext_modules=ext_modules,
)

-编译命令:

python3 setup.py build_ext --inplace --verbose

在日志中我收到以下消息:

clang++ -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I. -I/usr/include/python3.6m -I/usr/include/python3.6m -c /path/source2.c -o build/temp.linux-x86_64-3.6/./path/source2.o -std=c++17 -O3
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Wdeprecated]

编译通过,但警告看起来很讨厌。我怎样才能摆脱它? 天真的解决方案

os.environ["CC"] = "clang"
os.environ["CXX"] = "clang++"

error: invalid argument '-std=c++17' not allowed with 'C' 失败

【问题讨论】:

    标签: c++ compiler-errors clang cython clang++


    【解决方案1】:

    gcc(或clang)只是一个前端,它调用适当的编译器(c-或c++-compiler)给定编译文件的文件扩展名(.c意味着它应该用c-compiler和.cpp编译意味着它应该用 c++-compiler 编译),例如这个类似的SO-issue

    因此无需将编译器设置为“clang++”,因为它会自动发生。

    但是,cython 需要知道它必须生成一个 cpp 文件(以及接受 c++ 语法)而不是 c 文件。链接器还必须知道,它必须链接到 cpp 库(如果使用 g++/clang++ 进行链接,它会自动完成)。因此,我们需要将 language = "c++" 添加到 Extension 的定义中,正如 DavidW 的回答中所建议的那样 - 这将解决最后两个问题。

    剩下的问题是,我们希望对不同的源文件使用不同的编译器选项(-std=c++17 仅用于 cpp 文件)。这可以使用一个鲜为人知的命令build_clib来实现:

    #setup.py:
    
    from distutils.core import setup
    from Cython.Build import cythonize
    from distutils.extension import Extension
    
    ext_modules = [
        Extension(
            name="mydummy",
            sources=["mydummy.pyx", "source1.cpp"]
            language="c++",
            extra_compile_args=["-std=c++17"],
       )
    ]
    
    ext_modules = cythonize(ext_modules)
    
    myclib = ('myclib', {'sources': ["source2.c"]})
    
    setup(
        name="mydummy",
        libraries=[myclib],
        ext_modules=ext_modules,
    )
    

    现在使用

    python setup.py build --verbose
    

    python setup.py build_clib --verbose build_ext -i --verbose
    

    将首先构建一个由 C 代码组成的简单静态库,并将其链接到由 c++ 代码组成的扩展。

    上面的代码在构建静态库时使用了默认的编译标志,在你的情况下应该足够了。

    distutils doesn't offer 可以指定附加标志,因此如果有必要,我们必须切换到提供此功能的setuptools,或修补build_clib 命令。

    对于第二种选择,我们必须在上面的setup.py-file 中添加以下内容:

    #setup.py
    ...
    # adding cflags to build_clib
    from distutils import log
    from distutils.command.build_clib import build_clib
    
    # use original implementation but with tweaked build_libraries!
    class build_clib_with_cflags(build_clib):
        def build_libraries(self, libraries):
                for (lib_name, build_info) in libraries:
                    sources = build_info.get('sources')
                    if sources is None or not isinstance(sources, (list, tuple)):
                        raise DistutilsSetupError(
                               "in 'libraries' option (library '%s'), "
                               "'sources' must be present and must be "
                               "a list of source filenames" % lib_name)
                    sources = list(sources)
    
                    log.info("building '%s' library", lib_name)
    
    
                    macros = build_info.get('macros')
                    include_dirs = build_info.get('include_dirs')
                    cflags = build_info.get('cflags')                    # HERE we add cflags
                    objects = self.compiler.compile(sources, 
                                                    output_dir=self.build_temp,
                                                    macros=macros,
                                                    include_dirs=include_dirs,
                                                    extra_postargs=cflags,        # HERE we use cflags
                                                    debug=self.debug)
    
    
                    self.compiler.create_static_lib(objects, lib_name,
                                                    output_dir=self.build_clib,
                                                    debug=self.debug)
    
    ...
    setup(
        ...
        cmdclass={'build_clib': build_clib_with_cflags}, # use our class instead of built-in!
    )
    

    现在我们可以在库定义中添加额外的编译标志(如果使用setuptools,可以跳过上一步):

    ...
    myclib = ('myclib', {'sources': ["source2.c"], 'cflags' : ["-O3"]})
    ...
    

    【讨论】:

    • 我认为您误解了我的一小部分答案:我建议不要在 setup.py 中使用language=c++(它也已经在原始问题中)。相反,我建议只将它放在 pyx 文件中。答案的其余部分看起来像是为不同来源指定不同命令行参数的合理方法
    • 我想我的另一个问题是:扩展是与myclib 相关联,还是myclib 刚刚构建?
    • @DavidW 我很确定 # distutils: language = c++ 与通过 language-argument of Extension 设置的效果相同(它不仅从 pyx-file 生成 cpp-file,而且使用 g++ 进行链接)。
    • @DavidW distutils 负责在链接实际扩展时将构建的 libmyclib.a 传递给链接器。
    • 我认为将语言放入 setup.py 也会将其强制用于sources 中列出的任何 .c 文件(无论如何这不会影响您的答案)。这似乎是错误的,所以我删除了我的答案,因为这是我提出的主要观点。我也不确定libraries 默认情况下是否链接到所有内容(并且没有文档)。我猜你已经测试过了,所以我现在要安静了......
    猜你喜欢
    • 2015-11-29
    • 1970-01-01
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-20
    相关资源
    最近更新 更多