15_apollo_tools_platform子模块软件架构分析

7 阅读9分钟

15_apollo_tools_platform子模块软件架构分析

1. 概述

  apollo_tools_platform子模块是Apollo构建系统的平台配置工具,负责管理跨平台构建的条件编译和平台特定的配置选项。该模块通过build_defs.bzl和common.bzl文件提供了丰富的平台抽象函数,支持GPU、CPU架构、CAN总线、远程控制等特性的条件编译,为Apollo系统在多种硬件平台上的构建提供了统一的接口。

2. 软件架构图

graph TB
    subgraph "Bazel构建系统"
        B1[Bazel工作空间]
        B2[配置系统]
        B3[条件编译器]
    end

    subgraph "Platform模块"
        P1[build_defs.bzl]
        P2[common.bzl]
        P3[平台抽象层]
        P4[工具链管理器]
    end

    subgraph "平台特性"
        F1[GPU支持]
        F2[CPU架构]
        F3[CAN总线]
        F4[远程控制]
        F5[性能分析]
    end

    subgraph "工具链"
        T1[Python工具链]
        T2[Bash工具链]
        T3[编译器工具链]
    end

    subgraph "构建目标"
        G1[x86_64目标]
        G2[aarch64目标]
        G3[GPU目标]
        G4[CPU目标]
    end

    B1 --> B2
    B2 --> P1
    B2 --> P2
    P1 --> P3
    P2 --> P4
    P3 --> F1
    P3 --> F2
    P3 --> F3
    P3 --> F4
    P3 --> F5
    P4 --> T1
    P4 --> T2
    P4 --> T3
    B3 --> G1
    B3 --> G2
    B3 --> G3
    B3 --> G4

    style P1 fill:#e1f5fe
    style P2 fill:#f3e5f5
    style P3 fill:#fff3e0

3. 调用流程图

sequenceDiagram
    participant Build as 构建系统
    participant Platform as Platform模块
    participant BuildDefs as build_defs.bzl
    participant Common as common.bzl
    participant Config as 配置系统
    participant Toolchain as 工具链

    Build->>Platform: 初始化平台配置
    Platform->>BuildDefs: 加载build_defs.bzl
    BuildDefs->>Config: 查询平台配置
    Config-->>BuildDefs: 返回配置值
    BuildDefs->>BuildDefs: 生成条件编译选项
    BuildDefs-->>Platform: 返回平台抽象函数
    Platform->>Common: 加载common.bzl
    Common->>Toolchain: 查询工具链路径
    Toolchain-->>Common: 返回工具链路径
    Common->>Common: 执行工具链检查
    Common-->>Platform: 返回工具链管理器
    Platform-->>Build: 平台配置完成
    Build->>BuildDefs: 应用平台条件
    BuildDefs->>Config: 评估select语句
    Config-->>BuildDefs: 返回选中的值
    BuildDefs-->>Build: 条件编译完成

4. UML类图

4.1 平台抽象类图

classDiagram
    class PlatformConfig {
        +use_gpu: bool
        +with_teleop: bool
        +use_esd_can: bool
        +enable_profiler: bool
        +cpu_arch: string
        +getGpuConfig(): bool
        +getTeleopConfig(): bool
        +getCanConfig(): bool
        +getProfilerConfig(): bool
        -configMap: map
    }

    class PlatformAbstraction {
        +if_gpu(if_true, if_false): list
        +if_teleop(if_true, if_false): list
        +if_x86_64(if_true, if_false): list
        +if_aarch64(if_true, if_false): list
        +if_esd_can(if_true, if_false): list
        +if_profiler(): list
        +copts_if_gpu(): list
        +copts_if_teleop(): list
        +copts_if_esd_can(): list
    }

    class ToolchainManager {
        +python_bin: string
        +bash_bin: string
        +getPythonBin(): string
        +getBashBin(): string
        +findTool(tool_name): string
        -toolPaths: map
    }

    class ConfigResolver {
        +resolve(config_name): string
        +select(conditions): any
        +validate(config): bool
        -configCache: map
    }

    class PlatformDetector {
        +detectCPU(): string
        +detectGPU(): bool
        +detectFeatures(): list
        -cpuInfo: string
        -gpuInfo: string
    }

    PlatformConfig --> PlatformAbstraction
    PlatformAbstraction --> ConfigResolver
    PlatformAbstraction --> ToolchainManager
    ToolchainManager --> PlatformDetector

4.2 工具链管理类图

classDiagram
    class ToolchainLocator {
        +which(repository_ctx, program_name): string
        +getPythonBin(repository_ctx): string
        +getBashBin(repository_ctx): string
        +getEnviron(repository_ctx, name): string
        -path: string
    }

    class ToolchainValidator {
        +validatePython(path): bool
        +validateBash(path): bool
        +validateCompiler(path): bool
        +checkVersion(path, min_version): bool
    }

    class ToolchainConfigurator {
        +configurePython(repository_ctx)
        +configureBash(repository_ctx)
        +configureCompiler(repository_ctx)
        -config: map
    }

    class FileOperator {
        +readDir(repository_ctx, src_dir): list
        +filesExist(repository_ctx, paths): list
        +realpath(repository_ctx, path): string
        +copyFiles(src, dst): bool
    }

    class TemplateProcessor {
        +loadTemplate(template): string
        +expandTemplate(content, substitutions): string
        +saveTemplate(content, output): bool
        -templates: map
    }

    ToolchainLocator --> ToolchainValidator
    ToolchainLocator --> ToolchainConfigurator
    ToolchainConfigurator --> FileOperator
    ToolchainConfigurator --> TemplateProcessor

4.3 条件编译类图

classDiagram
    class ConditionalCompiler {
        +conditions: map
        +evaluate(condition): bool
        +select(conditions): any
        +apply(target, config): list
        -evaluated: map
    }

    class GPUCondition {
        +enabled: bool
        +evaluate(): bool
        +getCopts(): list
        +getLinkopts(): list
    }

    class CPUCondition {
        +arch: string
        +evaluate(): bool
        +getCopts(): list
        +getLinkopts(): list
    }

    class FeatureCondition {
        +feature: string
        +enabled: bool
        +evaluate(): bool
        +getCopts(): list
    }

    class SelectStatement {
        +conditions: map
        +default: any
        +evaluate(): any
        -context: Context
    }

    ConditionalCompiler --> GPUCondition
    ConditionalCompiler --> CPUCondition
    ConditionalCompiler --> FeatureCondition
    ConditionalCompiler --> SelectStatement

5. 状态机分析

5.1 平台配置状态机

stateDiagram-v2
    [*] --> UNCONFIGURED: 模块加载
    UNCONFIGURED --> DETECTING_PLATFORM: 开始检测平台
    DETECTING_PLATFORM --> LOADING_CONFIG: 平台检测完成
    LOADING_CONFIG --> VALIDATING_CONFIG: 配置加载完成
    VALIDATING_CONFIG --> READY: 配置验证通过
    VALIDATING_CONFIG --> ERROR: 配置验证失败
    READY --> APPLYING_CONFIG: 开始应用配置
    APPLYING_CONFIG --> COMPLETED: 配置应用完成
    ERROR --> RECOVERING: 开始恢复
    RECOVERING --> READY: 恢复成功
    RECOVERING --> FATAL: 恢复失败
    FATAL --> [*]: 致命错误
    COMPLETED --> [*]: 配置完成

    note right of DETECTING_PLATFORM : 检测CPU架构和GPU
    note right of LOADING_CONFIG : 加载平台配置文件
    note right of VALIDATING_CONFIG : 验证配置有效性
    note right of APPLYING_CONFIG : 应用条件编译选项

5.2 工具链检测状态机

stateDiagram-v2
    [*] --> IDLE: 检测开始
    IDLE --> CHECKING_PYTHON: 检查Python
    CHECKING_PYTHON --> CHECKING_BASH: Python检查完成
    CHECKING_BASH --> CHECKING_COMPILER: Bash检查完成
    CHECKING_COMPILER --> VALIDATING_VERSIONS: 编译器检查完成
    VALIDATING_VERSIONS --> READY: 版本验证通过
    VALIDATING_VERSIONS --> ERROR: 版本验证失败
    READY --> CONFIGURING: 开始配置
    CONFIGURING --> COMPLETED: 配置完成
    ERROR --> [*]: 检测失败
    COMPLETED --> [*]: 检测完成

    note right of CHECKING_PYTHON : 查找Python解释器
    note right of CHECKING_BASH : 查找Bash解释器
    note right of CHECKING_COMPILER : 查找C++编译器
    note right of VALIDATING_VERSIONS : 验证工具链版本

