Distutils:构建多个共享方法的 Python 扩展模块(用 Swig 编写)

2024-03-10

我有四个 C++ 文件:A.h、A.cpp、B.h、B.cpp,A.h 包含在 B.cpp 中

A.h:

#pragma once 
void A();

A.cpp:

#include <iostream>
void A() {
    std::cout << "A" << std::endl;
}

B.h:

#pragma once
void B();

B.cpp:

#include "A.h"
#include <iostream>
void B() {
    A();
    std::cout << "B" << std::endl;
}

现在我写了两个SWIG接口文件A.i和B.i

A.i:

%module A
%{
#include "A.h"
%}
%include "A.h"

B.i:

%module B
%{
#include "B.h"
%}
%include "B.h"

setup.py 文件是:

from distutils.core import setup, Extension
A_ext = Extension( "_A", [ "A.i", "A.cpp", ], swig_opts = ['-c++'], extra_compile_args = ['-g'])
B_ext = Extension( "_B", [ "B.i", "B.cpp", ], swig_opts = ['-c++'], extra_compile_args = ['-g'])
setup(
    name = "test",
    version = "1.0",
    ext_modules = [ A_ext, B_ext ],
    py_modules = [ "A", "B" ]
)

如果我输入下面的命令,它将显示“A”。

python -c 'import A; A.A()' 

如果我输入以下命令,则会出现分段错误:

python -c 'import B; B.B()'

我怎样才能让这个命令正确运行?由于我不想多次编译B.cpp,除了下面的方法之外还有什么方法吗?

B_ext = Extension( "_B", [ "B.i", "A.cpp", "B.cpp", ], swig_opts = ['-c++'], extra_compile_args = ['-g'])

为了清楚起见,我对你的文件做了一些更改。

a.h:

#pragma once 


void funcA();

a.cpp:

#include <iostream>


void funcA() {
    std::cout << __FILE__ << " " << __LINE__ <<  " " << __FUNCTION__ <<  std::endl;
}

a.i:

%module a
%{
    #include "a.h"
%}
%include "a.h"

b.h:

#pragma once


void funcB();

b.cpp:

#include "a.h"
#include <iostream>


void funcB() {
    std::cout << __FILE__ << " " << __LINE__ <<  " " << __FUNCTION__ <<  std::endl;
    funcA();
}

b.i:

%module b
%{
    #include "b.h"
%}
%include "b.h"

setup.py:

from distutils.core import setup
from distutils.extension import Extension


a = "a"
b = "b"

ext_a = Extension("_" + a, [a + ".i", a + ".cpp"], swig_opts=("-c++",), extra_compile_args=["-g"])
ext_b = Extension("_" + b, [b + ".i", b + ".cpp"], swig_opts=("-c++",), extra_compile_args=["-g"])

setup(
    name="test",
    version="1.0",
    ext_modules=[ext_a, ext_b],
    py_modules=[a, b]
)

调用时会发生什么(简化)b.funcB(只有堆栈跟踪,进口保留)。每个步骤都会调用下一个步骤:

  1. funcB来自模块b (b.py)
  2. funcB from module _b (_b.so, or _b.cpython-35m-x86_64-linux-gnu.so)
    • 从这里开始的一切都发生在C (or C++)
    • Current funcB is 不同的比来自b.cpp: 它是由生成的swig它的名字是_wrap_funcB
    • 上一条也适用于funcA and a.cpp
  3. funcB from b.cpp
  4. funcA from a.cpp

问题是步骤中的代码#4.不在模块中_b,并且会在运行时失败。但事情有点奇怪:失败(核心转储)不会出现funcB被调用但在模块(b -> _b) import时间(我认为这是因为swig是幕后魔法),如下所示。

Output:

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[064bit prompt]> ls
a.cpp  a.h  a.i  b.cpp  b.h  b.i  setup.py
[064bit prompt]> python3 setup.py build > /dev/null 2>&1
[064bit prompt]> echo $?
0
[064bit prompt]> ls
a.cpp  a.h  a.i  a.py  a_wrap.cpp  b.cpp  b.h  b.i  b.py  build  b_wrap.cpp  setup.py
[064bit prompt]> ls ./build/lib.linux-x86_64-3.5
_a.cpython-35m-x86_64-linux-gnu.so  _b.cpython-35m-x86_64-linux-gnu.so
[064bit prompt]> PYTHONPATH=${PYTHONPATH}:./build/lib.linux-x86_64-3.5 python3 -c "import _a;_a.funcA()"
a.cpp 6 funcA
[064bit prompt]> PYTHONPATH=${PYTHONPATH}:./build/lib.linux-x86_64-3.5 python3 -c "import _b"
Segmentation fault (core dumped)

