如何在 windows/visual studio 上使用 waf 构建 python 扩展模块?
How to build python extension module with waf on windows/visual studio?
我正在尝试弄清楚如何使用 waf 构建 python 扩展模块,但我被卡住了。考虑一个简单的树,例如:
foo.h
#pragma once
class Foo {
public:
Foo();
const int &a() const;
int &a();
private:
int m_a;
};
inline const int &Foo::a() const { return m_a; }
inline int &Foo::a() { return m_a; }
foo.cpp
#include "foo.h"
Foo::Foo() : m_a(0) {}
foo.i
%module foo
%{
#include "foo.h"
%}
%include "foo.h"
main.cpp
#include <iostream>
#include "foo.h"
using namespace std;
int main() {
Foo foo;
cout << foo.a() << endl;
foo.a() = 10;
cout << foo.a() << endl;
}
main.py
import sys
sys.path.append('build')
from foo import Foo
foo = Foo()
print foo.a
foo.a = 1
print foo.a
wscript
import subprocess
from pathlib import Path
def configure(conf):
conf.env.MSVC_VERSIONS = ["msvc 15.0"]
conf.env.MSVC_TARGETS = ["x86"]
conf.load("msvc python swig")
conf.check_python_version((3, 6, 8))
# This method is not working properly on windows/virtualenv
# conf.check_python_headers()
# Maybe something like this could help?
# from distutils import sysconfig
# print(sysconfig.get_python_inc())
conf.check_swig_version()
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.shlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
# bld.program(
# source = 'main.cpp',
# target = 'main',
# use = ['FOO'],
# rpath = ['$ORIGIN'],
# )
# bld(
# features = 'cxx cxxshlib pyext',
# source = 'foo.i',
# target = '_foo',
# swig_flags = '-c++ -python -Wall',
# includes = '.',
# use = 'FOO',
# rpath = ['$ORIGIN', ],
# )
def run(bld):
root_path = Path(bld.path.abspath())
subprocess.run(str(root_path / "build/test.exe"))
我在这里已经面临的第一个问题是当我waf distclean configure build
时,我会得到这个错误:
(py382_64) D:\swigtest>waf distclean configure build
'distclean' finished successfully (0.006s)
Setting top to : D:\swigtest
Setting out to : D:\swigtest\build
Checking for program 'CL' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\CL.exe
Checking for program 'CL' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\CL.exe
Checking for program 'LINK' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\LINK.exe
Checking for program 'LIB' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\LIB.exe
Checking for program 'MT' : C:\Program Files (x86)\Windows Kits\bin.0.18362.0\x86\MT.exe
Checking for program 'RC' : C:\Program Files (x86)\Windows Kits\bin.0.18362.0\x86\RC.exe
Checking for program 'python' : d:\virtual_envs\py368_32\scripts\python.exe
Checking for program 'swig' : D:\software\swig\swig.exe
Checking for python version >= 3.6.8 : 3.6.8
Checking for swig version : 4.0.0
'configure' finished successfully (1.538s)
Waf: Entering directory `D:\swigtest\build'
[1/5] Compiling foo.cpp
[2/5] Compiling main.cpp
[3/5] Compiling foo.cpp
foo.cpp
foo.cpp
[4/5] Linking build\foo.dll
main.cpp
[5/5] Linking build\test.exe
Waf: Leaving directory `D:\swigtest\build'
Build failed
-> missing file: 'D:\swigtest\build\foo.lib'
问题:我该如何解决这个小问题?无论如何,我的主要问题是在 windows 上使用 swig 和 waf 生成 python 扩展模块的正确方法是什么?
您可以通过将库类型从共享 (shlib
) 更改为静态 (stlib
) 来修复当前构建,即
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.stlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
要构建 python 扩展模块,您可以使用:
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.stlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
bld(
features = 'cxx cxxshlib pyext',
source = 'foo.i',
target = '_foo.pyd',
swig_flags = '-c++ -python -Wall',
includes = '.',
use = 'FOO',
)
这会将一个 dll 库输出到一个名为 _foo.pyd
的文件和一个 python 脚本 foo.py
.
我并不是说这是正确的方法,但它对我有用。
此外,在使用扩展名(例如 from foo import Foo
)时,请确保这些文件位于 python 的搜索路径中(使用 PYTHONPATH
或 sys.path.append
) ,否则你可能会得到如下错误:
- ModuleNotFoundError: 没有名为“_foo”的模块
- ImportError:导入 _foo 时 DLL 加载失败:参数不正确。
当我直接从主目录尝试 运行 你的 main.py
并通过在 [=20= 中使用绝对(而不是相对)路径解决它时,我遇到了第二个错误] 调用 main.py
.
编辑
我使用的是 python 3.8.5 和 Visual Studio 2017
以下是完整的 wscript。
您需要将路径更改为 Python headers 和 CXXFLAGS、LDFLAGS 中引用的库(尽管我想有更好的配置方法)
import subprocess
from pathlib import Path
def configure(conf):
conf.env.MSVC_VERSIONS = ["msvc 15.9"]
conf.env.MSVC_TARGETS = ["x64"]
conf.env.append_value('CXXFLAGS', ["/Id:\tools\Python38\include"])
conf.env.append_value('LDFLAGS', ["/LIBPATH:d:\tools\Python38\libs"])
conf.load("msvc python swig")
conf.check_python_version((3, 6, 8))
# This method is not working properly on windows/virtualenv
#conf.check_python_headers()
# Maybe something like this could help?
# from distutils import sysconfig
# print(sysconfig.get_python_inc())
conf.check_swig_version()
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.stlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
bld(
features = 'cxx cxxshlib pyext',
source = 'foo.i',
target = '_foo.pyd',
swig_flags = '-c++ -python -Wall',
includes = '.',
use = 'FOO',
)
def run(bld):
root_path = Path(bld.path.abspath())
subprocess.run(str(root_path / "build/test.exe"))
编辑 2
最初我收到评论中提到的错误,但现在我发现我通过将 foo.i
中的模块声明从文件顶部移动到文件底部而无意中解决了 bug that you've reported。
%{
#include "foo.h"
%}
%include "foo.h"
%module foo
我正在尝试弄清楚如何使用 waf 构建 python 扩展模块,但我被卡住了。考虑一个简单的树,例如:
foo.h
#pragma once
class Foo {
public:
Foo();
const int &a() const;
int &a();
private:
int m_a;
};
inline const int &Foo::a() const { return m_a; }
inline int &Foo::a() { return m_a; }
foo.cpp
#include "foo.h"
Foo::Foo() : m_a(0) {}
foo.i
%module foo
%{
#include "foo.h"
%}
%include "foo.h"
main.cpp
#include <iostream>
#include "foo.h"
using namespace std;
int main() {
Foo foo;
cout << foo.a() << endl;
foo.a() = 10;
cout << foo.a() << endl;
}
main.py
import sys
sys.path.append('build')
from foo import Foo
foo = Foo()
print foo.a
foo.a = 1
print foo.a
wscript
import subprocess
from pathlib import Path
def configure(conf):
conf.env.MSVC_VERSIONS = ["msvc 15.0"]
conf.env.MSVC_TARGETS = ["x86"]
conf.load("msvc python swig")
conf.check_python_version((3, 6, 8))
# This method is not working properly on windows/virtualenv
# conf.check_python_headers()
# Maybe something like this could help?
# from distutils import sysconfig
# print(sysconfig.get_python_inc())
conf.check_swig_version()
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.shlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
# bld.program(
# source = 'main.cpp',
# target = 'main',
# use = ['FOO'],
# rpath = ['$ORIGIN'],
# )
# bld(
# features = 'cxx cxxshlib pyext',
# source = 'foo.i',
# target = '_foo',
# swig_flags = '-c++ -python -Wall',
# includes = '.',
# use = 'FOO',
# rpath = ['$ORIGIN', ],
# )
def run(bld):
root_path = Path(bld.path.abspath())
subprocess.run(str(root_path / "build/test.exe"))
我在这里已经面临的第一个问题是当我waf distclean configure build
时,我会得到这个错误:
(py382_64) D:\swigtest>waf distclean configure build
'distclean' finished successfully (0.006s)
Setting top to : D:\swigtest
Setting out to : D:\swigtest\build
Checking for program 'CL' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\CL.exe
Checking for program 'CL' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\CL.exe
Checking for program 'LINK' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\LINK.exe
Checking for program 'LIB' : C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.26.28801\bin\HostX86\x86\LIB.exe
Checking for program 'MT' : C:\Program Files (x86)\Windows Kits\bin.0.18362.0\x86\MT.exe
Checking for program 'RC' : C:\Program Files (x86)\Windows Kits\bin.0.18362.0\x86\RC.exe
Checking for program 'python' : d:\virtual_envs\py368_32\scripts\python.exe
Checking for program 'swig' : D:\software\swig\swig.exe
Checking for python version >= 3.6.8 : 3.6.8
Checking for swig version : 4.0.0
'configure' finished successfully (1.538s)
Waf: Entering directory `D:\swigtest\build'
[1/5] Compiling foo.cpp
[2/5] Compiling main.cpp
[3/5] Compiling foo.cpp
foo.cpp
foo.cpp
[4/5] Linking build\foo.dll
main.cpp
[5/5] Linking build\test.exe
Waf: Leaving directory `D:\swigtest\build'
Build failed
-> missing file: 'D:\swigtest\build\foo.lib'
问题:我该如何解决这个小问题?无论如何,我的主要问题是在 windows 上使用 swig 和 waf 生成 python 扩展模块的正确方法是什么?
您可以通过将库类型从共享 (shlib
) 更改为静态 (stlib
) 来修复当前构建,即
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.stlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
要构建 python 扩展模块,您可以使用:
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.stlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
bld(
features = 'cxx cxxshlib pyext',
source = 'foo.i',
target = '_foo.pyd',
swig_flags = '-c++ -python -Wall',
includes = '.',
use = 'FOO',
)
这会将一个 dll 库输出到一个名为 _foo.pyd
的文件和一个 python 脚本 foo.py
.
我并不是说这是正确的方法,但它对我有用。
此外,在使用扩展名(例如 from foo import Foo
)时,请确保这些文件位于 python 的搜索路径中(使用 PYTHONPATH
或 sys.path.append
) ,否则你可能会得到如下错误:
- ModuleNotFoundError: 没有名为“_foo”的模块
- ImportError:导入 _foo 时 DLL 加载失败:参数不正确。
当我直接从主目录尝试 运行 你的 main.py
并通过在 [=20= 中使用绝对(而不是相对)路径解决它时,我遇到了第二个错误] 调用 main.py
.
编辑
我使用的是 python 3.8.5 和 Visual Studio 2017
以下是完整的 wscript。
您需要将路径更改为 Python headers 和 CXXFLAGS、LDFLAGS 中引用的库(尽管我想有更好的配置方法)
import subprocess
from pathlib import Path
def configure(conf):
conf.env.MSVC_VERSIONS = ["msvc 15.9"]
conf.env.MSVC_TARGETS = ["x64"]
conf.env.append_value('CXXFLAGS', ["/Id:\tools\Python38\include"])
conf.env.append_value('LDFLAGS', ["/LIBPATH:d:\tools\Python38\libs"])
conf.load("msvc python swig")
conf.check_python_version((3, 6, 8))
# This method is not working properly on windows/virtualenv
#conf.check_python_headers()
# Maybe something like this could help?
# from distutils import sysconfig
# print(sysconfig.get_python_inc())
conf.check_swig_version()
def build(bld):
bld.program(source=["main.cpp", "foo.cpp"], cxxflags=["/EHsc"], target="test")
bld.stlib(
features="cxx",
source="foo.cpp",
target="foo",
includes=".",
export_includes=".",
name="FOO",
)
bld(
features = 'cxx cxxshlib pyext',
source = 'foo.i',
target = '_foo.pyd',
swig_flags = '-c++ -python -Wall',
includes = '.',
use = 'FOO',
)
def run(bld):
root_path = Path(bld.path.abspath())
subprocess.run(str(root_path / "build/test.exe"))
编辑 2
最初我收到评论中提到的错误,但现在我发现我通过将 foo.i
中的模块声明从文件顶部移动到文件底部而无意中解决了 bug that you've reported。
%{
#include "foo.h"
%}
%include "foo.h"
%module foo