5.3 条件编译状态机

stateDiagram-v2
    [*] --> IDLE: 编译开始
    IDLE --> EVALUATING_CONDITIONS: 评估条件
    EVALUATING_CONDITIONS --> SELECTING_TARGETS: 条件评估完成
    SELECTING_TARGETS --> GENERATING_COPTS: 目标选择完成
    GENERATING_COPTS --> GENERATING_LOPTS: 编译选项生成完成
    GENERATING_LOPTS --> COMPILING: 链接选项生成完成
    COMPILING --> COMPLETED: 编译完成
    COMPLETED --> [*]: 编译成功

    note right of EVALUATING_CONDITIONS : 评估GPU、CPU等条件
    note right of SELECTING_TARGETS : 选择编译目标
    note right of GENERATING_COPTS : 生成编译选项
    note right of GENERATING_LOPTS : 生成链接选项

5.4 配置解析状态机

stateDiagram-v2
    [*] --> IDLE: 解析开始
    IDLE --> PARSING_CONFIG: 解析配置文件
    PARSING_CONFIG --> RESOLVING_VARIABLES: 配置解析完成
    RESOLVING_VARIABLES --> EXPANDING_TEMPLATES: 变量解析完成
    EXPANDING_TEMPLATES --> VALIDATING: 模板展开完成
    VALIDATING --> READY: 验证通过
    VALIDATING --> ERROR: 验证失败
    READY --> CACHING: 开始缓存
    CACHING --> COMPLETED: 缓存完成
    ERROR --> [*]: 解析失败
    COMPLETED --> [*]: 解析完成

    note right of PARSING_CONFIG : 解析.bzl配置文件
    note right of RESOLVING_VARIABLES : 解析环境变量
    note right of EXPANDING_TEMPLATES : 展开模板变量
    note right of VALIDATING : 验证配置有效性

6. 源码分析

6.1 build_defs.bzl文件分析

6.1.1 文件结构概述

  build_defs.bzl文件是Apollo平台配置的核心文件,定义了多个平台抽象函数,用于处理不同平台和特性的条件编译。

def if_gpu(if_true, if_false = []):
    """Shorthand for select()'ing on whether we're building with gpu enabled"""
    return select({
        "//tools/platform:use_gpu": if_true,
        "//conditions:default": if_false,
    })
6.1.2 if_gpu函数分析
def if_gpu(if_true, if_false = []):
    """Shorthand for select()'ing on whether we're building with gpu enabled
    Returns a select statement which evaluates to if_true if we're building
    with use_gpu enabled. Otherwise, the select statement evaluates to
    if_false.
    """
    return select({
        "//tools/platform:use_gpu": if_true,
        "//conditions:default": if_false,
    })

  该函数提供了一个简化的接口来处理GPU相关的条件编译。它使用Bazel的select语句,根据//tools/platform:use_gpu配置的值来选择返回if_trueif_false

  使用示例:

cc_library(
    name = "gpu_module",
    srcs = if_gpu(["gpu.cc"], ["cpu.cc"]),
    copts = if_gpu(["-DUSE_GPU=1"], ["-DUSE_GPU=0"]),
)
6.1.3 copts_if_gpu函数分析
def copts_if_gpu():
    return if_gpu(["-DUSE_GPU=1"], ["-DUSE_GPU=0"])

  该函数是if_gpu的特化版本,专门用于生成编译选项。它返回一个列表,包含-DUSE_GPU=1(启用GPU)或-DUSE_GPU=0(禁用GPU)。

  这种设计使得在cc_library规则中可以更简洁地使用:

