欢迎访问宙启技术站
智能推送

distutils.command.build_ext.build_ext的源码分析与逻辑解读

发布时间:2024-01-20 12:45:59

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时,可以自定义扩展模块类和构建命令类,以适应不同的构建需求。