distutils.command.build_ext.build_ext的源码分析与逻辑解读
distutils是Python中用于构建和分发软件包的标准库。在distutils中,build_ext是一个用于构建Python扩展模块的命令类。build_ext会根据构建环境和配置选项将扩展模块编译为动态链接库,并将其复制到指定位置。
build_ext的源码位于distutils/command/build_ext.py文件中。在源码中,build_ext继承自distutils.cmd.Command类,它覆盖了父类中的一些方法,以实现扩展模块的构建逻辑。
下面是build_ext的源码分析和逻辑解读:
1. 初始化方法
build_ext的初始化方法会调用父类的初始化方法,并设置一些默认属性,如描述命令的名称、描述、用户选项等。
def __init__(self, dist):
# 调用父类的初始化方法
build.__init__(self, dist)
# 设置命令的名称和描述
self.description = "build C/C++ extension modules"
self.build_extensions = None
# 设置用户选项
self.user_options.append(('build-lib=', None,
"directory for compiled C/C++ modules"))
self.user_options.append(('build-temp=', 't',
"directory for temporary files (build by-products)"))
self.user_options.append(('plat-name=', 'p',
"platform name to cross-compile for, if supported "
"(default: %s)" % get_platform()))
self.user_options.append(('inplace', 'i',
"ignore build-lib and put compiled extensions into "
"the source directory alongside your pure Python "
"modules"))
2. finalize_options方法
finalize_options方法会根据用户传入的选项值,设置一些属性的默认值。例如,如果用户没有指定目标路径(build-lib),则将其设置为当前目录下的"build/lib"。
def finalize_options(self):
# 调用父类的finalize_options方法
build.finalize_options(self)
# 设置默认的目标路径
if self.build_lib is None:
# 默认将扩展模块放在 "build/lib" 目录下
self.build_lib = self.get_finalized_command('build').build_lib
3. run方法
run方法是build_ext的主要方法,它会遍历所有的扩展模块,调用build_extension方法编译和构建每个模块。
def run(self):
# 调用父类的run方法
build.run(self)
# 构建并安装扩展模块
for ext in self.extensions:
self.build_extension(ext)
4. build_extension方法
build_extension方法会根据扩展模块的源文件路径、编译选项等信息,调用CCompiler进行编译和链接操作。最终生成的动态链接库会被复制到设定的目标路径(build-lib)。
def build_extension(self, ext):
...
# 获取编译器对象
self.compiler = new_compiler(compiler=self.compiler,
dry_run=self.dry_run,
force=self.force)
# 编译并链接扩展模块
sources = ext.sources
output_dir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name)))
macros = ext.define_macros[:]
include_dirs = ext.include_dirs[:]
extra_objects = ext.extra_objects[:]
extra_compile_args = ext.extra_compile_args[:]
extra_link_args = ext.extra_link_args[:]
# 使用CCompiler进行编译和链接
self.compiler.compile(sources,
output_dir=output_dir,
macros=macros,
include_dirs=include_dirs,
debug=self.debug,
extra_preargs=extra_compile_args,
extra_postargs=extra_link_args,
depends=ext.depends)
self.compiler.link_shared_object(sources,
ext_filename,
output_dir=output_dir,
libraries=self.get_libraries(ext),
library_dirs=ext.library_dirs,
runtime_library_dirs=ext.runtime_library_dirs,
extra_preargs=extra_link_args,
extra_postargs=extra_objects,
export_symbols=self.get_export_symbols(ext),
debug=self.debug)
# 将生成的扩展模块复制到目标路径(build-lib)
if not ext.optional:
self.copy_file(self.get_ext_filename(ext.name), self.get_ext_fullpath(ext.name))
使用build_ext来构建扩展模块的例子:
from distutils.core import setup, Extension
from distutils.command.build_ext import build_ext
# 自定义的扩展模块类
class MyExtension(Extension):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 构建扩展模块的命令类
class MyBuildExt(build_ext):
def build_extensions(self):
# 设置扩展模块的源文件、编译选项等信息
self.extensions = [MyExtension('my_module', sources=['my_module.c'])]
# 调用父类的方法进行构建
build_ext.build_extensions(self)
# 设置setup函数的参数
setup(
# ...
# 其他参数
# ...
# 指定扩展模块的构建命令
cmdclass={'build_ext': MyBuildExt}
)
以上是对distutils.command.build_ext.build_ext源码的分析和逻辑解读。build_ext是distutils库中用于构建Python扩展模块的命令类,通过设置编译选项、调用CCompiler进行编译和链接操作,最终将生成的动态链接库复制到目标路径。使用build_ext时,可以自定义扩展模块类和构建命令类,以适应不同的构建需求。
