为了清楚起见,我对你的文件做了一些更改。
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(只有堆栈跟踪,进口保留)。每个步骤都会调用下一个步骤:
-
funcB来自模块b (b.py)
-
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
-
funcB from b.cpp
-
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)
为了解决这个问题,可以:
- 正如你所指出的包括funcA在模块中_b(通过增加a.cpp in ext_b的源文件列表)。这样,两个模块都将是独立的(从funcA's PoV),每个人都会独立工作,而且funcA将在两者中重复
- Make _b取决于_a(毕竟,他们是共享对象)。但事实并非如此Python要使用扩展模块,并且它不会工作Win(还有一些Nix口味)。所以,这更像是一个(蹩脚的)解决方法(gainarie)
- 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