深入解析 OpenHarmony 构建系统-3-GN 构建系统管理脚本

引言

OpenHarmony作为一款面向全场景的分布式操作系统,其构建系统在开发过程中扮演着至关重要的角色。本文将详细介绍OpenHarmony构建系统下的一个gn封装脚本,该脚本用于管理和执行 gn 命令,更高效地管理构建过程。
位置:/build/hb/services/gn.py

脚本概述

该脚本定义了一个 Gn 类,继承自 BuildFileGeneratorInterface 接口,并实现了多个方法来执行不同的 gn 命令。这些命令包括生成构建文件、查询路径、描述目标、列出目标、引用检查、格式化和清理等。

脚本结构
1. 导入必要的模块
import sys
import os
from enum import Enum

from containers.status import throw_exception
from exceptions.ohos_exception import OHOSException
from services.interface.build_file_generator_interface import BuildFileGeneratorInterface
from resources.config import Config
from containers.arg import Arg, ModuleType
from util.system_util import SystemUtil
from util.io_util import IoUtil
from util.log_util import LogUtil
  • sysos 模块用于处理系统相关的操作。
  • Enum 用于定义枚举类型。
  • throw_exception 是一个装饰器,用于捕获和处理异常。
  • OHOSException 是自定义的异常类。
  • BuildFileGeneratorInterface 是接口类,定义了构建文件生成器的基本方法。
  • Config 类用于读取和管理配置文件。
  • ArgModuleType 用于处理命令行参数。
  • SystemUtilIoUtil 提供系统和输入输出相关的工具函数。
  • LogUtil 用于日志记录。
2. 定义枚举类型 CMDTYPE
class CMDTYPE(Enum):
    GEN = 1
    PATH = 2
    DESC = 3
    LS = 4
    REFS = 5
    FORMAT = 6
    CLEAN = 7
  • CMDTYPE 枚举类型定义了 gn 命令的类型,包括生成构建文件 (GEN)、查询路径 (PATH)、描述目标 (DESC)、列出目标 (LS)、引用检查 (REFS)、格式化 (FORMAT) 和清理 (CLEAN)。
3. Gn 类定义
class Gn(BuildFileGeneratorInterface):
    def __init__(self):
        super().__init__()
        self.config = Config()
        self._regist_gn_path()

    def run(self):
        self.execute_gn_cmd(CMDTYPE.GEN)
  • Gn 类继承自 BuildFileGeneratorInterface 接口。
  • __init__ 方法初始化 Gn 类实例,读取配置文件并注册 gn 可执行文件路径。
  • run 方法默认执行生成构建文件的命令。
4. 注册 gn 可执行文件路径
@throw_exception
def _regist_gn_path(self):
    gn_path = os.path.join(self.config.root_path, 'prebuilts/build-tools/{}-x86/bin/gn'
            .format(sys.platform))
    if os.path.exists(gn_path):
        self.exec = gn_path
    else:
        raise OHOSException(
            'There is no gn executable file at {}'.format(gn_path), '0001')
  • _regist_gn_path 方法根据系统平台查找 gn 可执行文件的路径,并存储在 self.exec 中。
  • 如果路径不存在,抛出 OHOSException 异常。
5. 转换注册的参数和标志
def _convert_args(self) -> list:
    args_list = []
    for key, value in self.args_dict.items():
        if isinstance(value, bool):
            args_list.append('{}={}'.format(key, str(value).lower()))
        elif isinstance(value, str):
            args_list.append('{}="{}"'.format(key, value))
        elif isinstance(value, int):
            args_list.append('{}={}'.format(key, value))
        elif isinstance(value, list):
            args_list.append('{}="{}"'.format(key, "&&".join(value)))
    return args_list

def _convert_flags(self) -> list:
    flags_list = []
    for key, value in self.flags_dict.items():
        if key == 'gn_flags' and isinstance(value, list):
            flags_list += value
        elif value == '':
            flags_list.append('{}'.format(key))
        else:
            flags_list.append('{}={}'.format(key, str(value)).lower())
    return flags_list
  • _convert_args 方法将注册的参数转换为列表。
  • _convert_flags 方法将注册的标志转换为列表。
6. 检查选项的有效性
def _check_options_validity(self, option: str, args_file: dict):
    support_sub_options = args_file.get("arg_attribute").get("support_sub_options")
    option_name = option.lstrip('-')
    option_value = ""
    if '=' in option:
        option_name, option_value = option.lstrip('-').split('=')
    if option_name in support_sub_options:
        sub_optional_list = support_sub_options.get(option_name).get("arg_attribute").get("optional")
        if sub_optional_list and option_value not in sub_optional_list:
            raise OHOSException('ERROR argument "--{}": Invalid choice "{}". choose from {}'.format(option_name, option_value, sub_optional_list), '3006')
    else:
        raise OHOSException('ERROR argument "{}": Invalid choice "{}". choose from {}'.format(args_file.get("arg_name"), option, list(support_sub_options.keys())), '3003')
  • _check_options_validity 方法检查传入的选项是否有效,确保其值在支持的范围内。
7. 执行 gn 命令
@throw_exception
def execute_gn_cmd(self, cmd_type: int, **kwargs):
    if cmd_type == CMDTYPE.GEN:
        return self._execute_gn_gen_cmd()
    elif cmd_type == CMDTYPE.PATH:
        return self._execute_gn_path_cmd(**kwargs)
    elif cmd_type == CMDTYPE.DESC:
        return self._execute_gn_desc_cmd(**kwargs)
    elif cmd_type == CMDTYPE.LS:
        return self._execute_gn_ls_cmd(**kwargs)
    elif cmd_type == CMDTYPE.REFS:
        return self._execute_gn_refs_cmd(**kwargs)
    elif cmd_type == CMDTYPE.FORMAT:
        return self._execute_gn_format_cmd(**kwargs)
    elif cmd_type == CMDTYPE.CLEAN:
        return self._execute_gn_clean_cmd(**kwargs)
    else:
        raise OHOSException(
            'You are tring to use an unsupported gn cmd type "{}"'.format(cmd_type), '3001')
  • execute_gn_cmd 方法根据命令类型调用相应的执行方法。
8. 具体命令的执行方法
@throw_exception
def _execute_gn_gen_cmd(self, **kwargs):
    gn_gen_cmd = [self.exec, 'gen', '--args={}'.format(' '.join(self._convert_args())), self.config.out_path] + self._convert_flags()
    if self.config.os_level == 'mini' or self.config.os_level == 'small':
        gn_gen_cmd.append(f'--script-executable={sys.executable}')
    try:
        LogUtil.write_log(self.config.log_path, 'Excuting gn command: {} {} --args="{}" {}'.format(self.exec, 'gen', ' '.join(self._convert_args()).replace('"', "\\\""), ' '.join(gn_gen_cmd[3:])), 'info')
        SystemUtil.exec_command(gn_gen_cmd, self.config.log_path)
    except OHOSException:
        raise OHOSException('GN phase failed', '3000')

@throw_exception
def _execute_gn_path_cmd(self, **kwargs):
    out_dir = kwargs.get("out_dir")
    default_options = ['--all']
    args_file = Arg.read_args_file(ModuleType.TOOL)['path']
    if (os.path.exists(os.path.join(out_dir, "args.gn"))):
        gn_path_cmd = [self.exec, 'path', out_dir]
        for arg in kwargs.get('args_list'):
            if arg.startswith('-'):
                self._check_options_validity(arg, args_file)
            gn_path_cmd.append(arg)
        gn_path_cmd.extend(default_options)
        sort_index = gn_path_cmd.index
        gn_path_cmd = list(set(gn_path_cmd))
        gn_path_cmd.sort(key=sort_index)
        SystemUtil.exec_command(gn_path_cmd)
    else:
        raise OHOSException('"{}" Not a build directory.'.format(out_dir), '3004')

@throw_exception
def _execute_gn_desc_cmd(self, **kwargs):
    out_dir = kwargs.get("out_dir")
    default_options = ['--tree', '--blame']
    args_file = Arg.read_args_file(ModuleType.TOOL)['desc']
    if (os.path.exists(os.path.join(out_dir, "args.gn"))):
        gn_desc_cmd = [self.exec, 'desc', out_dir]
        for arg in kwargs.get('args_list'):
            if arg.startswith('-'):
                self._check_options_validity(arg, args_file)
            gn_desc_cmd.append(arg)
        gn_desc_cmd.extend(default_options)
        sort_index = gn_desc_cmd.index
        gn_desc_cmd = list(set(gn_desc_cmd))
        gn_desc_cmd.sort(key=sort_index)
        SystemUtil.exec_command(gn_desc_cmd)
    else:
        raise OHOSException('"{}" Not a build directory.'.format(out_dir), '3004')

@throw_exception
def _execute_gn_ls_cmd(self, **kwargs):
    out_dir = kwargs.get("out_dir")
    args_file = Arg.read_args_file(ModuleType.TOOL)['ls']
    if (os.path.exists(os.path.join(out_dir, "args.gn"))):
        gn_ls_cmd = [self.exec, 'ls', out_dir]
        for arg in kwargs.get('args_list'):
            if arg.startswith('-'):
                self._check_options_validity(arg, args_file)
            gn_ls_cmd.append(arg)
        SystemUtil.exec_command(gn_ls_cmd)
    else:
        raise OHOSException('"{}" Not a build directory.'.format(out_dir), '3004')

@throw_exception
def _execute_gn_refs_cmd(self, **kwargs):
    out_dir = kwargs.get("out_dir")
    args_file = Arg.read_args_file(ModuleType.TOOL)['refs']
    if (os.path.exists(os.path.join(out_dir, "args.gn"))):
        gn_refs_cmd = [self.exec, 'refs', out_dir]
        for arg in kwargs.get('args_list'):
            if arg.startswith('-'):
                self._check_options_validity(arg, args_file)
            gn_refs_cmd.append(arg)
        SystemUtil.exec_command(gn_refs_cmd)
    else:
        raise OHOSException('"{}" Not a build directory.'.format(out_dir), '3004')
  • _execute_gn_gen_cmd 方法执行 gn gen 命令,生成构建文件。
  • _execute_gn_path_cmd 方法执行 gn path 命令,查询路径。
  • _execute_gn_desc_cmd 方法执行 gn desc 命令,描述目标。
  • _execute_gn_ls_cmd 方法执行 gn ls 命令,列出目标。
  • _execute_gn_refs_cmd 方法执行 gn refs 命令,引用检查。
使用场景
  1. 生成构建文件

    • 使用 gn gen 命令生成 Ninja 构建文件,支持多种配置选项。
  2. 查询路径

    • 使用 gn path 命令查询目标路径,支持多个选项和默认选项。
  3. 描述目标

    • 使用 gn desc 命令描述目标的详细信息,支持树形结构和责任分配信息。
  4. 列出目标

    • 使用 gn ls 命令列出所有目标,支持多种过滤和排序选项。
  5. 引用检查

    • 使用 gn refs 命令检查目标的引用关系,确保构建依赖的正确性。
结论

通过这个基于 Python 的脚本,我们可以更高效地管理和执行 gn 命令,提高构建过程的自动化水平。希望本文能帮助你更好地理解和使用这个脚本,提升开发效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值