Distutils: сборка нескольких модулей расширения Python (написанных на Swig), которые совместно используют метод

У меня есть четыре файла C ++: A.h, A.cpp, B.h, B.cpp, и A.h включен в B.cpp

хиджры:

#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

а.и.:

%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'])

0

Решение

Я немного изменил ваши файлы, для ясности.

хиджры:

#pragma oncevoid funcA();

a.cpp:

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

а.и.:

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

b.h:

#pragma oncevoid 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 Extensiona = "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.py)
  2. funcB из модуля _b (_b.so, или же _b.cpython-35m-x86_64-linux-gnu.so)
    • Все отсюда происходит в С (или же C ++)
    • Текущий funcB является разные чем один из b.cpp: генерируется глоток спиртного и его зовут _wrap_funcB
    • Предыдущая пуля также относится к funcA а также a.cpp
  3. funcB от b.cpp
  4. funcA от a.cpp

Проблема в том, что код из шага # 4 нет в модуле _b, и он потерпит неудачу во время выполнения. Но все немного странно: сбой (дамп ядра) не появляется, когда funcB называется но у модуля (б -> _b) Импортировать время (я думаю, что это происходит из-за глоток спиртногоза кулисами магии), как видно ниже.

Выход:

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> ls
a.cpp  a.h  a.i  b.cpp  b.h  b.i  setup.py
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> python3 setup.py build > /dev/null 2>&1
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> echo $?
0
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> 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
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> ls ./build/lib.linux-x86_64-3.5
_a.cpython-35m-x86_64-linux-gnu.so  _b.cpython-35m-x86_64-linux-gnu.so
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> PYTHONPATH=${PYTHONPATH}:./build/lib.linux-x86_64-3.5 python3 -c "import _a;_a.funcA()"a.cpp 6 funcA
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> PYTHONPATH=${PYTHONPATH}:./build/lib.linux-x86_64-3.5 python3 -c "import _b"Segmentation fault (core dumped)

Чтобы решить это, либо:

  1. Как вы указали, включают funcA в модуле _b (добавляя a.cpp в ext_bсписок исходных файлов). Таким образом, оба модуля будут автономными (от funcA«s PoV), каждый будет работать независимо, но и funcA будут продублированы в обоих
  2. Делать _b зависит от _a (в конце концов, они общие объекты). Но это не так питон модули расширения должны быть использованы, и он не будет работать на Выиграть (а также на некоторых Ux ароматизаторы). Итак, это больше похоже на (хромой) обходной путь (gainarie)
  3. строить a.cpp в другую общую библиотеку ( .так, но не питон модуль расширения), который будет использоваться обоими модулями. Само собой разумеется, что во время выполнения каждый из них потребует .так присутствовать

Очевидно, к варианту # 3 это идеальный кандидат. Но Distutils ([Python]: Справочник по API) не обеспечивает необходимую функциональность OOTB (очевидно, создание модуля расширения и внешней разделяемой библиотеки, от которой это зависит, не является сценарием, Distutils стремится) или, по крайней мере, я не смог найти ни одного.
Eсть build_clib модуль, который предлагает функциональность построения статический lib (для использования модулями расширения), но это будет то же самое, что и option # 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):
passa_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]
)

Заметки:

  • build_clib_dyn продолжается build_clib так как его функциональность пришлось модифицировать. 2 метода переопределены, но только небольшие их части фактически изменились (комментарии не были скопированы из методов базового класса (питон3.5.4 кодовая база), чтобы уменьшить количество кода, но это не считается изменением)
  • Задача заняла довольно много Distutils просмотр кода, так как некоторые параметры не документированы (и я не очень знаком с ним)
  • Немного Lnx Знание также требовалось, поскольку все становится сложнее при загрузке разделяемых библиотек, которые не находятся в системных путях, а также при сохранении гладкости (например, без изменений). $ {LD_LIBRARY_PATH})
  • build_ext_w_dyn_dep похож на build_clib_dyn (Выиграть только). Так как при строительстве Выиграть библиотека динамических ссылок (.длл), Генерируются 2 файла, и в этом случае они не находятся в одном и том же каталоге, пути поиска в библиотеке нуждаются в некоторых корректировках
  • python3 а также python2 совместимый

Выход (снова выполнив вышеуказанные команды):

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> ls
a.cpp  a.h  a.i  b.cpp  b.h  b.i  setup.py
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> python3 setup.py build > /dev/null 2>&1
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> echo $?
0
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> 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
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> ls build/lib.linux-x86_64-3.5/
_a.cpython-35m-x86_64-linux-gnu.so  _b.cpython-35m-x86_64-linux-gnu.so  libabcommon.so
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> ldd build/lib.linux-x86_64-3.5/_a.cpython-35m-x86_64-linux-gnu.so
linux-vdso.so.1 =>  (0x00007fffadb49000)
libabcommon.so => /home/cfati/Work/Dev/StackOverflow/q050938128/build/lib.linux-x86_64-3.5/libabcommon.so (0x00007f91cd50f000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f91cd18d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f91ccf77000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f91ccbad000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f91cc990000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f91cc687000)
/lib64/ld-linux-x86-64.so.2 (0x00007f91cd916000)
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> PYTHONPATH=${PYTHONPATH}:./build/lib.linux-x86_64-3.5 python3 -c "import _a;_a.funcA()"a.cpp 6 funcA
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q050938128]> PYTHONPATH=${PYTHONPATH}:./build/lib.linux-x86_64-3.5 python3 -c "import _b;_b.funcB()"b.cpp 7 funcB
a.cpp 6 funcA

@ EDIT0:

  • Добавлена ​​поддержка Выиграть (Я думаю, это не очень важно для вопроса)
    • Поскольку структура модуля проста, можно было сделать все на setup.pyуровень (не нужно было изменять исходные файлы)
    • Единственное дополнительное требование заключается в том, что swig.exeреж должен быть в %ДОРОЖКА%
0

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector