如何使用Autotools在构建库的同时构建Python接口

5
我目前有一个用GNU autotools编写的C++库,并且想为其添加Python接口。我已经使用SWIG开发了接口,但是我遇到了一些问题,不知道如何将Python模块的编译与整个过程集成起来。
我尝试了AM_PATH_PYTHON,但是这个宏似乎没有设置Python.h的包含路径,因此在编译模块时会出现许多缺少包含文件的错误。有没有办法从AM_PATH_PYTHON中获取Python包含路径和ldflags?
仅供参考,我认为不可能使用Python的distutils方法(setup.py),因为这需要库的位置才能链接新模块。由于库在编译时尚未安装,因此我必须使用相对路径(例如../src/lib.so),这当然会在安装Python模块后破坏(因为库现在位于/usr/lib或/usr/local/lib中)。
编辑:
现在它可以找到正在编译的.h文件,但是在安装它之后(在正确的位置),Python无法加载这个模块。代码生成了foo.so,当我“import foo”时,我得到以下错误:
ImportError: dynamic module does not define init function (initfoo)
但是,如果我将其从foo.so重命名为_foo.so,那么它就可以正常加载和运行,只是我必须“import _foo”,这不是我想要的。按照SWIG的指示,在当前目录中生成_foo.so时,“import foo”可以正常工作,因此我不确定为什么将库安装在站点目录中时它会出现问题。
第二次编辑:
原来问题是我忘记将SWIG生成的foo.py复制到与_foo.so相邻的安装目录中。一旦我这样做了,一切都按预期工作!现在我只需要弄清楚一些自动化规则,将文件复制到安装目录中即可...

要将一个非可执行文件复制到目录中,你可能需要像这样使用 pkgpythondir_DATA = foo.py - Jack Kelly
4个回答

6
为了找到包含路径,我会使用python-config。诀窍是使用与安装在$PYTHON中的Python相对应的python-config
AM_PATH_PYTHON
AC_ARG_VAR([PYTHON_INCLUDE], [Include flags for python, bypassing python-config])
AC_ARG_VAR([PYTHON_CONFIG], [Path to python-config])
AS_IF([test -z "$PYTHON_INCLUDE"], [
  AS_IF([test -z "$PYTHON_CONFIG"], [
    AC_PATH_PROGS([PYTHON_CONFIG],
                  [python$PYTHON_VERSION-config python-config],
                  [no],
                  [`dirname $PYTHON`])
    AS_IF([test "$PYTHON_CONFIG" = no], [AC_MSG_ERROR([cannot find python-config for $PYTHON.])])
  ])
  AC_MSG_CHECKING([python include flags])
  PYTHON_INCLUDE=`$PYTHON_CONFIG --includes`
  AC_MSG_RESULT([$PYTHON_INCLUDE])
])

还有一种选择是在 distutils.sysconfig 模块中查找信息(与使用 distutils 构建代码无关)。运行 python -c "import distutils.sysconfig; help(distutils.sysconfig)" 并查看。


谢谢 - 这个方法有效了,但是现在我遇到了最终的.so二进制文件的问题,我已经更新了原始问题。 - Malvineous

3

这里是我从我的"configure.ac"中调用的autoconf宏,用来查找Python包含目录(PYTHONINC)和Python安装目录(通过 AM_PATH_PYTHON 获得)。

AC_DEFUN([adl_CHECK_PYTHON], 
 [AM_PATH_PYTHON([2.0])
  AC_CACHE_CHECK([for $am_display_PYTHON includes directory],
    [adl_cv_python_inc],
    [adl_cv_python_inc=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_inc()" 2>/dev/null`])
  AC_SUBST([PYTHONINC], [$adl_cv_python_inc])])

然后我的wrap/python/Makefile.am像这样使用Libtool构建了两个Swig模块:

SUBDIRS = . cgi-bin ajax tests

AM_CPPFLAGS = -I$(PYTHONINC) -I$(top_srcdir)/src $(BUDDY_CPPFLAGS) \
              -DSWIG_TYPE_TABLE=spot

EXTRA_DIST = spot.i buddy.i
python_PYTHON = $(srcdir)/spot.py $(srcdir)/buddy.py
pyexec_LTLIBRARIES = _spot.la _buddy.la

MAINTAINERCLEANFILES = \
  $(srcdir)/spot_wrap.cxx $(srcdir)/spot.py \
  $(srcdir)/buddy_wrap.cxx $(srcdir)/buddy.py

## spot

_spot_la_SOURCES = $(srcdir)/spot_wrap.cxx $(srcdir)/spot_wrap.h
_spot_la_LDFLAGS = -avoid-version -module
_spot_la_LIBADD = $(top_builddir)/src/libspot.la

$(srcdir)/spot_wrap.cxx: $(srcdir)/spot.i
        $(SWIG) -c++ -python -I$(srcdir) -I$(top_srcdir)/src $(srcdir)/spot.i


$(srcdir)/spot.py: $(srcdir)/spot.i
        $(MAKE) $(AM_MAKEFLAGS) spot_wrap.cxx

## buddy

_buddy_la_SOURCES = $(srcdir)/buddy_wrap.cxx
_buddy_la_LDFLAGS = -avoid-version -module $(BUDDY_LDFLAGS)

$(srcdir)/buddy_wrap.cxx: $(srcdir)/buddy.i
        $(SWIG) -c++ -python $(BUDDY_CPPFLAGS) $(srcdir)/buddy.i

$(srcdir)/buddy.py: $(srcdir)/buddy.i
        $(MAKE) $(AM_MAKEFLAGS) buddy_wrap.cxx

上述规则是为了使Swig的结果被视为源文件(即在tarball中分发),就像Bison生成的解析器一样。这样,最终用户不需要安装Swig。
当您运行make时,*.so文件会被Libtool隐藏在某个.libs/目录中,但在make install之后,它们会被复制到正确的位置。
唯一的问题是如何在运行make install之前从源目录中使用模块,例如在运行make check时。为此,我使用configure生成一个名为run的脚本,在运行任何python脚本之前设置PYTHONPATH,并通过此run脚本执行所有测试用例。以下是run.in的内容,在configure替换任何值之前:
# Darwin needs some help in figuring out where non-installed libtool
# libraries are (on this platform libtool encodes the expected final
# path of dependent libraries in each library).
modpath='../.libs:@top_builddir@/src/.libs:@top_builddir@/buddy/src/.libs'

# .. is for the *.py files, and ../.libs for the *.so.  We used to
# rely on a module called ltihooks.py to teach the import function how
# to load a Libtool library, but it started to cause issues with
# Python 2.6.
pypath='..:../.libs:@srcdir@/..:@srcdir@/../.libs:$PYTHONPATH'

test -z "$1" &&
  PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath exec @PYTHON@

case $1 in
  *.py)
    PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath exec @PYTHON@ "$@";;
  *.test)
    exec sh -x "$@";;
  *)
    echo "Unknown extension" >&2
    exit 2;;
esac

如果您想在实际项目中看到所有这些操作,可以从https://spot.lrde.epita.fr/install.html获取。

1

在命令行上手动构建

如果您没有将SWIG步骤添加到makefile中,您可以使用以下命令构建SWIG扩展(如果您有source_file.cpp和your_extension.i来创建Python模块):

  # creation of your_extension_wrap.cpp
  swig -c++ -python -o your_extension_wrap.cpp your_extension.i
  # creation of your_extension_wrap.o, source_file.o and your_extension.py
  g++ -fPIC -c your_extension_wrap.cpp source_file.cpp -I/usr/include/python2.7
  # creation of the library _your_extension.so
  g++ -shared your_extension_wrap.o source_file.o -o _your_extension.so

注意: 对于共享对象的名称,重要的是在前面加上下划线,否则当执行import your_extension时,Python将无法找到它。

将SWIG添加到Autoconf

我用Python编写了一个小程序,其中需要一个单独的C ++文件(例如舍入方法)。

您需要额外的Autoconf宏来启用SWIG支持。正如johanvdw所指出的那样,如果您使用这两个m4宏:ax_pck_swig和ax_swig_python,将更容易。我从Autoconf Macro Archive下载并将其放置在项目树的m4子目录中:

trunk
    ├── configure.ac
    ├── __init__.py
    ├── m4
    │   ├── ax_pkg_swig.m4
    │   ├── ax_swig_python.m4
    │   ├── libtool.m4
    │   ├── lt~obsolete.m4
    │   ├── ltoptions.m4
    │   ├── ltsugar.m4
    │   └── ltversion.m4
    ├── Makefile.am
    ├── rounding_swig
    │   ├── compile.txt
    │   ├── __init__.py
    │   ├── Makefile.am
    │   ├── rnd_C.cpp
    │   ├── rounding.i
    │   ├── rounding_wrap.cpp
    └── src
        ├── cadna_add.py
        ├── cadna_computedzero.py
        ├── cadna_convert.py
        ├── __init__.py
        └── Makefile.am