为了解决这个问题,可以:

  1. 正如你所指出的包括funcA在模块中_b(通过增加a.cpp in ext_b的源文件列表)。这样,两个模块都将是独立的(从funcA's PoV),每个人都会独立工作,而且funcA将在两者中重复
  2. Make _b取决于_a(毕竟,他们是共享对象)。但事实并非如此Python要使用扩展模块,并且它不会工作Win(还有一些Nix口味)。所以,这更像是一个(蹩脚的)解决方法(gainarie)
  3. Build a.cpp到不同的共享库(.so, but not a Python扩展模块)由两个模块使用。不用说,在运行时它们每个都需要.so在场

显然,要选择#3.是完美的候选人。但迪斯图尔斯 ([Python.Docs]:API 参考 https://docs.python.org/3/distutils/apiref.html#api-reference) 不提供所需的功能OOTB(显然构建一个扩展模块和它所依赖的外部共享库,不是一个场景迪斯图尔斯的目标),或者至少,我找不到任何。
有一个构建_clib模块,它提供了构建一个staticlib(由扩展模块使用),但这与选项相同#1..

setup.py:

import sys
import os
from distutils.core import setup
from distutils.extension import Extension
from distutils.command.build_clib import build_clib
from distutils.command.build_ext import build_ext
from distutils.ccompiler import CCompiler


__win = sys.platform[:3].lower() == "win"
export_symbols_option = "export_symbols"


class build_clib_dyn(build_clib):
    def finalize_options(self):
        self.set_undefined_options('build',
                                   ('build_lib', 'build_clib'),
                                   ('build_temp', 'build_temp'),
                                   ('compiler', 'compiler'),
                                   ('debug', 'debug'),
                                   ('force', 'force'))
        self.libraries = self.distribution.libraries
        if self.libraries:
            self.check_library_list(self.libraries)
        if self.include_dirs is None:
            self.include_dirs = self.distribution.include_dirs or []
        if isinstance(self.include_dirs, str):
            self.include_dirs = self.include_dirs.split(os.pathsep)

    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)
            macros = build_info.get('macros')
            include_dirs = build_info.get('include_dirs')
            objects = self.compiler.compile(sources,
                                            output_dir=self.build_temp,
                                            macros=macros,
                                            include_dirs=include_dirs,
                                            debug=self.debug)
            self.compiler.link(CCompiler.SHARED_OBJECT, objects, self.compiler.library_filename(lib_name, lib_type="shared"),
                               output_dir=self.build_clib,
                               export_symbols=build_info.get(export_symbols_option),
                               debug=self.debug)


if __win:
    class build_ext_w_dyn_dep(build_ext):
        def finalize_options(self):
            super(build_ext_w_dyn_dep, self).finalize_options()
            self.library_dirs.append(os.path.dirname(self.build_temp))

else:
    class build_ext_w_dyn_dep(build_ext):
        pass


a_name = "a"
b_name = "b"
common_name = a_name + b_name + "common"
swig_opts = ["-c++"]
libraries = [common_name]
lib_common_build_info = {"sources": [a_name + ".cpp"]}
if __win:
    extra_compile_args = None
    extra_link_args = None
    lib_common_build_info[export_symbols_option] = ["funcA"]
else:
    extra_compile_args = ["-g"]
    extra_link_args = ["-Wl,-rpath,${ORIGIN}"]

lib_common_info = (common_name, lib_common_build_info)
ext_a = Extension("_" + a_name, [a_name + ".i"], libraries=libraries, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, swig_opts=swig_opts)
ext_b = Extension("_" + b_name, [b_name + ".i", b_name + ".cpp"], libraries=libraries, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, swig_opts=swig_opts)

setup(
    name="test",
    version="1.0",
    libraries=[lib_common_info],
    cmdclass={"build_clib": build_clib_dyn, "build_ext": build_ext_w_dyn_dep},
    ext_modules=[ext_a, ext_b],
    py_modules=[a_name, b_name]
)

