簡體   English   中英

如何使用autotools在構建庫的同時構建Python接口

[英]How to use autotools to build Python interface at same time as library

我目前有一個用 C++ 編寫的庫,使用 GNU autotools 構建,我想向它添加一個 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,當我“導入 foo”時,我得到了這個:

ImportError: dynamic module does not define init function (initfoo)

但是,如果我將它從 foo.so 重命名為 _foo.so,那么它會加載並運行良好,除非我必須“導入 _foo”,而我不想這樣做。 當我按照 SWIG 說明在當前目錄“import foo”中生成 _foo.so 時,我不確定為什么在站點目錄中安裝庫時它會中斷。

編輯2:

原來問題是我忘記將 SWIG 生成的 foo.py 與 _foo.so 一起復制到安裝目錄中。 一旦我這樣做了,一切都按預期進行! 現在我只需要找出一些 automake 規則來將文件復制到安裝目錄中......

要找到包含路徑,我會使用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)"看看。

這是我從我的 ``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 ,Libtool 會將*.so文件隱藏在某些.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獲取

在命令行上手動構建

在不將 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

將 SWIG 添加到 Automake

在您的主干/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

在您的主干/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 並將其放在前綴/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 中失敗了)。

我知道這是一篇舊帖子,但既然我還是來到了這里:有一些 m4 宏可以讓使用 swig 編譯 python 綁定變得非常容易:

http://www.gnu.org/software/autoconf-archive/ax_pkg_swig.htmlhttp://www.gnu.org/software/autoconf-archive/ax_swig_python.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM