如何在 windows/visual studio 上使用 waf 构建 python 扩展模块?

问题描述

我想弄清楚如何使用 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"

ma​​in.cpp

#include <iostream>

#include "foo.h"

using namespace std;
int main() {
  Foo foo;
  cout << foo.a() << endl;
  foo.a() = 10;
  cout << foo.a() << endl;
}

ma​​in.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 Studio\2019\Community\VC\Tools\MSVC\14.26.28801\bin\HostX86\x86\CL.exe
Checking for program 'CL'                : C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\bin\HostX86\x86\CL.exe
Checking for program 'LINK'              : C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\bin\HostX86\x86\LINK.exe
Checking for program 'LIB'               : C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\bin\HostX86\x86\LIB.exe
Checking for program 'MT'                : C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x86\MT.exe
Checking for program 'RC'                : C:\Program Files (x86)\Windows Kits\10\bin\10.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",)

    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 的搜索路径上(使用 PYTHONPATHsys.path.append),否则您可能会遇到诸如:

  • ModuleNotFoundError:没有名为“_foo”的模块
  • 导入错误:导入 _foo 时 DLL 加载失败:参数不正确。

当我尝试直接从主目录运行您的 main.py 并通过在 {{1} 中的 sys.path.append 调用中使用绝对(而不是相对)路径解决它时,我遇到了第二个错误}}。

编辑
我使用的是 python 3.8.5 和 Visual Studio 2017
以下是完整的 wscript。
您需要更改 CXXFLAGS、LDFLAGS 中引用的 Python 头文件和库的路径(尽管我认为有更好的配置方法)

main.py

编辑 2 最初我收到了评论中提到的错误,但现在我发现我无意中通过将 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",) def run(bld): root_path = Path(bld.path.abspath()) subprocess.run(str(root_path / "build/test.exe")) 中的模块声明从文件顶部移到底部来解决 bug that you've reported

foo.i

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...