Notes:

  • 构建_clib_dyn延伸构建_clib因为必须修改其功能。重写了 2 个方法,但实际上只更改了其中的一小部分(注释没有从基类方法复制(Python3.5.4
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Distutils:构建多个共享方法的 Python 扩展模块(用 Swig 编写) 的相关文章

随机推荐

  • Java简单计算器

    我用Java编写了这个计算器程序 仅当同时计算两个数字时 此方法才有效 这意味着要获得 1 2 3 的总和 您必须这样做 按 1 按 按 2 按 按 按 3 按 计算结果为 6 但我想对此进行编程 以便我可以通过以下方式获得答案 按 1 按
  • 在 Pi 上通过 python 执行 bluetoothctl 命令

    每次我想在我的 pi 上运行 python 脚本之前 我都会在终端上运行一系列 Bluetoothctl 命令 我想从 pi 自动连接到 BLE 设备 无需任何配对确认或用户交互 以下是每次在运行另一个 python 脚本之前重新启动 pi
  • 事务性与非事务性 msmq

    我不断看到文档 http msdn microsoft com en us library ms702030 VS 85 aspx表示不可能发送到事务范围之外的远程事务 msmq 队列 我发现这很难相信 因为我think我已经这样做了好几个
  • 无法将新元数据附加到捕获的图像

    我正在尝试将我自己的一些字段附加到我捕获的图像中 我似乎能够更改现有的 EXIF 条目 但无法添加新条目 无论是在 EXIF 字典中还是作为单独的字典添加到图像中 当我进行添加时 我可以将它们视为图像数据的一部分 但它们永远不会保存到图像文
  • Acumatica 过滤 PXSelector 具有快速搜索功能

    我有一个自定义 dac 和 pxselector 如下所示 With quick search red bordered search I want to filter with every properties For example w
  • main 的 ret 指令去哪里了

    我在 Programming from ground up 一书中了解了汇编 x86 如何在全球范围内工作 在本书中 每个程序都以退出中断调用结束 然而 在C编译的程序中 我发现程序以ret结尾 这假设有一个地址要弹出 这将导致程序结束 所
  • TYPO3 Extbase如何清空ObjectStorage

    我想在更新对象时 清空 ObjectStorage 它是 TYPO3 4 6 带有 Extbase 扩展 允许您在前端显示 添加 编辑 删除数据集 乍一看一切看起来都不错 我有一个字段引用另一张表 TCA partner gt array
  • Fortran 77 续行[重复]

    这个问题在这里已经有答案了 所以我必须用 fortran77 和 f 文件扩展名编写这个程序 并且我不想使用选项来编译它 我想修复该错误 但由于某种原因 我尝试过的所有操作仍然给我这个错误 我已经包含了代码和错误终端输出 程序 p1 隐式无
  • Android getActivity() 在片段内总是返回 null

    我遇到了一个奇怪的问题 getActivity 方法always在片段内返回 null 我在 onAttach 和 onCreateView 完成运行后调用它 该片段位于包含片段堆栈的 FragmentActivity 内 我向其中添加片段
  • 上传的图片仅在刷新页面后可用

    当我上传图片时 文件保存成功 路径设置成功 但表单提交后上传的图像并没有立即显示 仅当我重新加载页面时 才会显示上传的图像 我正在保存上传的文件 如下所示 InputStream is try File file new File C Do
  • 未找到 CodeIgniter 对象,仅索引函数有效

    我是 CodeIgniter 的新手 一切都很顺利 直到我发现我只能打电话给index 功能 我已经设置了config php autoload php and routes php正如预期的那样 在 config php 上 config
  • 从 .eml 文件获取文本的最佳方法是什么?

    我尝试从本地驱动器上的几个 eml 文件获取主题和消息正文 现在我尝试使用 Apache Commons Email 但有时它会循环且没有错误 这是我的代码 它应该从 eml 获取文本并将其保存到 txt MimeMessage mimeM
  • 如何修复 x86 Turbo 汇编语言的输出

    DOSSEG MODEL SMALL STACK 100h DATA UserName db Name CLRF db 10 13 inputName db 24 dup CurYear db Current Year inputCurre
  • GWT java.util.Date 序列化错误

    GWT 无法正确序列化 Java 日期 当我尝试通过网络发送用 Javascript 创建的日期时 我发现 1983 年之前的年份中 4 月 1 日 有趣 到 10 月 25 日之间的日期会减去一天 这意味着 例如 1982 04 01 和
  • Databricks 删除增量表?

    如何在 Databricks 中删除增量表 我在文档中找不到任何信息 也许唯一的解决方案是使用 magic 命令或 dbutils 删除文件夹 delta 内的文件 fs rm r delta mytable EDIT 为了澄清起见 我在这
  • 如何在 Mobile Safari 中检测 3G 与 Wifi 连接?

    我特别询问的是移动网页上的 javascript 而不是 Objective C 应用程序 是否有像 Apple Reachability for javascript 这样的东西 以便我可以知道 Edge 3G 或 Wifi 连接何时建立
  • 如何从 cognito 异步函数返回响应并将其显示在页面上?

    给出下面的代码 当响应成功或失败时 如何将响应输出回网页 loginUser data var authenticationData Username data email Password data password var authen
  • ConfigParser - 打印 config.sections() 返回 []

    我正在尝试使用ConfigParser模块来解析 ini文件 问题是当我尝试打印时sections或者其他什么 它返回空列表 配置文件 SERVER host localhost port 9999 max clients 5 REGULA
  • 如何在 Android NDK 中创建新的 NativeWindow?

    我尝试了 test libstdc 演示 并且喜欢我可以从控制台启动它 而无需运行成熟的 Android 应用程序 我想创建一个 opengl 上下文 以便我能够从控制台内执行我的 opengl es 2 0 应用程序 而无需太多麻烦 知道
  • Distutils:构建多个共享方法的 Python 扩展模块(用 Swig 编写)

    我有四个 C 文件 A h A cpp B h B cpp A h 包含在 B cpp 中 A h pragma once void A A cpp include