当您将两个m4宏放在子目录中时,您需要将此行添加到trunk/Makefile.am中:
ACLOCAL_AMFLAGS = -I m4

现在,让我们看一下 trunk/configure.ac :
AC_PREREQ([2.69]) # Check autoconf version 
AC_INIT(CADNA_PY, 1.0.0, cadna-team@lip6.fr) # Name of your software
AC_CONFIG_SRCDIR([rounding_swig/rnd_C.cpp]) # Name of the c++ source
AC_CONFIG_MACRO_DIR(m4)     # Indicate where are your m4 macro
AC_CONFIG_HEADERS(config.h)
AM_INIT_AUTOMAKE

AC_DISABLE_STATIC #enable shared libraries

# Checks for programs.
AC_PROG_LIBTOOL  # check libtool
AC_PROG_CXX  # check c++ compiler
AM_PATH_PYTHON(2.3) # check python version
AX_PKG_SWIG(1.3.21) # check swig version
AX_SWIG_ENABLE_CXX # fill some variable usefull later
AX_SWIG_PYTHON # same

# Checks for header files.
AC_CHECK_HEADERS([fenv.h stdlib.h string.h]) # any header needed by your c++ source
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
AC_TYPE_SIZE_T
# Checks for library functions.
AC_FUNC_MALLOC
AC_CHECK_FUNCS([fesetround memset strstr])

AC_CONFIG_FILES([
        Makefile
    src/Makefile
    rounding_swig/Makefile
        ])

LIBPYTHON="python$PYTHON_VERSION" # define the python interpreter
LDFLAGS="$LDFLAGS -l$LIBPYTHON"
AC_OUTPUT

在Automake中添加SWIG

在您的trunk/Makefile.am文件中,您需要执行以下操作:

ACLOCAL_AMFLAGS = -I m4

# Indicate the subdir of c++ file and python file
SUBDIRS = src rounding_swig

# Indicate a list of all the files that are part of the package, but
# are not installed by default and were not specified in any other way
EXTRA_DIST= \
rounding_swig/rounding.i \
rounding_swig/testrounding.py \
rounding_swig/testrounding.cpp

在你的 trunk/src/Makefile.am 文件中:
# Python source files that will be install in prefix/lib/name_of_your_python_interpreter/site-packages/name_of_your_project
pkgpython_PYTHON = cadna_add.py cadna_computedzero.py cadna_convert.py

困难的部分在trunk/rounding_swig/Makefile.am中。它创建了一个库.la和一个_your_extension.so,并将其放置在prefix/lib64/python2.7/site-packages/name_of_the_project/。

# Name of the cpp source file
BUILT_SOURCES = rounding_wrap.cpp
# Name of the swig source file
SWIG_SOURCES = rounding.i
# Python source files that will be install in prefix/lib/name_of_your_python_interpreter/site-packages/name_of_your_project
pkgpython_PYTHON = rounding.py __init__.py
pkgpyexec_LTLIBRARIES = _rounding.la
_rounding_la_SOURCES = rounding_wrap.cpp $(SWIG_SOURCES) rnd_C.cpp
_rounding_la_CPPFLAGS = $(AX_SWIG_PYTHON_CPPFLAGS) -I$(top_srcdir)/rounding_swig -I/usr/include/python@PYTHON_VERSION@ -lpython@PYTHON_VERSION@
_rounding_la_LDFLAGS = -module

rounding_wrap.cpp: $(SWIG_SOURCES)
    $(SWIG) $(AX_SWIG_PYTHON_OPT) -I$(top_srcdir)/rounding_swig -I/usr/include/python@PYTHON_VERSION@ -o $@ $<

最后,如果你没有其他5个宏命令,你可以通过输入以下内容获得它们:
autoreconf -i

最后,安装您的项目:

libtoolize && aclocal && autoheader && autoconf && automake -a -c
./configure --prefix=<install prefix>
make
make install

PS:这里是一个过时但简单的教程,帮助了我(过时是因为它使用的ac_pkg_swig.m4在新版本的swig中失败)。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接