cc_library(
    name = "module",
    copts = copts_if_gpu(),
)
6.1.4 if_teleop函数分析
def if_teleop(if_true, if_false = []):
    return select({
        "//tools/platform:with_teleop": if_true,
        "//conditions:default": if_false,
    })

  该函数处理远程控制(teleoperation)相关的条件编译。远程控制功能允许通过远程接口控制车辆,这对于测试和调试非常有用。

6.1.5 copts_if_teleop函数分析
def copts_if_teleop():
    return if_teleop(["-DWITH_TELEOP=1"], ["-DWITH_TELEOP=0"])

  该函数生成远程控制相关的编译选项。

6.1.6 if_x86_64函数分析
def if_x86_64(if_true, if_false = []):
    return select({
        "@platforms//cpu:x86_64": if_true,
        "//conditions:default": if_false,
    })

  该函数处理x86_64 CPU架构的条件编译。x86_64是Apollo在开发和测试中最常用的CPU架构。

6.1.7 if_aarch64函数分析
def if_aarch64(if_true, if_false = []):
    return select({
        "@platforms//cpu:aarch64": if_true,
        "//conditions:default": if_false,
    })

  该函数处理ARM64(aarch64)CPU架构的条件编译。ARM64架构常用于嵌入式和车载系统。

6.1.8 if_esd_can函数分析
def if_esd_can(if_true, if_false = []):
    return select({
        "//tools/platform:use_esd_can": if_true,
        "//conditions:default": if_false,
    })

  该函数处理ESD CAN总线相关的条件编译。ESD(Electronic System Design)是一家提供CAN接口卡的公司,其产品常用于车辆通信。

6.1.9 copts_if_esd_can函数分析
def copts_if_esd_can():
    return if_esd_can(["-DUSE_ESD_CAN=1"], ["-DUSE_ESD_CAN=0"])

  该函数生成ESD CAN总线相关的编译选项。

6.1.10 if_profiler函数分析
def if_profiler():
    return select({
        "//tools/platform:enable_profiler": ["-DENABLE_PROFILER=1"],
        "//conditions:default": ["-DENABLE_PROFILER=0"],
    })

  该函数处理性能分析器相关的条件编译。性能分析器用于分析和优化代码性能。

6.2 common.bzl文件分析

6.2.1 文件结构概述

  common.bzl文件提供了跨平台的通用工具函数,用于工具链定位、环境变量处理、文件操作等。该文件从TensorFlow项目借鉴而来,提供了丰富的实用函数。

# This file was adapted from third_party/remote_config/common.bzl in tensorflow.git
"""Functions common across configure rules."""

BAZEL_SH = "BAZEL_SH"
PYTHON_BIN_PATH = "PYTHON_BIN_PATH"
PYTHON_LIB_PATH = "PYTHON_LIB_PATH"
TF_PYTHON_CONFIG_REPO = "TF_PYTHON_CONFIG_REPO"
6.2.2 auto_config_fail函数分析
def auto_config_fail(msg):
    """Output failure message when auto configuration fails."""
    red = "\033[0;31m"
    no_color = "\033[0m"
    fail("%sConfiguration Error:%s %s\n" % (red, no_color, msg))

  该函数用于输出配置失败的错误信息。它使用ANSI颜色代码将错误信息显示为红色,提高可读性。

6.2.3 which函数分析
def which(repository_ctx, program_name):
    """Returns the full path to a program on the execution platform.

    Args:
      repository_ctx: the repository_ctx
      program_name: name of the program on the PATH

    Returns:
      The full path to a program on the execution platform.
    """
    result = execute(repository_ctx, ["which", program_name])
    return result.stdout.rstrip()

  该函数在执行平台上查找程序的全路径。它使用Unix的which命令来定位程序。

6.2.4 get_python_bin函数分析
def get_python_bin(repository_ctx):
    """Gets the python bin path.

    Args:
      repository_ctx: the repository_ctx

    Returns:
      The python bin path.
    """
    python_bin = get_host_environ(repository_ctx, PYTHON_BIN_PATH)
    if python_bin != None:
        return python_bin
    python_bin_path = which(repository_ctx, "python")
    if python_bin_path == None:
        auto_config_fail("Cannot find python in PATH, please make sure " +
                         "python is installed and add its directory in PATH, or --define " +
                         "%s='/something/else'.\nPATH=%s" % (
                             PYTHON_BIN_PATH,
                             get_environ("PATH", ""),
                         ))
    return python_bin_path

  该函数获取Python解释器的路径。它首先检查环境变量PYTHON_BIN_PATH,如果未设置,则使用which命令查找Python。如果找不到Python,则输出错误信息并失败。

6.2.5 get_bash_bin函数分析
def get_bash_bin(repository_ctx):
    """Gets the bash bin path.

    Args:
      repository_ctx: the repository_ctx

    Returns:
      The bash bin path.
    """
    bash_bin = get_host_environ(repository_ctx, BAZEL_SH)
    if bash_bin != None:
        return bash_bin
    bash_bin_path = which(repository_ctx, "bash")
    if bash_bin_path == None:
        auto_config_fail("Cannot find bash in PATH, please make sure " +
                         "bash is installed and add its directory in PATH, or --define " +
                         "%s='/path/to/bash'.\nPATH=%s" % (
                             BAZEL_SH,
                             get_environ("PATH", ""),
                         ))
    return bash_bin_path

  该函数获取Bash解释器的路径,逻辑与get_python_bin类似。

6.2.6 read_dir函数分析
def read_dir(repository_ctx, src_dir):
    """Returns a sorted list with all files in a directory.

    Finds all files inside a directory, traversing subfolders and following
    symlinks.

    Args:
      repository_ctx: the repository_ctx
      src_dir: the directory to traverse

    Returns:
      A sorted list with all files in a directory.
    """
    find_result = execute(
        repository_ctx,
        ["find", src_dir, "-follow", "-type", "f"],
        empty_stdout_fine = True,
    )
    result = find_result.stdout
    return sorted(result.splitlines())

  该函数递归地读取目录中的所有文件,包括子目录和符号链接。它使用Unix的find命令来查找文件。

6.2.7 get_environ函数分析
def get_environ(repository_ctx, name, default_value = None):
    """Returns the value of an environment variable on the execution platform.

    Args:
      repository_ctx: the repository_ctx
      name: the name of environment variable
      default_value: the value to return if not set

    Returns:
      The value of the environment variable 'name' on the execution platform
      or 'default_value' if it's not set.
    """
    cmd = "echo -n \"$%s\"" % name
    result = execute(
        repository_ctx,
        [get_bash_bin(repository_ctx), "-c", cmd],
        empty_stdout_fine = True,
    )
    if len(result.stdout) == 0:
        return default_value
    return result.stdout

  该函数获取执行平台上的环境变量值。它使用Bash的echo命令来读取环境变量。

6.2.8 get_host_environ函数分析
def get_host_environ(repository_ctx, name, default_value = None):
    """Returns the value of an environment variable on the host platform.

    The host platform is the machine that Bazel runs on.

    Args:
      repository_ctx: the repository_ctx
      name: the name of environment variable
      default_value: the value to return if not set

    Returns:
      The value of the environment variable 'name' on the host platform
      or 'default_value' if it's not set.
    """
    if name in repository_ctx.os.environ:
        return repository_ctx.os.environ.get(name).strip()

    if hasattr(repository_ctx.attr, "environ") and name in repository_ctx.attr.environ:
        return repository_ctx.attr.environ.get(name).strip()

    return default_value

  该函数获取主机平台上的环境变量值。它首先检查repository_ctx.os.environ,然后检查repository_ctx.attr.environ

6.2.9 execute函数分析
def execute(
        repository_ctx,
        cmdline,
        error_msg = None,
        error_details = None,
        empty_stdout_fine = False,
        ignore_error = True):
    """Executes an arbitrary shell command.

    Args:
      repository_ctx: the repository_ctx object
      cmdline: list of strings, the command to execute
      error_msg: string, a summary of the error if the command fails
      error_details: string, details about the error or steps to fix it
      empty_stdout_fine: bool, if True, an empty stdout result is fine,
        otherwise it's an error
      ignore_error: bool, if True, ignore errors

    Returns:
      The result of repository_ctx.execute(cmdline)
    """
    result = raw_exec(repository_ctx, cmdline)
    if result.stderr or not (empty_stdout_fine or result.stdout):
      if not ignore_error:
        fail(
            "\n".join([
                error_msg.strip() if error_msg else "Repository command failed",
                result.stderr.strip(),
                error_details if error_details else "",
            ]),
        )
    return result

  该函数执行任意的shell命令,并提供错误处理机制。它调用raw_exec来执行命令,然后根据结果决定是否失败。

