我有一堆SWIG接口(foo.i,bar.i等)。我想使用setuptools将它们构建到我的平台(MS Windows)的Python(3.6.4)模块中。该模块应包括SWIG生成的Python文件(* .py),二进制扩展(* .pyd)和编译的缓存(* .pyc)。我的setup.py基本上是这样的:
from setuptools import setup, Extension
from pathlib import Path
paths=list(Path('.').glob('*.i'))
py=[path.stem for path in paths]
ext=[Extension('_' + path.stem, [str(path)]) for path in paths]
setup(py_modules=py, ext_modules=ext)
现在我使用以下步骤构建它:
python setup.py build_ext -I..\include --swig-opts="-I..\include -c++" -b pkg
python setup.py build_py -c -d pkg
echo. > pkg\__init__.py
使用这些步骤,我可以在pkg
目录下获得我想要的内容。
我的问题是:使用setup.py
的单一调用,例如setup.py build
,是否无法获得此效果?我认为构建应该调用build_ext,但是我无法看到如何传递,例如,swig-opts选项。
解决了传递SWIG选项的问题(h / t @hoefling)。解决方案如下所示:
ext=[Extension(name='_' + path.stem,
sources=[str(path)],
swig_opts=['-I../include', '-c++'],
include_dirs=['../include'])
for path in paths]
然而,洋葱剥落了,我现在可以看到下面的图层了:setup.py build
作为单个调用,想要运行{{1首先,然后build_py
。你能明白为什么会失败吗?我没有Python源代码。我的模块中的Python脚本将由SWIG生成,但SWIG在build_ext
步骤之前不会运行。因此,我最终得到了同样的问题,即如何在一次调用中构建模块。
build_ext
完成后,foo.py存在于foo.i旁边的当前工作目录中,并且_foo.cp36-win_amd64.pyd存在于build \ lib.win-amd64-3.6下。 (仅供参考,这个成绩单略有编辑以保护专有信息,例如,我没有显示所有命令的所有回声的完整路径。而且我没有在源中包含bar.i。)
澄清:
错误就是这个:C:\some\path> python setup.py build
running build
running build_py
file foo.py (for module foo) not found
file foo.py (for module foo) not found
running build_ext
building '_foo' extension
swigging foo.i to foo_wrap.cpp
swig.exe -python -I../include -c++ -o foo_wrap.cpp foo.i
creating build
creating build\temp.win-amd64-3.6
creating build\temp.win-amd64-3.6\Release
cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -I../include /EHsc /Tpfoo_wrap.cpp /Fobuild\temp.win-amd64-3.6\Release\foo_wrap.obj foo_wrap.cpp
Creating build\wib.win-amd64-3.6
link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /EXPORT:PyInit__foo build\temp.win-amd64-3.6\Release\foo_wrap.obj /IMPLIB:build\temp.win-amd64-3.6\Release\_foo.cp36-win_amd64.lib
Creating library build\temp.win-amd64-3.6\Release\_foo.cp36-win_amd64.lib and object build\temp.win-amd64-3.6\Release\_foo.cp36-win_amd64.exp
Generating code
Finished generating code
虽然输出中存在所有必需的文件,但除了我可能没有的foo.cp36-win_amd64.pyc之外,我必须手动将foo.py和_foo.cp36-win_amd64.pyd复制到一些新文件中用于制作干净的Python模块的目录。
答案 0 :(得分:1)
由于基本思想(参见@ hoefling的答案)是将构建命令子类化以设置其sub_commands属性,因此这有效:
from setuptools import setup, Extension
from distutils.command.build import build
class build_alt_order(build):
def __init__(self, *args):
super().__init__(*args)
self.sub_commands = [('build_ext', build.has_ext_modules),
('build_py', build.has_pure_modules)]
setup(py_modules=['foo.py', ...],
ext_modules=[Extension('_foo', swig_opts=...), ...],
cmdclass={'build':build_alt_order})
答案 1 :(得分:0)
问题在于,一旦您声明py_modules
,distutils
在build_py
中包含build
作为子命令,并且由于{{1}的排序,它始终首先被执行在sub_commands
类中列出。它根本不知道SWIG将在build
中生成python模块包装器。我想你可以看到这是一个bug,因为我们希望build_ext
能够处理生成的模块,但是meh。
由于您只有SWIG接口并且所有python模块都是生成包装器(因此在您的情况下distutils
不依赖于build_ext
),您可以在{{1}中更改子命令的顺序首先执行build_py
,然后执行其余操作。这将确保在执行build
之前生成python模块。
基本上,您需要覆盖build_ext
命令类并更改build_py
列表中元素的顺序。有很多方法可以在python中重新排序列表;以下是我的建议。为了重新排序列表,我使用了来自itertools
recipes的列表条件分区配方:
build
答案 2 :(得分:0)
有一个演示项目swig-python-demo,其中SWIG集成在setup.py
中。
其中有2个扩展名。
一个(example.c)是由C编写的,另一个是C ++和STL(stl_example.cpp)。
class BuildPy(build_py)
也解决了生成py文件丢失的问题。
EXAMPLE_EXT = Extension(
name='_example',
sources=[
'src/example/example.c',
'src/example/example.i',
],
)
STD_EXT = Extension(
name='_stl_example',
swig_opts=['-c++'],
sources=[
'src/example/stl_example.cpp',
'src/example/stl_example.i',
],
include_dirs=[
'src/example',
],
extra_compile_args=[ # The g++ (4.8) in Travis needs this
'-std=c++11',
]
)
# Build extensions before python modules,
# or the generated SWIG python files will be missing.
class BuildPy(build_py):
def run(self):
self.run_command('build_ext')
super(build_py, self).run()
setup(
name='swig-example-demo',
description='A Python demo for SWIG',
...
ext_modules=[EXAMPLE_EXT, STD_EXT],
cmdclass={
'build_py': BuildPy,
},
...
)