“Python/docs/3.9/extending/windows”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:5. 在 Windows 上构建 C 和 C++ 扩展 — Python 文档}} | ||
<div id="building-c-and-c-extensions-on-windows" class="section"> | <div id="building-c-and-c-extensions-on-windows" class="section"> | ||
<span id="building-on-windows"></span> | <span id="building-on-windows"></span> | ||
− | = | + | = 5. 在 Windows 上构建 C 和 C++ 扩展 = |
− | + | 本章简要说明如何使用 Microsoft Visual C++ 为 Python 创建 Windows 扩展模块,然后提供有关其工作原理的更详细的背景信息。 说明材料对学习构建 Python 扩展的 Windows 程序员和有兴趣开发可以在 Unix 和 Windows 上成功构建的软件的 Unix 程序员都很有用。 | |
− | |||
− | |||
− | Windows | ||
− | |||
− | |||
− | + | 鼓励模块作者使用 distutils 方法来构建扩展模块,而不是本节中描述的方法。 您仍然需要用于构建 Python 的 C 编译器; 通常是 Microsoft Visual C++。 | |
− | |||
− | |||
− | C++ | ||
<div class="admonition note"> | <div class="admonition note"> | ||
− | + | 笔记 | |
− | + | 本章提到了许多包含编码 Python 版本号的文件名。 这些文件名用显示为 <code>XY</code> 的版本号表示; 实际上,<code>'X'</code> 将是主要版本号,而 <code>'Y'</code> 将是您正在使用的 Python 版本的次要版本号。 例如,如果您使用 Python 2.2.1,<code>XY</code> 实际上将是 <code>22</code>。 | |
− | |||
− | |||
− | |||
− | |||
第31行: | 第20行: | ||
<span id="win-cookbook"></span> | <span id="win-cookbook"></span> | ||
− | == | + | == 5.1. 食谱方法 == |
− | + | 有两种方法可以在 Windows 上构建扩展模块,就像在 Unix 上一样:使用 [[../../library/distutils#module-distutils|distutils]] 包来控制构建过程,或者手动进行。 distutils 方法适用于大多数扩展; 关于使用 [[../../library/distutils#module-distutils|distutils]] 构建和打包扩展模块的文档可在 [[../../distutils/index#distutils-index|Distributing Python Modules (Legacy version)]] 中找到。 如果你发现你真的需要手动做事,研究项目文件可能对你有帮助。 [[#id1|:source:`winsound `]] 标准库模块。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | [ | ||
第46行: | 第29行: | ||
<span id="dynamic-linking"></span> | <span id="dynamic-linking"></span> | ||
− | == | + | == 5.2. Unix 和 Windows 之间的差异 == |
− | Unix | + | Unix 和 Windows 使用完全不同的范例来运行时加载代码。 在尝试构建可以动态加载的模块之前,请注意您的系统是如何工作的。 |
− | |||
− | |||
− | + | 在 Unix 中,共享对象 (<code>.so</code>) 文件包含程序要使用的代码,以及它希望在程序中找到的函数和数据的名称。 当文件加入程序时,文件代码中对这些函数和数据的所有引用都将更改为指向程序中函数和数据在内存中放置的实际位置。 这基本上是一个链接操作。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 在 Windows 中,动态链接库 (<code>.dll</code>) 文件没有悬空引用。 相反,对函数或数据的访问通过查找表。 因此不必在运行时修复 DLL 代码以引用程序的内存; 相反,代码已经使用了 DLL 的查找表,并且在运行时修改了查找表以指向函数和数据。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 在 Unix 中,只有一种类型的库文件 (<code>.a</code>) 包含来自多个目标文件 (<code>.o</code>) 的代码。 在创建共享对象文件 (<code>.so</code>) 的链接步骤期间,链接器可能会发现它不知道在哪里定义了标识符。 链接器将在库的目标文件中寻找它; 如果找到它,它将包含该目标文件中的所有代码。 | |
− | |||
− | |||
− | |||
− | |||
− | + | 在 Windows 中,有两种类型的库,静态库和导入库(均称为 <code>.lib</code>)。 一个静态库就像一个 Unix <code>.a</code> 文件; 它包含必要时包含的代码。 导入库基本上仅用于向链接器保证某个标识符是合法的,并且会在加载 DLL 时出现在程序中。 因此,链接器使用来自导入库的信息来构建查找表,以使用未包含在 DLL 中的标识符。 链接应用程序或 DLL 时,可能会生成一个导入库,该库将需要用于依赖于应用程序或 DLL 中的符号的所有未来 DLL。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 假设您正在构建两个动态加载模块 B 和 C,它们应该共享另一个代码块 A。 在 Unix 上,您将 ''不'' 将 <code>A.a</code> 传递给 <code>B.so</code> 和 <code>C.so</code> 的链接器; 这将导致它被包含两次,因此 B 和 C 将各自拥有自己的副本。 在 Windows 中,构建 <code>A.dll</code> 也会构建 <code>A.lib</code>。 您 ''do'' 将 <code>A.lib</code> 传递给 B 和 C 的链接器。 <code>A.lib</code> 不包含代码; 它只包含将在运行时用于访问 A 代码的信息。 | |
− | |||
− | |||
− | |||
− | <code>A.dll</code> | ||
− | |||
− | |||
− | + | 在 Windows 中,使用导入库有点像使用 <code>import spam</code>; 它使您可以访问垃圾邮件的名称,但不会创建单独的副本。 在 Unix 上,链接库更像是 <code>from spam import *</code>; 它确实创建了一个单独的副本。 | |
− | |||
− | |||
− | |||
第99行: | 第50行: | ||
<span id="win-dlls"></span> | <span id="win-dlls"></span> | ||
− | == | + | == 5.3. 在实践中使用 DLL == |
− | Windows Python | + | Windows Python 是用 Microsoft Visual C++ 构建的; 使用其他编译器可能会也可能不会(尽管 Borland 似乎可以)。 本节的其余部分是 MSVC++ 特定的。 |
− | |||
− | |||
− | + | 在 Windows 中创建 DLL 时,必须将 <code>pythonXY.lib</code> 传递给链接器。 要构建两个 DLL,垃圾邮件和 ni(使用垃圾邮件中的 C 函数),您可以使用以下命令: | |
− | |||
− | |||
<div class="highlight-c notranslate"> | <div class="highlight-c notranslate"> | ||
第113行: | 第60行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="c">cl /LD /I/python/include spam.c ../libs/pythonXY.lib |
− | cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib</ | + | cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 第一个命令创建了三个文件:<code>spam.obj</code>、<code>spam.dll</code> 和 <code>spam.lib</code>。 <code>Spam.dll</code> 不包含任何 Python 函数(例如 [[../../c-api/arg#c|PyArg_ParseTuple()]]),但由于 <code>pythonXY.lib</code>,它确实知道如何找到 Python 代码。 | |
− | <code>spam.lib</code> | ||
− | |||
− | |||
− | + | 第二个命令创建了 <code>ni.dll</code>(以及 <code>.obj</code> 和 <code>.lib</code>),它知道如何从垃圾邮件以及 Python 可执行文件中找到必要的函数。 | |
− | |||
− | Python | ||
− | + | 并非每个标识符都导出到查找表。 如果您希望任何其他模块(包括 Python)能够看到您的标识符,您必须说 <code>_declspec(dllexport)</code>,如 <code>void _declspec(dllexport) initspam(void)</code> 或 <code>PyObject _declspec(dllexport) *NiGetSpamData(void)</code>。 | |
− | |||
− | <code>_declspec(dllexport)</code> | ||
− | <code>PyObject _declspec(dllexport) *NiGetSpamData(void)</code> | ||
− | Developer Studio | + | Developer Studio 会放入很多您并不真正需要的导入库,为您的可执行文件增加大约 100K。 要摆脱它们,请使用“项目设置”对话框的“链接”选项卡,指定 ''忽略默认库'' 。 将正确的 <code>msvcrtxx.lib</code> 添加到库列表中。 |
− | |||
− | |||
− | |||
第142行: | 第78行: | ||
</div> | </div> | ||
+ | <div class="clearer"> | ||
− | [[Category:Python 3.9 | + | |
+ | |||
+ | </div> | ||
+ | |||
+ | [[Category:Python 3.9 文档]] |
2021年10月31日 (日) 04:50的最新版本
5. 在 Windows 上构建 C 和 C++ 扩展
本章简要说明如何使用 Microsoft Visual C++ 为 Python 创建 Windows 扩展模块,然后提供有关其工作原理的更详细的背景信息。 说明材料对学习构建 Python 扩展的 Windows 程序员和有兴趣开发可以在 Unix 和 Windows 上成功构建的软件的 Unix 程序员都很有用。
鼓励模块作者使用 distutils 方法来构建扩展模块,而不是本节中描述的方法。 您仍然需要用于构建 Python 的 C 编译器; 通常是 Microsoft Visual C++。
笔记
本章提到了许多包含编码 Python 版本号的文件名。 这些文件名用显示为 XY
的版本号表示; 实际上,'X'
将是主要版本号,而 'Y'
将是您正在使用的 Python 版本的次要版本号。 例如,如果您使用 Python 2.2.1,XY
实际上将是 22
。
5.1. 食谱方法
有两种方法可以在 Windows 上构建扩展模块,就像在 Unix 上一样:使用 distutils 包来控制构建过程,或者手动进行。 distutils 方法适用于大多数扩展; 关于使用 distutils 构建和打包扩展模块的文档可在 Distributing Python Modules (Legacy version) 中找到。 如果你发现你真的需要手动做事,研究项目文件可能对你有帮助。 :source:`winsound ` 标准库模块。
5.2. Unix 和 Windows 之间的差异
Unix 和 Windows 使用完全不同的范例来运行时加载代码。 在尝试构建可以动态加载的模块之前,请注意您的系统是如何工作的。
在 Unix 中,共享对象 (.so
) 文件包含程序要使用的代码,以及它希望在程序中找到的函数和数据的名称。 当文件加入程序时,文件代码中对这些函数和数据的所有引用都将更改为指向程序中函数和数据在内存中放置的实际位置。 这基本上是一个链接操作。
在 Windows 中,动态链接库 (.dll
) 文件没有悬空引用。 相反,对函数或数据的访问通过查找表。 因此不必在运行时修复 DLL 代码以引用程序的内存; 相反,代码已经使用了 DLL 的查找表,并且在运行时修改了查找表以指向函数和数据。
在 Unix 中,只有一种类型的库文件 (.a
) 包含来自多个目标文件 (.o
) 的代码。 在创建共享对象文件 (.so
) 的链接步骤期间,链接器可能会发现它不知道在哪里定义了标识符。 链接器将在库的目标文件中寻找它; 如果找到它,它将包含该目标文件中的所有代码。
在 Windows 中,有两种类型的库,静态库和导入库(均称为 .lib
)。 一个静态库就像一个 Unix .a
文件; 它包含必要时包含的代码。 导入库基本上仅用于向链接器保证某个标识符是合法的,并且会在加载 DLL 时出现在程序中。 因此,链接器使用来自导入库的信息来构建查找表,以使用未包含在 DLL 中的标识符。 链接应用程序或 DLL 时,可能会生成一个导入库,该库将需要用于依赖于应用程序或 DLL 中的符号的所有未来 DLL。
假设您正在构建两个动态加载模块 B 和 C,它们应该共享另一个代码块 A。 在 Unix 上,您将 不 将 A.a
传递给 B.so
和 C.so
的链接器; 这将导致它被包含两次,因此 B 和 C 将各自拥有自己的副本。 在 Windows 中,构建 A.dll
也会构建 A.lib
。 您 do 将 A.lib
传递给 B 和 C 的链接器。 A.lib
不包含代码; 它只包含将在运行时用于访问 A 代码的信息。
在 Windows 中,使用导入库有点像使用 import spam
; 它使您可以访问垃圾邮件的名称,但不会创建单独的副本。 在 Unix 上,链接库更像是 from spam import *
; 它确实创建了一个单独的副本。
5.3. 在实践中使用 DLL
Windows Python 是用 Microsoft Visual C++ 构建的; 使用其他编译器可能会也可能不会(尽管 Borland 似乎可以)。 本节的其余部分是 MSVC++ 特定的。
在 Windows 中创建 DLL 时,必须将 pythonXY.lib
传递给链接器。 要构建两个 DLL,垃圾邮件和 ni(使用垃圾邮件中的 C 函数),您可以使用以下命令:
cl /LD /I/python/include spam.c ../libs/pythonXY.lib
cl /LD /I/python/include ni.c spam.lib ../libs/pythonXY.lib
第一个命令创建了三个文件:spam.obj
、spam.dll
和 spam.lib
。 Spam.dll
不包含任何 Python 函数(例如 PyArg_ParseTuple()),但由于 pythonXY.lib
,它确实知道如何找到 Python 代码。
第二个命令创建了 ni.dll
(以及 .obj
和 .lib
),它知道如何从垃圾邮件以及 Python 可执行文件中找到必要的函数。
并非每个标识符都导出到查找表。 如果您希望任何其他模块(包括 Python)能够看到您的标识符,您必须说 _declspec(dllexport)
,如 void _declspec(dllexport) initspam(void)
或 PyObject _declspec(dllexport) *NiGetSpamData(void)
。
Developer Studio 会放入很多您并不真正需要的导入库,为您的可执行文件增加大约 100K。 要摆脱它们,请使用“项目设置”对话框的“链接”选项卡,指定 忽略默认库 。 将正确的 msvcrtxx.lib
添加到库列表中。