5. 在Windows平台编译C和C++扩展

    鼓励模块作者使用 distutils 方式来构建扩展模块,而不使用本节所描述的方式。 你仍将需要使用 C 编译器来构建 Python;通常为 Microsoft Visual C++。

    注解

    这一章提及了多个包括已编码 Python 版本号的文件名。 这些文件名以显示为 的版本号来代表;在实践中,'X' 将为你所使用的 Python 发布版的主版本号而 'Y' 将为次版本号。 例如,如果你所使用的是 Python 2.2.1,XY 将为 22

    在 Windows 和 Unix 上构建扩展模块都有两种方式:使用 distutils 包来控制构建过程,或者全手动操作。 distutils 方式适用于大多数扩展;使用 构建和打包扩展模块的文档见 分发 Python 模块(遗留版本)。 如果你发现你确实需要手动操作,那么研究一下 标准库模块的项目文件可能会很有帮助。

    Unix 和 Windows 对于代码的运行时加载使用了完全不同的范式。 在你尝试构建可动态加载的模块之前,要先了解你所用系统是如何工作的。

    在 Windows 中,一个动态链接库 (.dll) 文件中没有悬挂的引用。 而是通过一个查找表执行对函数或数据的访问。 因此在运行时 DLL 代码不必在运行时进行修改;相反地,代码已经使用了 DLL 的查找表,并且在运行时查找表会被修改以指向特定的函数和数据。

    在 Unix 中,只存在一种库文件 (.a),它包含来自多个对象文件 (.o) 的代码。 在创建共享对象文件 (.so) 的链接阶段,链接器可能会发现它不知道某个标识符是在哪里定义的。 链接器将在各个库的对象文件中查找它;如果找到了它,链接器将会包括来自该对象文件的所有代码。

    在 Windows 中,存在两种库类型,静态库和导入库 (扩展名都是 .lib)。 静态库类似于 Unix 的 文件;它包含在必要时可被包括的代码。 导入库基本上仅用于让链接器能确保特定标识符是合法的,并且将在 DLL 被加载时出现于程序中。 这样链接器可使用来自导入库的信息构建查找表以便使用未包括在 DLL 中的标识符。 当一个应用程序或 DLL 被链接时,可能会生成一个导入库,它将需要被用于应用程序或 DLL 中未来所有依赖于这些符号的 DLL。

    假设你正在编译两个动态加载模块 B 和 C,它们应当共享另一个代码块 A。 在 Unix 上,你 不应A.a 传给链接器作为 B.soC.so;那会导致它被包括两次,这样 B 和 C 将分别拥有它们自己的副本。 在 Windows 上,编译 A.dll 将同时编译 A.lib。 你 应当A.lib 传给链接器用于 B 和 C。 A.lib 并不包含代码;它只包含将在运行时被用于访问 A 的代码的信息。

    在 Windows 上,使用导入库有点像是使用 import spam;它让你可以访问 spam 中的名称,但并不会创建一个单独副本。 在 Unix 上,链接到一个库更像是 from spam import *;它会创建一个单独副本。

    当在 Windows 中创建 DLL 时,你必须将 pythonXY.lib 传给链接器。 要编译两个 DLL,spam 和 ni (会使用 spam 中找到的 C 函数),你应当使用以下命令:

    第一个命令创建了三个文件: , spam.dllspam.libSpam.dll 不包含任何 Python 函数 (例如 PyArg_ParseTuple()),但它通过 pythonXY.lib 可以知道如何找到所需的 Python 代码。

    第二条命令创建了 ni.dll (以及 .obj.lib),它知道如何从 spam 以及 Python 可执行文件中找到所需的函数。

    不是每个标识符都会被导出到查找表。 如果你想要任何其他模块(包括 Python)都能看到你的标识符,你必须写上 _declspec(dllexport),就如在 void _declspec(dllexport) initspam(void)PyObject _declspec(dllexport) *NiGetSpamData(void) 中一样。

    Developer Studio 将加入大量你并不真正需要的导入库,使你的可执行文件大小增加 100K。 要摆脱它们,请使用项目设置对话框的链接选项卡指定 忽略默认库。 将正确的 添加到库列表中。