6.2.10 files_exist函数分析
def files_exist(repository_ctx, paths, bash_bin = None):
    """Checks which files in paths exists.

    Args:
      repository_ctx: the repository_ctx
      paths: a list of paths
      bash_bin: path to the bash interpreter

    Returns:
      Returns a list of Bool. True means that the path at the
      same position in the paths list exists.
    """
    if bash_bin == None:
        bash_bin = get_bash_bin(repository_ctx)

    cmd_tpl = "[ -e \"%s\" ] && echo True || echo False"
    cmds = [cmd_tpl % path for path in paths]
    cmd = " ; ".join(cmds)

    stdout = execute(repository_ctx, [bash_bin, "-c", cmd]).stdout.strip()
    return [val == "True" for val in stdout.splitlines()]

  该函数检查哪些文件存在。它使用Bash的[ -e ]测试命令来检查文件存在性。

6.2.11 realpath函数分析
def realpath(repository_ctx, path, bash_bin = None):
    """Returns the result of "realpath path".

    Args:
      repository_ctx: the repository_ctx
      path: a path on the file system
      bash_bin: path to the bash interpreter

    Returns:
      Returns the result of "realpath path"
    """
    if bash_bin == None:
        bash_bin = get_bash_bin(repository_ctx)

    return execute(repository_ctx, [bash_bin, "-c", "realpath \"%s\"" % path]).stdout.strip()

  该函数返回路径的规范形式(解析所有符号链接)。

