Python 程序
Python 作为一种解释型语言,其编写的代码在执行层面上是与架构无关的,可通过任何硬件架构上的 Python 解释器执行。
导致 Python 语言编写的代码与架构相关的原因主要可归为以下几类:
- 按不同架构调用外部 C/C++ 动态链接库
- 作为获取或生成架构相关配置的工具脚本
按不同架构调用外部 C/C++ 动态链接库
Python 中可以通过 ctypes
、SWIG
及 Python/C API
等方式调用 C/C++ 语言编写的动态链接库中的函数, 这些动态链接库通常需要针对不同的目标架构编译为不同版本。 Python 程序在调用这些动态链接库时需要使用与当前运行环境架构相匹配的版本, 若这些动态链接库尚未支持特定的目标架构,则调用这些库的 Python 代码将无法正常执行。
作为架构相关配置文件的工具脚本
不同软件在构建过程中会使用不同的工具链,其中一些所需参数可能会通过其它语言编写的工具脚本获取。 这些参数可以是:
- 预先定义在工具脚本中的
- 由工具脚本调用一些特有 API 后按条件选取或生成的
由于 Python 语言先天兼容 JSON 格式,一些开发者会将部分参数以结构化 JSON(Python 字典)的形式编写在 Python 文件中, 并加入一些简单的处理逻辑。这些配置中可能存在架构相关的配置,主要可通过架构相关关键字识别。
示例:mozjs78 通过 Python 定义架构相关参数
- 项目名称:mozjs78
- 项目版本:git#f2815665e3500a465a1b2121cef22ebb7290f157
- 相关文件:
/python/mozbuild/mozbuild/configure/constants.py
- 简介:mozjs78 的 mozbuild 模块通过 Python 预定义了一些目标架构的位数及预处理检查条件,并在编译工具链配置文件中进行检查。
在 mozjs78 mozbuild 模块的 /python/mozbuild/mozbuild/configure/constants.py
文件中, 定义了以下与架构相关的变量:
CPU_bitness
:架构名到位数的映射字典,表示不同目标 CPU 架构名称下对应的位数CPU
:架构名列表,表示当前已支持的所有架构名称CPU_preprocessor_checks
:目标架构对应的 C/C++ 宏表达式,用于检查目标架构对应的 C/C++ 源文件中是否存在对应架构的宏条件语句
# Line 42-60
CPU_bitness = {
'aarch64': 64,
'Alpha': 64,
'arm': 32,
'hppa': 32,
'ia64': 64,
'mips32': 32,
'mips64': 64,
'ppc': 32,
'ppc64': 64,
'riscv64': 64, # RISC-V Support
's390': 32,
's390x': 64,
'sh4': 32,
'sparc': 32,
'sparc64': 64,
'x86': 32,
'x86_64': 64,
}
# Line 62
CPU = EnumString.subclass(*CPU_bitness.keys())
# Line 75-93
CPU_preprocessor_checks = OrderedDict((
('x86', '__i386__ || _M_IX86'),
('x86_64', '__x86_64__ || _M_X64'),
('arm', '__arm__ || _M_ARM'),
('aarch64', '__aarch64__ || _M_ARM64'),
('ia64', '__ia64__'),
('s390x', '__s390x__'),
('s390', '__s390__'),
('ppc64', '__powerpc64__'),
('ppc', '__powerpc__'),
('Alpha', '__alpha__'),
('hppa', '__hppa__'),
('sparc64', '__sparc__ && __arch64__'),
('sparc', '__sparc__'),
('mips64', '__mips64'),
('mips32', '__mips__'),
('sh4', '__sh__'),
('riscv64', '__riscv && __riscv_xlen == 64'),
))
# Line 95
assert sorted(CPU_preprocessor_checks.keys()) == sorted(CPU.POSSIBLE_VALUES)
其中,架构相关变量 CPU_preprocessor_checks
在编译配置文件 /build/moz.configure/toolchain.configure
中引入并使用, 用于检查目标架构的工具链中,是否存在该架构对应的宏分支语句。
# Line 378-379
@imports(_from='mozbuild.configure.constants',
_import='CPU_preprocessor_checks')
# Line 426-444
# While we're doing some preprocessing, we might as well do some more
# preprocessor-based tests at the same time, to check the toolchain
# matches what we want.
for name, preprocessor_checks in (
('CPU', CPU_preprocessor_checks),
('KERNEL', kernel_preprocessor_checks),
('OS', OS_preprocessor_checks),
):
for n, (value, condition) in enumerate(iteritems(preprocessor_checks)):
check += dedent('''\
#%(if)s %(condition)s
%%%(name)s "%(value)s"
''' % {
'if': 'elif' if n else 'if',
'condition': condition,
'name': name,
'value': value,
})
check += '#endif\n'
架构相关变量 CPU
和 CPU_bitness
在编译配置文件 /build/moz.configure/init.configure
中引入并使用, 用于合成目标架构对应的编译参数。
# Line 638-639
@imports(_from='mozbuild.configure.constants', _import='CPU')
@imports(_from='mozbuild.configure.constants', _import='CPU_bitness')
# Line 764-775
return namespace(
alias=triplet,
cpu=CPU(canonical_cpu), # Usage of CPU
bitness=CPU_bitness[canonical_cpu], # Usage of CPU_bitness
kernel=Kernel(canonical_kernel),
os=OS(canonical_os),
endianness=Endianness(endianness),
raw_cpu=cpu,
raw_os=os,
toolchain=toolchain,
vendor=vendor,
)
另一方面,Python 的 platform
库提供了方法 platform.machine()
,用于获取当前执行环境的硬件架构。 该方法与 Linux 系统命令 uname -m
的返回值保持一致,因此该 API 返回值的可选值可参见 此表 。工具脚本可通过该 API 获取当前架构名称。
示例:Firefox 通过 Python 获取当前架构
- 项目名称:Firefox
- 项目版本:78.15.0esr
- 相关文件:
/security/nss/coreconf/detect_host_arch.py
- 简介:Firefox 的 NSS 模块在构建脚本中使用 Python 工具脚本获取了当前设备的硬件架构, 并将其作为 Ninja 构建工具的参数。
在 Firefox NSS (Network Security Services) 模块的构建脚本 /security/nss/build.sh
中,使用了 Python 脚本 /security/nss/coreconf/detect_host_arch.py
获取当前硬件架构,并将其作为 Ninja 构建工具的参数。
# Line 72-73
host_arch=$(python "$cwd/coreconf/detect_host_arch.py")
target_arch=$host_arch
# Line 141
gyp_params+=(-Dtarget_arch="$target_arch")
在被调用的 Python 脚本 /security/nss/coreconf/detect_host_arch.py
中, 通过 platform
模块提供的 platform.machine()
方法获取当前设备的硬件架构, 按照公共前缀聚类后打印到标准输出,作为返回值提供给上述构建脚本。
# Line 13-22
host_arch = platform.machine().lower()
if host_arch in ('amd64', 'x86_64'):
host_arch = 'x64'
elif fnmatch.fnmatch(host_arch, 'i?86') or host_arch == 'i86pc':
host_arch = 'ia32'
elif host_arch.startswith('arm'):
host_arch = 'arm'
elif host_arch.startswith('mips'):
host_arch = 'mips'
print(host_arch) # Line 22
若希望让上述代码支持 RISC-V 架构,则应在确认 Ninja 等构建工具链支持 RISC-V 的基础上, 于上述 Python 脚本的第 22 行之前加入以下形式的代码:
elif host_arch.startswith('riscv'):
host_arch = 'riscv'