6.2.12 make_copy_files_rule函数分析
def make_copy_files_rule(repository_ctx, name, srcs, outs):
    """Returns a rule to copy a set of files."""
    cmds = []

    # Copy files.
    for src, out in zip(srcs, outs):
        cmds.append('cp -f "%s" "$(location %s)"' % (src, out))
    outs = [('        "%s",' % out) for out in outs]
    return """genrule(
    name = "%s",
    outs = [
%s
    ],
    cmd = \"""%s \\""",
)""" % (name, "\n".join(outs), " && \\\n".join(cmds))

  该函数生成一个Bazel genrule来复制一组文件。

6.2.13 make_copy_dir_rule函数分析
def make_copy_dir_rule(repository_ctx, name, src_dir, out_dir, exceptions = None):
    """Returns a rule to recursively copy a directory.
    If exceptions is not None, it must be a list of files or directories in
    'src_dir'; these will be excluded from copying.
    """
    src_dir = _norm_path(src_dir)
    out_dir = _norm_path(out_dir)
    outs = read_dir(repository_ctx, src_dir)
    post_cmd = ""
    if exceptions != None:
        outs = [x for x in outs if not any([
            x.startswith(src_dir + "/" + y)
            for y in exceptions
        ])]
    outs = [('        "%s",' % out.replace(src_dir, out_dir)) for out in outs]

    out_dir = "$(@D)/%s" % out_dir if len(outs) > 1 else "$(@D)"
    if exceptions != None:
        for x in exceptions:
            post_cmd += " ; rm -fR " + out_dir + "/" + x
    return """genrule(
    name = "%s",
    outs = [
%s
    ],
    cmd = \"""cp -rLf "%s/." "%s/" %s\"\"",
)""" % (name, "\n".join(outs), src_dir, out_dir, post_cmd)

  该函数生成一个Bazel genrule来递归复制目录,支持排除特定文件。

6.3 平台配置集成分析

6.3.1 WORKSPACE文件配置

  平台配置需要在WORKSPACE文件中定义相应的配置标志:

# WORKSPACE文件
new_local_repository(
    name = "platform",
    path = "tools/platform",
    build_file = "tools/platform/BUILD",
)

# 定义平台配置
# 通过命令行参数或配置文件设置
# --define use_gpu=1
# --define with_teleop=1
# --define use_esd_can=1
6.3.2 BUILD文件配置

  在BUILD文件中使用平台抽象函数:

load("//tools/platform:build_defs.bzl", "if_gpu", "if_x86_64", "copts_if_gpu")

cc_library(
    name = "perception_module",
    srcs = if_gpu(["gpu_perception.cc"], ["cpu_perception.cc"]),
    copts = copts_if_gpu() + if_x86_64(["-march=haswell"], []),
    deps = [
        "//cyber:cyber",
    ],
)
6.3.3 跨平台构建

  支持跨平台构建的命令:

# x86_64平台构建
bazel build --config=linux //modules/...

# ARM64平台构建
bazel build --config=linux_aarch64 //modules/...

# GPU构建
bazel build --config=gpu //modules/...

6.4 工具链管理分析

6.4.1 Python工具链

  Python工具链用于执行配置脚本和生成代码:

# 在配置规则中使用
python_bin = get_python_bin(repository_ctx)
result = execute(repository_ctx, [python_bin, "configure.py"])
6.4.2 Bash工具链

  Bash工具链用于执行shell命令:

# 在配置规则中使用
bash_bin = get_bash_bin(repository_ctx)
result = execute(repository_ctx, [bash_bin, "-c", "ls -la"])
6.4.3 编译器工具链

  编译器工具链通过Bazel的C++工具链配置:

# 在BUILD文件中使用
cc_toolchain_suite(
    name = "toolchain",
    toolchains = {
        "//conditions:default": ":gcc_toolchain",
        "@platforms//cpu:x86_64": ":gcc_x86_64_toolchain",
        "@platforms//cpu:aarch64": ":gcc_aarch64_toolchain",
    },
)

7. 设计模式

7.1 策略模式(Strategy Pattern)

  build_defs.bzl中的条件函数体现了策略模式:

def if_gpu(if_true, if_false = []):
    return select({
        "//tools/platform:use_gpu": if_true,
        "//conditions:default": if_false,
    })

  根据不同的配置选择不同的编译策略。

7.2 工厂模式(Factory Pattern)

  工具链定位器使用工厂模式创建工具链实例:

def get_python_bin(repository_ctx):
    python_bin = get_host_environ(repository_ctx, PYTHON_BIN_PATH)
    if python_bin != None:
        return python_bin
    python_bin_path = which(repository_ctx, "python")
    if python_bin_path == None:
        auto_config_fail("Cannot find python...")
    return python_bin_path

7.3 适配器模式(Adapter Pattern)

  common.bzl中的函数适配了不同平台的差异:

def get_host_environ(repository_ctx, name, default_value = None):
    if name in repository_ctx.os.environ:
        return repository_ctx.os.environ.get(name).strip()
    if hasattr(repository_ctx.attr, "environ") and name in repository_ctx.attr.environ:
        return repository_ctx.attr.environ.get(name).strip()
    return default_value

7.4 模板方法模式(Template Method Pattern)

  规则生成函数使用模板方法模式:

def make_copy_files_rule(repository_ctx, name, srcs, outs):
    cmds = []
    for src, out in zip(srcs, outs):
        cmds.append('cp -f "%s" "$(location %s)"' % (src, out))
    # 生成genrule模板
    return """genrule(...)"""

7.5 单例模式(Singleton Pattern)

  全局常量体现了单例模式:

BAZEL_SH = "BAZEL_SH"
PYTHON_BIN_PATH = "PYTHON_BIN_PATH"

7.6 建造者模式(Builder Pattern)

  复杂的配置可以使用建造者模式构建:

class PlatformConfigBuilder:
    def __init__(self):
        self.config = {}
    
    def with_gpu(self, enabled):
        self.config["use_gpu"] = enabled
        return self
    
    def with_teleop(self, enabled):
        self.config["with_teleop"] = enabled
        return self
    
    def build(self):
        return self.config

8. 总结

  apollo_tools_platform子模块通过平台抽象层实现跨平台构建统一管理。模块采用build_defs.bzl和common.bzl核心文件,分别提供平台特定条件编译函数和通用工具链管理函数。通过if_gpu、if_x86_64、if_aarch64等函数实现GPU、CPU架构等特性条件编译,为Apollo多硬件平台构建提供基础设施支持。