【详细深入分析OpenHarmony编译流程】

文章细致剖析了OpenHarmony操作系统的编译构建流程,覆盖编译入口、源码结构、命令行参数处理、系统能力记录、gn/ninja构建步骤及优化策略,揭示了Python脚本在构建系统中的应用,以及如何管理部件、配置和系统能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

详细深入分析OpenHarmony编译流程

前言

OpenHarmony做为复杂的操作系统软件,其编译构建也比较复杂。本文主要记录作者自己在阅读编译脚本代码后对其的认识,不代表官方观点,不足之处还请批评指正。

本文顺着编译流程阅读和解释编译脚本代码,即执行时间靠前的代码先解释,执行时间靠后的代码后解释。

本文参考代码为2024年7月8日master分支代码。

为了方便查看OpenHarmony代码,作者搭建了一个服务器,欢迎使用https://lengqinjie.xyz
对于master分支代码,此服务器上的代码每小时更新一次,所以当你看到时,可能和本文有差异,属于正常情况。

编译入口

共有3种形式的编译入口

  1. hb build
  2. ./build.sh
  3. ./build.py

3种形式的编译入口最终都汇总到hb build。即内部实际上是统一的。下面以build.py为例进行讲解。

源码根目录

源码根目录下有很多子目录,这些子目录大多数为OpenHarmony各软件子系统的实现,其中1个目录为build目录,即本文重点讨论的内容,此目录内主要为编译构建脚本,主要是python语言实现。

为了方便使用,源码根目录下有2个符号链接,引用了编译构建的脚本文件

build.py -> build/build_scripts/build.py
build.sh -> build/build_scripts/build.sh

因此在源码根目录下执行这2个文件中的一个就能够启动编译构建过程。还有一种启动方式是hb命令,稍后再讲解。

编译命令

以build.py为例,在源码根目录下执行

./build.py -p rk3568

这里-p rk3568意思是编译构建产品(rk3568)。当然还支持很多其它参数,为了简化,这里只列出这个最重要的参数

获取源码根目录位置

build/build_script/build.py

0088 def main():
0089     root_path = find_top()
0090     return build(root_path, sys.argv[1:])
0091 
0092 
0093 if __name__ == "__main__":
0094     sys.exit(main())

注意89行代码中find_top函数指的是查找源码根目录,90行代码的作用是执行编译构建过程。
build/build_script/build.py

0032 def find_top() -> str:
0033     cur_dir = os.getcwd()
0034     while cur_dir != "/":
0035         build_config_file = os.path.join(
0036             cur_dir, 'build/config/BUILDCONFIG.gn')
0037         if os.path.exists(build_config_file):
0038             return cur_dir
0039         cur_dir = os.path.dirname(cur_dir)

上述代码递归向父目录搜索源码根目录,判断源码根目录的方法很简单,因为源码根目录下有build子目录,build内有config子目录,并且config内有BUILDCONFIG.gn文件。

开始构建

build/build_script/build.py

0073 def build(path: str, args_list: list) -> str:
0074     python_dir = None
0075     if "--python-dir" in args_list:
0076         index = args_list.index("--python-dir")
0077         if index < len(args_list) - 1:
0078             python_dir = args_list[index + 1]
0079             del args_list[index: index + 2]
0080         else:
0081             print("-python-dir parmeter missing value.")
0082             sys.exit()
0083     python_executable = get_python(python_dir)
0084     cmd = [python_executable, 'build/hb/main.py', 'build'] + args_list
0085     return check_output(cmd, cwd=path)

源码根目录获取成功以后,开始进行系统构建(build)。首先获取python解释器, 通过这个解释器执行main.py脚本,在原来参数的基础上,插入build参数。解释执行完后,返回执行结果。

从84行代码可以看出,这里等价于执行

hb build args_list

前提条件是对build/hb目录中python代码进行了安装动作(源码根目录下执行如下代码)

pip install build/hb

安装完后,就可以执行hb XXX命令了。

获取python解释器

build/build_script/build.py

0042 def get_python(python_relative_dir: str) -> str:
0043     topdir = find_top()
0044     if python_relative_dir is None:
0045         python_relative_dir = 'prebuilts/python'
0046     python_base_dir = os.path.join(topdir, python_relative_dir)
0047 
0048     if os.path.exists(python_base_dir):
0049         python_dir = search(python_base_dir, 'python3')
0050         return os.path.join(python_dir, 'python3')
0051     else:
0052         print("please execute build/prebuilts_download.sh.",
0053             "if you used '--python-dir', check whether the input path is valid.")
0054         sys.exit()

这里的主要目的是判断源码根目录下是否有prebuilts/python这个目录,如果没有的话,则需要执行
build/prebuilts_download.sh(当然是在源码根目录执行了)去下载对应的python解释器程序文件,当然这里还会下载其它一些工具。

这样做的目的是减少用户自行安装软件失败的可能性,因为这些软件对版本有要求,随着openharmony代码的更新,可能需要新的工具软件或者工具软件的新版本。通过上述脚本下载的工具软件都会存放在prebuilts目录下,且是和代码配套,不会存在兼容性问题(因为prebuilts_download.sh会维护兼容性)。
上述程序返回的是python解释器程序的文件路径。

0045 def search(findir, target):
0046     for root, dirs, files in os.walk(findir):
0047         if target in files:
0048             return root
0049     return False

这里主要是从目录findir中去寻找target文件,可以是在子目录内(递归查找),如果文件存在,返回文件所在的目录。因为python解释器可能在prebuilts/python内比较深的子目录中(如prebuilts/python/linux-x86/current/bin/python3)。

hb主程序

当python解释器成功获取后,就会使用构造的参数调用hb主程序
build/hb/main.py

0241     @staticmethod
0242     @throw_exception
0243     def main():
0244         main = Main()
0245         module_initializers = {
0246             'build': main._init_indep_build_module if main._is_indep_build() else main._init_build_module,
0247             'init': main._init_hb_init_module,
0248             'indep_build': main._init_indep_build_module,
0249             'set': main._init_set_module,
0250             'env': main._init_env_module,
0251             'clean': main._init_clean_module,
0252             'tool': main._init_tool_module,
0253             'install': main._init_install_module,
0254             'package': main._init_package_module,
0255             'publish': main._init_publish_module,
0256             'update': main._init_update_module,
0257             'push': main._push_module
0258         }
0259 
0260         module_type = sys.argv[1]
0261         if module_type == 'help':
0262             for all_module_type in ModuleType:
0263                 LogUtil.hb_info(Separator.long_line)
0264                 LogUtil.hb_info(Arg.get_help(all_module_type))
0265             exit()
0266 
0267         if module_type not in module_initializers:
0268             raise OHOSException(f'There is no such option {module_type}', '0018')
0269 
0270         start_time = SystemUtil.get_current_time()
0271         module = module_initializers[module_type]()
0272         try:
0273             module.run()
0274             if module_type == 'build':
0275                 LogUtil.hb_info('Cost Time:  {}'.format(SystemUtil.get_current_time() - start_time))
0276         except KeyboardInterrupt:
0277             for file in os.listdir(ARGS_DIR):
0278                 if file.endswith('.json') and os.path.exists(os.path.join(ARGS_DIR, file)):
0279                     os.remove(os.path.join(ARGS_DIR, file))
0280             print('User abort')
0281             return -1
0282         else:
0283             return 0
0284 
0285 
0286 if __name__ == "__main__":
0287     sys.exit(Main.main())

本程序有2种使用方式

  1. hb (build | set | clean | env | tool | help | … ) args
  2. python3 build/hb/main.py (build | set | clean | env | tool | help) args
    第一种方式需要安装此python模块,即
pip install build/hb

第二种方式即本文介绍的方式,参考build/build_script/build.py。
build.py是对hb build的封装,其它hb xxx命令目前还没有看到直接封装的脚本,本文主要研究build的流程,如果还有时间的话,再研究hb set, hb clean, hb env等命令的作用和实现。

build模块初始化

build/hb/main.py

0095     def _init_build_module(self) -> BuildModuleInterface:
0096         args_dict = Arg.parse_all_args(ModuleType.BUILD)
0097 
0098         if args_dict.get("product_name").arg_value != '':
0099             set_args_dict = Arg.parse_all_args(ModuleType.SET)
0100             set_args_resolver = SetArgsResolver(set_args_dict)
0101             ohos_set_module = OHOSSetModule(set_args_dict, set_args_resolver, "")
0102             ohos_set_module.set_product()
0103 
0104         preloader = OHOSPreloader()
0105         loader = OHOSLoader()
0106         generate_ninja = Gn()
0107         ninja = Ninja()
0108         build_args_resolver = BuildArgsResolver(args_dict)
0109 
0110         return OHOSBuildModule(args_dict, build_args_resolver, preloader, loader, generate_ninja, ninja)

96行先将所有build能识别的参数进行解析,并存入args_dict字典中。
如果参数里面有"product_name", 那么还要执行set的逻辑(98到102行)。本文中的-p参数就是product_name的简写,所以这段逻辑会执行, hb set也支持product_name参数。也就是说hb build -p product_name实际上相当于hb set -p product_name和hb build -p product_name这2条命令的执行效果。
104到108行初始化几个对象,代表编译构建的几个阶段:预加载、加载、生成ninja脚本,执行ninja脚本,以及初始化build阶段各参数的解析器。
最后(110行)用这些对象初始化构建模块

build模块执行

build/hb/modules/ohos_build_module.py

0062     @throw_exception
0063     def run(self):
0064         try:
0065             super().run()
0066         except OHOSException as exception:
0067             raise exception
0068         else:
0069             LogUtil.hb_info('{} build success'.format(
0070                 self.args_dict.get('product_name').arg_value))

run函数实际上使用的是父类的函数,如下
hb/modules/interface/build_module_interface.py

0063     def run(self):
0064         try:
0065             self._prebuild_and_preload()
0066             self._load()
0067             self._gn()
0068             self._ninja()
0069         except OHOSException as exception:
0070             raise exception
0071         else:
0072             self._post_target_compilation()
0073         finally:
0074             self._post_build()

0084     @TimerUtil.cost_time
0085     @abstractmethod
0086     def _prebuild_and_preload(self):
0087         self._prebuild()
0088         self._preload()

0106     @TimerUtil.cost_time
0107     @abstractmethod
0108     def _gn(self):
0109         self._pre_target_generate()
0110         self._target_generate()
0111         self._post_target_generate()

0121     @TimerUtil.cost_time
0122     @abstractmethod
0123     def _ninja(self):
0124         self._pre_target_compilation()
0125         self._target_compilation()

可以看到实际上run定义了一个编译构造的流程,分别是

  1. prebuild
  2. preload
  3. load
  4. pre_target_generate
  5. target_generate
  6. post_target_generate
  7. pre_target_compilation
  8. target_compilation
  9. post_target_compilation
  10. post_build

后续会详细讲这些流程

build和set参数解析

hb/containers/arg.py

0244     @staticmethod
0245     def parse_all_args(module_type: ModuleType) -> dict:
0246         args_dict = {}
0247         parser = argparse.ArgumentParser()
0248         all_args = Arg.read_args_file(module_type)
0249 
0250         for arg in all_args.values():
0251             arg = dict(arg)
0252             ArgsFactory.genetic_add_option(parser, arg)
0253             oh_arg = Arg.create_instance_by_dict(arg)
0254             args_dict[oh_arg.arg_name] = oh_arg
0255 
0256         parser.usage = 'hb {} [option]'.format(module_type.name.lower())
0257         parser_args = parser.parse_known_args(sys.argv[2:])
0258 
0259         for oh_arg in args_dict.values():
0260             if isinstance(oh_arg, Arg):
0261                 assigned_value = parser_args[0].__dict__[oh_arg.arg_name]
0262                 if oh_arg.arg_type == ArgType.LIST:
0263                     convert_assigned_value = TypeCheckUtil.tile_list(assigned_value)
0264                     convert_assigned_value = list(set(convert_assigned_value))
0265                 elif oh_arg.arg_type == ArgType.SUBPARSERS:
0266                     convert_assigned_value = TypeCheckUtil.tile_list(assigned_value)
0267                     if len(convert_assigned_value):
0268                         convert_assigned_value = list(set(convert_assigned_value))
0269                         convert_assigned_value.extend(parser_args[1])
0270                         convert_assigned_value.sort(key=sys.argv[2:].index)
0271                 elif oh_arg.arg_type == ArgType.BOOL:
0272                     if str(assigned_value).lower() == 'false':
0273                         convert_assigned_value = False
0274                     elif str(assigned_value).lower() == 'true' or assigned_value is None:
0275                         convert_assigned_value = True
0276                 else:
0277                     convert_assigned_value = assigned_value
0278 
0279                 if oh_arg.arg_attribute.get('deprecated', None) and oh_arg.arg_value != convert_assigned_value:
0280                     LogUtil.hb_warning(
0281                         'compile option "{}" will be deprecated, \
0282                             please consider use other options'.format(oh_arg.arg_name))
0283                 oh_arg.arg_value = convert_assigned_value
0284                 Arg.write_args_file(
0285                     oh_arg.arg_name, oh_arg.arg_value, module_type)
0286 
0287         return args_dict

246行: 解析完的参数存储在此字典,并最后返回
247行:通用的参数解析器对象
248行:读取此类型支持的参数列表,由json配置文件存储(如build支持的参数存储在hb/resources/args/default/buildargs.json中)
250行:遍历本模块(如build)支持的参数
251行:每个参数的描述信息也是一个字典形式
252行:命令行加入对此参数的识别能力
253行:创建一个描述本参数的软件对象
254行:记录下参数名称与参数对象的映射关系,加入字典
257行:先解析此模块能识别的参数,比如本文的“-p rk3568”,注意参数解析后,原来的参数表(sys.argv)并不会减少,下一个模块还能继续解析。
259行:遍历支持的参数表中的所有参数
261行:读取已解析出的参数值,如本例的rk3568,针对用户没有配置的参数,则读取的是默认值(由buildargs.json定义)
262~277行:对参数值进行规范化处理
279~282行:如果使用了一个过期的参数,且和默认值不同,即用户指定了其他值,则打印警告提示日志
283~285行:更新参数对象的值并刷新参数配置文件(如out/hb_args/buildargs.json)
286行:最后返回参数对象表(字典)。

参数配置文件读写

hb/containers/arg.py

0323     @staticmethod
0324     @throw_exception
0325     def read_args_file(module_type: ModuleType):
0326         args_file_path = ''
0327         default_file_path = ''
0328         if module_type == ModuleType.BUILD:
0329             args_file_path = CURRENT_BUILD_ARGS
0330             default_file_path = DEFAULT_BUILD_ARGS
0331         elif module_type == ModuleType.SET:
0332             args_file_path = CURRENT_SET_ARGS
0333             default_file_path = DEFAULT_SET_ARGS
0334         elif module_type == ModuleType.CLEAN:
0335             args_file_path = CURRENT_CLEAN_ARGS
0336             default_file_path = DEFAULT_CLEAN_ARGS
0337         elif module_type == ModuleType.ENV:
0338             args_file_path = CURRENT_ENV_ARGS
0339             default_file_path = DEFAULT_ENV_ARGS
0340         elif module_type == ModuleType.TOOL:
0341             args_file_path = CURRENT_TOOL_ARGS
0342             default_file_path = DEFAULT_TOOL_ARGS
0343         elif module_type == ModuleType.INDEP_BUILD:
0344             args_file_path = CURRENT_INDEP_BUILD_ARGS
0345             default_file_path = DEFAULT_INDEP_BUILD_ARGS
0346         elif module_type == ModuleType.INSTALL:
0347             args_file_path = CURRENT_INSTALL_ARGS
0348             default_file_path = DEFAULT_INSTALL_ARGS
0349         elif module_type == ModuleType.PACKAGE:
0350             args_file_path = CURRENT_PACKAGE_ARGS
0351             default_file_path = DEFAULT_PACKAGE_ARGS
0352         elif module_type == ModuleType.PUBLISH:
0353             args_file_path = CURRENT_PUBLISH_ARGS
0354             default_file_path = DEFAULT_PUBLISH_ARGS
0355         elif module_type == ModuleType.UPDATE:
0356             args_file_path = CURRENT_UPDATE_ARGS
0357             default_file_path = DEFAULT_UPDATE_ARGS
0358         elif module_type == ModuleType.PUSH:
0359             args_file_path = CURRENT_PUSH_ARGS
0360             default_file_path = DEFAULT_PUSH_ARGS
0361         else:
0362             raise OHOSException(
0363                 'You are trying to read args file, but there is no corresponding module "{}" args file'
0364                 .format(module_type.name.lower()), "0018")
0365         if not os.path.exists(CURRENT_ARGS_DIR):
0366             os.makedirs(CURRENT_ARGS_DIR, exist_ok=True)
0367         if not os.path.exists(args_file_path):
0368             IoUtil.copy_file(src=default_file_path, dst=args_file_path)
0369         return IoUtil.read_json_file(args_file_path)

以build类型的参数文件为例,其它类似。

  • CURRENT_BUILD_ARGS: 生效的参数配置文件
  • DEFAULT_BUILD_ARGS:默认的参数配置文件
    如果生效的参数文件存在,则直接读出,否则从默认参数文件拷贝后再读出
    参数文件的位置由下面的文件指定
    build/hb/resources/global_var.py
0022 CURRENT_OHOS_ROOT = os.path.dirname(os.path.dirname(
0023     os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
0024 CURRENT_BUILD_DIR = os.path.join(CURRENT_OHOS_ROOT, 'build')
0025 CURRENT_HB_DIR = os.path.join(CURRENT_BUILD_DIR, 'hb')
0026 DEFAULT_CCACHE_DIR = os.path.join(CURRENT_OHOS_ROOT, '.ccache')
0027 
0028 ARGS_DIR = os.path.join(CURRENT_HB_DIR, 'resources/args')
0029 
0030 DEFAULT_BUILD_ARGS = os.path.join(
0031     CURRENT_HB_DIR, 'resources/args/default/buildargs.json')
0032 DEFAULT_SET_ARGS = os.path.join(
0033     CURRENT_HB_DIR, 'resources/args/default/setargs.json')
0034 DEFAULT_CLEAN_ARGS = os.path.join(
0035     CURRENT_HB_DIR, 'resources/args/default/cleanargs.json')
0036 DEFAULT_ENV_ARGS = os.path.join(
0037     CURRENT_HB_DIR, 'resources/args/default/envargs.json')
0038 DEFAULT_TOOL_ARGS = os.path.join(
0039     CURRENT_HB_DIR, 'resources/args/default/toolargs.json')
0040 
0041 DEFAULT_INDEP_BUILD_ARGS = os.path.join(
0042     CURRENT_HB_DIR, 'resources/args/default/indepbuildargs.json')
0043 
0044 DEFAULT_INSTALL_ARGS = os.path.join(
0045     CURRENT_HB_DIR, 'resources/args/default/installargs.json')
0046 
0047 DEFAULT_PACKAGE_ARGS = os.path.join(
0048     CURRENT_HB_DIR, 'resources/args/default/packageargs.json')
0049 
0050 DEFAULT_PUBLISH_ARGS = os.path.join(
0051     CURRENT_HB_DIR, 'resources/args/default/publishargs.json')
0052 
0053 DEFAULT_UPDATE_ARGS = os.path.join(
0054     CURRENT_HB_DIR, 'resources/args/default/updateargs.json')
0055 
0056 DEFAULT_PUSH_ARGS = os.path.join(
0057     CURRENT_HB_DIR, 'resources/args/default/pushargs.json')
0058 
0059 CURRENT_ARGS_DIR = os.path.join(CURRENT_OHOS_ROOT, 'out/hb_args')
0060 CURRENT_BUILD_ARGS = os.path.join(
0061     CURRENT_ARGS_DIR, 'buildargs.json')
0062 CURRENT_SET_ARGS = os.path.join(
0063     CURRENT_ARGS_DIR, 'setargs.json')
0064 CURRENT_CLEAN_ARGS = os.path.join(
0065     CURRENT_ARGS_DIR, 'cleanargs.json')
0066 CURRENT_ENV_ARGS = os.path.join(
0067     CURRENT_ARGS_DIR, 'envargs.json')
0068 CURRENT_TOOL_ARGS = os.path.join(
0069     CURRENT_ARGS_DIR, 'toolargs.json')
0070 CURRENT_INDEP_BUILD_ARGS = os.path.join(
0071     CURRENT_ARGS_DIR, 'indepbuildargs.json')
0072 CURRENT_INSTALL_ARGS = os.path.join(
0073     CURRENT_ARGS_DIR, 'installargs.json')
0074 CURRENT_PACKAGE_ARGS = os.path.join(
0075     CURRENT_ARGS_DIR, 'packageargs.json')
0076 CURRENT_PUBLISH_ARGS = os.path.join(
0077     CURRENT_ARGS_DIR, 'publishargs.json')
0078 CURRENT_UPDATE_ARGS = os.path.join(
0079     CURRENT_ARGS_DIR, 'updateargs.json')
0080 CURRENT_PUSH_ARGS = os.path.join(
0081     CURRENT_ARGS_DIR, 'pushargs.json')
0082 
0083 BUILD_CONFIG_FILE = os.path.join(
0084     CURRENT_HB_DIR, 'resources/config/config.json')
0085 ROOT_CONFIG_FILE = os.path.join(CURRENT_OHOS_ROOT, 'out/ohos_config.json')
0086 STATUS_FILE = os.path.join(CURRENT_HB_DIR, 'resources/status/status.json')
0087 
0088 ENV_SETUP_FILE = os.path.join(
0089     CURRENT_BUILD_DIR, 'build_scripts', 'env_setup.sh')
0090 
0091 COMPONENTS_PATH_DIR = os.path.join(CURRENT_OHOS_ROOT, 'out/components_path.json')

hb build, hb set, hb clean等参数文件位置和文件名称定义如上
以build为例

  • 默认参数文件位置为:build/hb/resources/args/default/buildargs.json
  • 生效参数文件位置为:out/hb_args/buildargs.json

hb/containers/arg.py

0289     @staticmethod
0290     @throw_exception
0291     def write_args_file(key: str, value, module_type: ModuleType):
0292         args_file_path = ''
0293         if module_type == ModuleType.BUILD:
0294             args_file_path = CURRENT_BUILD_ARGS
0295         elif module_type == ModuleType.SET:
0296             args_file_path = CURRENT_SET_ARGS
0297         elif module_type == ModuleType.CLEAN:
0298             args_file_path = CURRENT_CLEAN_ARGS
0299         elif module_type == ModuleType.ENV:
0300             args_file_path = CURRENT_ENV_ARGS
0301         elif module_type == ModuleType.TOOL:
0302             args_file_path = CURRENT_TOOL_ARGS
0303         elif module_type == ModuleType.INDEP_BUILD:
0304             args_file_path = CURRENT_INDEP_BUILD_ARGS
0305         elif module_type == ModuleType.INSTALL:
0306             args_file_path = CURRENT_INSTALL_ARGS
0307         elif module_type == ModuleType.PACKAGE:
0308             args_file_path = CURRENT_PACKAGE_ARGS
0309         elif module_type == ModuleType.PUBLISH:
0310             args_file_path = CURRENT_PUBLISH_ARGS
0311         elif module_type == ModuleType.UPDATE:
0312             args_file_path = CURRENT_UPDATE_ARGS
0313         elif module_type == ModuleType.PUSH:
0314             args_file_path = CURRENT_PUSH_ARGS
0315         else:
0316             raise OHOSException(
0317                 'You are trying to write args file, but there is no corresponding module "{}" args file'
0318                 .format(module_type), "0002")
0319         args_file = Arg.read_args_file(module_type)
0320         args_file[key]["argDefault"] = value
0321         IoUtil.dump_json_file(args_file_path, args_file)

上述函数根据用户配置的参数值,刷新参数配置文件。

配置对象的初始化

参考build/hb/resolver/set_args_resolver.py的第37行,这里是config对象的首次创建,我们来看看其怎么初始化的
build/hb/resources/config.py

0039 class Config(metaclass=Singleton):
0040     def __init__(self):
0041         self.config_json = ""
0042         self._root_path = ""
0043         self._board = ""
0044         self._kernel = ""
0045         self._product = ""
0046         self._product_path = ""
0047         self._device_path = ""
0048         self._device_company = ""
0049         self._patch_cache = ""
0050         self._version = ""
0051         self._os_level = ""
0052         self._product_json = ""
0053         self._target_os = ""
0054         self._target_cpu = ""
0055         self._out_path = ""
0056         self._compile_config = ""
0057         self._component_type = ""
0058         self._device_config_path = ""
0059         self._product_config_path = ""
0060         self._subsystem_config_json = ""
0061         self._subsystem_config_overlay_json = ""
0062         self._support_cpu = ""
0063         self._log_mode = ""
0064         self.fs_attr = set()
0065         self.platform = platform.system()
0066         self.__post__init()

39行:Config类是一个单实例类,即只会存在一个Config类型的对象
41~64行:初始化大部分值为空
65行:记录当前平台类型,如windows, linux, macos等
66行: 进一步初始化
build/hb/resources/config.py

0068     def __post__init(self):
0069         self.config_json = get_config_path()
0070         config_content = IoUtil.read_json_file(self.config_json)
0071         self.root_path = CURRENT_OHOS_ROOT
0072         self.board = config_content.get('board', None)
0073         self.kernel = config_content.get('kernel', None)
0074         self.product = config_content.get('product', None)
0075         self.product_path = config_content.get('product_path', None)
0076         self.device_path = config_content.get('device_path', None)
0077         self.device_company = config_content.get('device_company', None)
0078         self.patch_cache = config_content.get('patch_cache', None)
0079         self.version = config_content.get('version', '3.0')
0080         self.os_level = config_content.get('os_level', 'small')
0081         self.product_json = config_content.get('product_json', None)
0082         self.target_os = config_content.get('target_os', None)
0083         self.target_cpu = config_content.get('target_cpu', None)
0084         self.out_path = config_content.get('out_path', None)
0085         self.compile_config = config_content.get('compile_config', None)
0086         self.component_type = config_content.get('component_type', None)
0087         self.device_config_path = config_content.get('device_config_path',
0088                                                      None)
0089         self.product_config_path = config_content.get('product_config_path',
0090                                                       None)
0091         self.subsystem_config_json = config_content.get(
0092             'subsystem_config_json', None)
0093         self.support_cpu = config_content.get('support_cpu', None)
0094         self.fs_attr = set()
0095         self.platform = platform.system()
0096         self.precise_branch = ""

69行:获取配置文件的位置
70行:读取配置文件的内容
71~96行:根据配置文件的内容填写相关字段

配置文件位置

build/hb/resources/config.py

0033 def get_config_path():
0034     if not os.path.exists(ROOT_CONFIG_FILE):
0035         IoUtil.copy_file(BUILD_CONFIG_FILE, ROOT_CONFIG_FILE)
0036     return ROOT_CONFIG_FILE

配置文件为out/ohos_config.json
如果文件不存在,则从build/hb/resources/config/config.json拷贝生成

产品配置信息的获取

刚开始时,除了在配置对象中设置了源码根目录的位置,其它数据都是None, 那么某产品(如rk3568)的实际配置是怎么获取到的呢
让我们回到build/hb/main.py第102行,这里会刷新产品信息
build/hb/main.py

0102             ohos_set_module.set_product()

然后我们看看set_product的实现
build/​hb/​modules/​ohos_set_module.py

0049     def set_product(self):
0050         self.args_resolver.resolve_arg(self.args_dict['product_name'], self)

进一步查看对应的resolve_arg函数
​build/​hb/​resolver/​set_args_resolver.py

0035     @staticmethod
0036     def resolve_product_name(target_arg: Arg, set_module: SetModuleInterface):
0037         config = Config()
0038         product_info = dict()
0039         device_info = dict()
0040         if target_arg.arg_value == '':
0041             product_info = set_module._menu.select_product()
0042         elif target_arg.arg_value.__contains__('@'):
0043             product_name, company_name = target_arg.arg_value.split('@', 2)
0044             product_info = ProductUtil.get_product_info(
0045                 product_name, company_name)
0046         else:
0047             product_info = ProductUtil.get_product_info(target_arg.arg_value)
0048 
0049         config.product = product_info.get('name')
0050         config.product_path = product_info.get('product_path')
0051         config.version = product_info.get('version')
0052         config.os_level = product_info.get('os_level')
0053         config.product_json = product_info.get('config')
0054         config.component_type = product_info.get('component_type')
0055         if product_info.get('product_config_path'):
0056             config.product_config_path = product_info.get(
0057                 'product_config_path')
0058         else:
0059             config.product_config_path = product_info.get('path')
0060 
0061         device_info = ProductUtil.get_device_info(config.product_json)
0062         config.board = device_info.get('board')
0063         config.kernel = device_info.get('kernel')
0064         config.target_cpu = device_info.get('target_cpu')
0065         config.target_os = device_info.get('target_os')
0066         config.support_cpu = device_info.get("support_cpu")
0067         kernel_version = device_info.get('kernel_version')
0068         config.device_company = device_info.get('company')
0069         board_path = device_info.get('board_path')
0070 
0071         if product_info.get('build_out_path'):
0072             config.out_path = os.path.join(config.root_path,
0073                                            product_info.get('build_out_path'))
0074         else:
0075             if config.os_level == 'standard':
0076                 config.out_path = os.path.join(config.root_path, 'out',
0077                                                config.board)
0078             else:
0079                 config.out_path = os.path.join(config.root_path, 'out',
0080                                                config.board, config.product)
0081 
0082         if product_info.get('subsystem_config_json'):
0083             config.subsystem_config_json = product_info.get(
0084                 'subsystem_config_json')
0085         else:
0086             config.subsystem_config_json = 'build/subsystem_config.json'
0087 
0088         subsystem_config_overlay_path = os.path.join(
0089             config.product_path, 'subsystem_config_overlay.json')
0090         if os.path.isfile(subsystem_config_overlay_path):
0091             if product_info.get('subsystem_config_overlay_json'):
0092                 config.subsystem_config_overlay_json = product_info.get(
0093                     'subsystem_config_overlay_json')
0094             else:
0095                 config.subsystem_config_overlay_json = subsystem_config_overlay_path
0096 
0097         if config.version == '2.0':
0098             config.device_path = board_path
0099         else:
0100             if config.os_level == "standard":
0101                 config.device_path = board_path
0102             else:
0103                 config.device_path = DeviceUtil.get_device_path(
0104                     board_path, config.kernel, kernel_version)
0105 
0106         if device_info.get('board_config_path'):
0107             config.device_config_path = device_info.get('board_config_path')
0108         else:
0109             config.device_config_path = config.device_path
0110 
0111         Arg.write_args_file(target_arg.arg_name,
0112                             product_info.get('name'), ModuleType.BUILD)
0113         Arg.write_args_file(target_arg.arg_name,
0114                             f"{product_info.get('name')}@{product_info.get('company')}", ModuleType.SET)

41行:产品名称为空,则弹出菜单让用户选择产品并获取产品配置
43行:产品名称含@符号,则用公司名和产品名一起查找产品配置
47行:直接用产品名查找产品配置。
无论用公司名和产品名一起查找,还是只用产品名查找,都是从vendor目录下去搜索(非内置产品),具体实现请读者参考build/hb/util/product_util.py
49~59行:根据产品配置刷新config对象
61行:获取设备配置信息
62~69行:根据设备配置信息刷新config对象
71~80行:设置编译构建的输出目录
82~95行:与产品相关的子系统配置信息
97~109行:设备路径和设备配置路径信息
111~114行:将产品名称记录到set和build对应的参数文件中

这里需要注意设备和产品的概念,理论上可以基于设备进行二次开发,形成不同的产品。所以设备和产品的关系可以是一对多的关系。这里的术语设备,有时我们也称为开发板。
至此,config对象的主要信息获取到了。
这里也许你会有一个疑惑,命令行参数product_name和处理函数resolve_product_name是怎么对应上的,这里给出解答
build/​hb/​resources/​args/​default/​setargs.json

0002     "product_name": {
0003         "arg_name": "--product-name",
0004         "argDefault": "",
0005         "arg_help": "Default:''. Help:build a specified product. You could use this option like this: 1.'hb set --product-name rk3568@hihope' 2.'hb set --product-name rk3568' 3.'hb set'[graphical ui]",
0006         "arg_phase": "prebuild",
0007         "arg_type": "str",
0008         "arg_attribute": {
0009             "abbreviation": "-p"
0010         },
0011         "resolve_function": "resolve_product_name",
0012         "testFunction": "testProductName"
0013     },

可以从11行看到product_name的处理函数为resolve_product_name。
build/hb/resolver/interface/args_resolver_interface.py

0031     @throw_exception
0032     def resolve_arg(self, target_arg: Arg, module):
0033         if target_arg.arg_name not in self._args_to_function.keys():
0034             raise OHOSException(
0035                 'You are tring to call {} resolve function, but it has not been defined yet', '0000')
0036         if not hasattr(self._args_to_function[target_arg.arg_name], '__call__'):
0037             raise OHOSException()
0038 
0039         resolve_function = self._args_to_function[target_arg.arg_name]
0040         return resolve_function(target_arg, module)
0041 
0042     @throw_exception
0043     def _map_args_to_function(self, args_dict: dict):
0044         for entity in args_dict.values():
0045             if isinstance(entity, Arg):
0046                 args_name = entity.arg_name
0047                 function_name = entity.resolve_function
0048                 if not hasattr(self, function_name) or \
0049                         not hasattr(self.__getattribute__(function_name), '__call__'):
0050                     raise OHOSException(
0051                         'There is no resolution function for arg: {}'.format(
0052                             args_name),
0053                         "0004")
0054                 entity.resolve_function = self.__getattribute__(function_name)
0055                 self._args_to_function[args_name] = self.__getattribute__(
0056                     function_name)

这里可以洞见,通用的resolve_arg函数怎么调用到具体的与参数对应的解析函数上的
39行:通过参数名称查询对应的解析函数
40行:调用对应的解析函数
那么名称和解析函数映射表是怎么建立的呢,请看43~56行代码
44行:遍历所有的参数对象
46~47行:分别获取参数名和解析函数名称
48行:判断此名称的函数是否存在
54~55行:如果存在则取出函数对象,再建立函数对象与参数名称的映射

0025 class ArgsResolverInterface(metaclass=ABCMeta):
0026 
0027     def __init__(self, args_dict: dict):
0028         self._args_to_function = dict()
0029         self._map_args_to_function(args_dict)

如上所示,解析器对象(子类)初始化的时候,就把参数名与函数映射表建立好了。
此时,config对象的信息也写入配置文件了,因为config对象中可以配置的属性基本都有一个setter方法和一个property方法,下面举个例子
build/hb/resources/config.py

0185     @property
0186     def device_company(self):
0187         if self._device_company is None:
0188             raise OHOSException('Failed to init compile config', '0019')
0189         return self._device_company
0190 
0191     @device_company.setter
0192     def device_company(self, value: str):
0193         self._device_company = value
0194         self.config_update('device_company', self._device_company)

如上。setter方法用于修改device_company属性时,property方法用于读取device_company属性时。而config_update会刷新配置文件
build/hb/resources/config.py

0399     def config_update(self, key: str, value: str):
0400         config_content = IoUtil.read_json_file(self.config_json)
0401         config_content[key] = value
0402         IoUtil.dump_json_file(self.config_json, config_content)

因此out/ohos_config.json会得到及时的刷新

总结

本阶段会生成out/ohos_config.json和out/hb_args的内容

prebuild

build/hb/modules/ohos_build_module.py

0073     def _prebuild(self):
0074         self._run_phase(BuildPhase.PRE_BUILD)
0111     def _run_phase(self, phase: BuildPhase):
0112         '''Description: Traverse all registered parameters in build process and 
0113             execute the resolver function of the corresponding phase
0114         @parameter: [phase]:  Build phase corresponding to parameter
0115         @return :none
0116         '''
0117         for phase_arg in [arg for arg in self.args_dict.values()if arg.arg_phase == phase]:
0118             self.args_resolver.resolve_arg(phase_arg, self)

刚开始遍历执行参数中为prebuild的阶段的这些处理函数,根据配置文件查询,这些函数有
build/​hb/​resources/​args/​default/​buildargs.json

0019     "resolve_function": "resolve_target_cpu",
0034     "resolve_function": "resolve_target_os",
0046     "resolve_function": "resolve_product",
0056     "resolve_function": "resolve_rename_last_log",
0071     "resolve_function": "resolve_log_mode",
0081     "resolve_function": "resolve_precise_branch",
0091     "resolve_function": "resolve_ccache",
0101     "resolve_function": "resolve_xcache",
0111     "resolve_function": "resolve_pycache",
0147     "resolve_function": "resolve_build_target",
0157     "resolve_function": "resolve_ninja_args",
0169     "resolve_function": "resolve_full_compilation",

还有几个参数标记成deprecated(过期)状态,这里没有列出

目标OS解析

build/hb/resolver/build_args_resolver.py

0135     @staticmethod
0136     def resolve_target_os(target_arg: Arg, build_module: BuildModuleInterface):
0137         """resolve '--target-os' arg.
0138         :param target_arg: arg object which is used to get arg value.
0139         :param build_module [maybe unused]: build module object which is used to get other services.
0140         :phase: prebuild.
0141         """
0142         config = Config()
0143         default_build_args = IoUtil.read_json_file(DEFAULT_BUILD_ARGS)
0144         if config.target_os == "":
0145             config.target_os = target_arg.arg_value
0146         elif target_arg.arg_value != default_build_args.get("target_os").get("argDefault"):
0147             config.target_os = target_arg.arg_value

记录用户配置的target_os选项,大多数情况已经在产品config.json中指定好,并且定义成ohos,不需要用户再配置

重命名上次记录的日志文件

build/hb/resolver/build_args_resolver.py

0246     @staticmethod
0247     def resolve_rename_last_log(target_arg: Arg, build_module: BuildModuleInterface):
0248         """resolve '--rename-last-log' arg
0249         :param target_arg: arg object which is used to get arg value.
0250         :param build_module [maybe unused]: build module object which is used to get other services.
0251         :phase: prebuild.
0252         """
0253         if target_arg.arg_value:
0254             config = Config()
0255             out_path = config.out_path
0256             logfile = os.path.join(out_path, 'build.log')
0257             if os.path.exists(logfile):
0258                 mtime = os.stat(logfile).st_mtime
0259                 rename_file(logfile, '{}/build.{}.log'.format(out_path, mtime))

将日志文件备份(重命名)
下次记录日志时会新建一个文件
重命名日志文件时,在文件名中嵌入文件最后一次修改时间,这样可以多次备份日志文件。
这个参数默认打开,一般不需要处理

设置日志模式

build/hb/resolver/build_args_resolver.py

0261     @staticmethod
0262     def resolve_log_mode(target_arg: Arg, build_module: BuildModuleInterface):
0263         """resolve '--log-mode' arg
0264         :param target_arg: arg object which is used to get arg value.
0265         :param build_module: build module object which is used to get other services.
0266         :phase: prebuild.
0267         """
0268         if target_arg.arg_value:
0269             config = Config()
0270             config.log_mode = target_arg.arg_value

记录日志模式,分为常规模式和安静模式。默认常规模式。
安静模式下,主要执行过程由文字[‘-’,‘|’,‘/’,‘\’] 4个符号形成动画表示还在继续运行。

测试部件集名称

build/hb/resolver/build_args_resolver.py

0149     @staticmethod
0150     def resolve_precise_branch(target_arg: Arg, build_module: BuildModuleInterface):
0151         """resolve '--precise-branch' arg.
0152         :param target_arg: arg object which is used to get arg value.
0153         :param build_module [maybe unused]: build module object which is used to get other services.
0154         :phase: prebuild.
0155         """
0156         config = Config()
0157         default_build_args = IoUtil.read_json_file(DEFAULT_BUILD_ARGS)
0158         if config.precise_branch == "":
0159             config.precise_branch = target_arg.arg_value
0160         elif target_arg.arg_value != default_build_args.get("precise_branch").get("argDefault"):
0161             config.precise_branch = target_arg.arg_value

在产品矩阵中指定测试部件集名称,方便编译对应的测试用例。产品矩阵位于manifest仓库。文件名称为matrix_product.csv。

xcache处理

build/hb/resolver/build_args_resolver.py

0326     @staticmethod
0327     def resolve_xcache(target_arg: Arg, build_module: BuildModuleInterface):
0328         """resolve '--xcache' arg
0329         :param target_arg: arg object which is used to get arg value.
0330         :param build_module [maybe unused]: build module object which is used to get other services.
0331         :phase: prebuild.
0332         """
0333         if target_arg.arg_value:
0334             config = Config()
0335             xcache_path = "/opt/buildtools/nextbuild/xcache"
0336             if not os.path.exists(xcache_path):
0337                 LogUtil.hb_warning('Failed to find xcache, xcache disabled.')
0338                 return
0339             else:
0340                 target_generator = build_module.target_generator
0341                 target_generator.regist_arg(
0342                     'ohos_build_enable_xcache', target_arg.arg_value)
0343                 os.environ['XCACHE_EXEC'] = xcache_path
0344                 os.environ['USE_XCACHE'] = '1'

记录xcache选项, xcache与ccache类似,用于缓存首次编译结果,加快后续编译进度(即便删除了.o文件, 但cache中缓存了原来的文件)。当前主要使用ccache。

产品名称解析,

build/hb/resolver/build_args_resolver.py

0059     @staticmethod
0060     def resolve_product(target_arg: Arg, build_module: BuildModuleInterface):
0061         """resolve '--product-name' arg.
0062         :param target_arg: arg object which is used to get arg value.
0063         :param build_module [maybe unused]: build module object which is used to get other services.
0064         :phase: prebuild.
0065         """
0066         config = Config()
0067         target_generator = build_module.target_generator
0068         target_generator.regist_arg('product_name', config.product)
0069         target_generator.regist_arg('product_path', config.product_path)
0070         target_generator.regist_arg(
0071             'product_config_path', config.product_config_path)
0072 
0073         target_generator.regist_arg('device_name', config.board)
0074         target_generator.regist_arg('device_path', config.device_path)
0075         target_generator.regist_arg('device_company', config.device_company)
0076         target_generator.regist_arg(
0077             'device_config_path', config.device_config_path)
0078 
0079         target_generator.regist_arg('target_cpu', config.target_cpu)
0080         target_generator.regist_arg('precise_branch', config.precise_branch)
0081         target_generator.regist_arg(
0082             'is_{}_system'.format(config.os_level), True)
0083 
0084         target_generator.regist_arg('ohos_kernel_type', config.kernel)
0085         target_generator.regist_arg('ohos_build_compiler_specified',
0086                                     ProductUtil.get_compiler(config.device_path))
0087 
0088         target_generator.regist_arg('ohos_build_time',
0089                                     SystemUtil.get_current_time(time_type='timestamp'))
0090         target_generator.regist_arg('ohos_build_datetime',
0091                                     SystemUtil.get_current_time(time_type='datetime'))
0092 
0093         features_dict = ProductUtil.get_features_dict(config.product_json)
0094         for key, value in features_dict.items():
0095             target_generator.regist_arg(key, value)
0096 
0097         if ProductUtil.get_compiler(config.device_path) == 'clang':
0098             target_generator.regist_arg(
0099                 'ohos_build_compiler_dir', config.clang_path)
0100 
0101         if target_arg.arg_value == 'ohos-sdk':
0102             target_generator = build_module.target_generator
0103             target_generator.regist_arg('build_ohos_sdk', True)
0104             target_generator.regist_arg('build_ohos_ndk', True)
0105             target_generator.regist_arg('enable_enhanced_opt', False)
0106             if len(build_module.args_dict['build_target'].arg_value) == 0:
0107                 build_module.args_dict['build_target'].arg_value = [
0108                     'build_ohos_sdk']
0109             build_module.args_dict['target_cpu'].arg_value = 'arm64'
0110         elif target_arg.arg_value == 'arkui-x':
0111             target_generator = build_module.target_generator
0112             target_generator.regist_arg('is_arkui_x', True)
0113             target_generator.regist_arg('enable_ng_build', True)
0114             target_generator.regist_arg('is_component_build', False)
0115             target_generator.regist_arg('use_musl', False)
0116             target_generator.regist_arg('is_use_check_deps', False)
0117             if len(build_module.args_dict['build_target'].arg_value) == 0:
0118                 build_module.args_dict['build_target'].arg_value = [
0119                     'arkui_targets']

66~86行:将config配置对象的数据读出并记录到target_generator对象中(后续gn程序会使用到),这些config配置对象在之前hb set 处理中已设置好。
93~95行:读取产品对应的特性配置,对于rk3568来说,其在vendor/hihope/rk3568/config.json和其包含的inherit文件中,并记录下来
97~99行:读取并记录clang编译器的目录位置
101~109行:如果编译的产品是openharmony sdk, 则需要记录一些特殊的参数, 这个是用来编译全量SDK的,系统类APP需要使用到。需要注意,当前指定的SDK的目标CPU是arm64。
110~119行:如果编译的产品是openharmony arkui-x, 则需要记录一些特殊的参数,这个未来再研究。

目标CPU解析

build/hb/resolver/build_args_resolver.py

0121     @staticmethod
0122     def resolve_target_cpu(target_arg: Arg, build_module: BuildModuleInterface):
0123         """resolve '--target-cpu' arg.
0124         :param target_arg: arg object which is used to get arg value.
0125         :param build_module [maybe unused]: build module object which is used to get other services.
0126         :phase: prebuild.
0127         """
0128         config = Config()
0129         default_build_args = IoUtil.read_json_file(DEFAULT_BUILD_ARGS)
0130         if config.target_cpu == "":
0131             config.target_cpu = target_arg.arg_value
0132         elif target_arg.arg_value != default_build_args.get("target_cpu").get("argDefault"):
0133             config.target_cpu = target_arg.arg_value

target_cpu表示此代码最终编译出来后在什么cpu架构上执行。
对于./build.py -p rk3568场景
由于hb set逻辑已经设置了target_cpu所以,这里if条件不满足
132~133行:用户指定了target_cpu参数,且与默认值不同,则更新一下。
编译产品时,应该不需要这个参数,因为vendor目录下的config.json文件中指定了这个参数,只要指定了产品,那么产品对应的cpu肯定也清楚了。

ccache参数解析

build/hb/resolver/build_args_resolver.py

0272     @staticmethod
0273     def resolve_ccache(target_arg: Arg, build_module: BuildModuleInterface):
0274         """resolve '--ccache' arg
0275         :param target_arg: arg object which is used to get arg value.
0276         :param build_module [maybe unused]: build module object which is used to get other services.
0277         :phase: prebuild.
0278         """
0279         if target_arg.arg_value:
0280             config = Config()
0281             ccache_path = find_executable('ccache')
0282             if ccache_path is None:
0283                 LogUtil.hb_warning('Failed to find ccache, ccache disabled.')
0284                 return
0285             else:
0286                 target_generator = build_module.target_generator
0287                 target_generator.regist_arg(
0288                     'ohos_build_enable_ccache', target_arg.arg_value)
0289 
0290             ccache_local_dir = os.environ.get('CCACHE_LOCAL_DIR')
0291             ccache_base = os.environ.get('CCACHE_BASE')
0292             if not ccache_local_dir:
0293                 ccache_local_dir = '.ccache'
0294             if not ccache_base:
0295                 ccache_base = os.environ.get('HOME')
0296             ccache_base = os.path.join(ccache_base, ccache_local_dir)
0297             if not os.path.exists(ccache_base):
0298                 os.makedirs(ccache_base, exist_ok=True)
0299 
0300             ccache_log_suffix = os.environ.get('CCACHE_LOG_SUFFIX')
0301             if ccache_log_suffix:
0302                 logfile = os.path.join(
0303                     ccache_base, "ccache.{}.log".format(ccache_log_suffix))
0304             else:
0305                 logfile = os.path.join(ccache_base, "ccache.log")
0306             if os.path.exists(logfile):
0307                 oldfile = os.path.join(ccache_base, '{}.old'.format(logfile))
0308                 if os.path.exists(oldfile):
0309                     os.unlink(oldfile)
0310                 rename_file(logfile, oldfile)
0311 
0312             os.environ['CCACHE_EXEC'] = ccache_path
0313             os.environ['CCACHE_LOGFILE'] = logfile
0314             os.environ['USE_CCACHE'] = '1'
0315             os.environ['CCACHE_DIR'] = ccache_base
0316             os.environ['CCACHE_UMASK'] = '002'
0317             os.environ['CCACHE_BASEDIR'] = config.root_path
0318             ccache_max_size = os.environ.get('CCACHE_MAXSIZE')
0319             if not ccache_max_size:
0320                 ccache_max_size = '100G'
0321 
0322             cmd = ['ccache', '-M', ccache_max_size]
0323 
0324             SystemUtil.exec_command(cmd, log_path=config.log_path)

ccache是一个工具,可以缓存C/C++编译过程的中间结果,这样下次编译的时候可以从中间结果继续往下执行,加快编译速度。
本段代码处理ccache功能打开或关闭的逻辑。当前此功能默认打开。
281行:查找ccache工具程序路径
286~288行:记录ccache功能开关
290~298行:查找或创建ccache输出文件夹
300~310行:ccache功能日志文件创建,并备份原始日志文件
203~208行:将ccache一些参数记入环境变量
209~215行:设置ccache的缓冲区尺寸

pycache参数解析

pycache是一个可以加快python程序性能的工具,默认是关闭的
build/hb/resolver/build_args_resolver.py

0346     @staticmethod
0347     def resolve_pycache(target_arg: Arg, build_module: BuildModuleInterface):
0348         """resolve '--enable-pycache' arg
0349         :param target_arg: arg object which is used to get arg value.
0350         :param build_module [maybe unused]: build module object which is used to get other services.
0351         :phase: prebuild.
0352         """
0353         if target_arg.arg_value:
0354             config = Config()
0355             pycache_dir = os.environ.get('CCACHE_BASE')
0356             # The default value is HOME for local users
0357             if not pycache_dir:
0358                 pycache_dir = os.environ.get('HOME')
0359             pycache_dir = os.path.join(pycache_dir, '.pycache')
0360             os.environ['PYCACHE_DIR'] = pycache_dir
0361             pyd_start_cmd = [
0362                 'python3',
0363                 '{}/build/scripts/util/pyd.py'.format(config.root_path),
0364                 '--root',
0365                 pycache_dir,
0366                 '--start',
0367             ]
0368             cmd = ['/bin/bash', '-c', ' '.join(pyd_start_cmd), '&']
0369             subprocess.Popen(cmd)

353行:如果开启了pycache功能,则执行后面的逻辑
主要目标就是执行build/scripts/util/pyd.py脚本,此脚本不做分析,读者自己阅读代码即可。

构建目标解析

操作系统编译构建可以有很多目标,如果不做指定,则会是整个操作系统镜像(非常大),也可以指定成一些具体的软件模块(相对较小)-比如二进制程序或者共享库等。可以通过hb tool或out/product_name/build.ninja查看当前支持哪些构建目标。
build/hb/resolver/build_args_resolver.py

0202     @staticmethod
0203     @throw_exception
0204     def resolve_build_target(target_arg: Arg, build_module: BuildModuleInterface):
0205         """resolve '--build-target' arg.
0206         :param target_arg: arg object which is used to get arg value.
0207         :param build_module [maybe unused]: build module object which is used to get other services.
0208         :phase: prebuild.
0209         :raise OHOSException: when build target not exist in compiling product.
0210         """
0211         config = Config()
0212         build_executor = build_module.target_compiler
0213         target_list = []
0214         test_target_list = ['build_all_test_pkg', 'package_testcase', 'package_testcase_mlf']
0215         if len(target_arg.arg_value):
0216             for target_name in target_arg.arg_value:
0217                 if target_name.endswith('make_test') or target_name.split(':')[-1] in test_target_list:
0218                     target_generator = build_module.target_generator
0219                     target_generator.regist_arg('use_thin_lto', False)
0220                     target_list.append(target_name)
0221                 elif target_name.startswith('TDD'):
0222                     target_list.extend(BuildArgsResolver.get_tdd_build_target(target_name, build_module))
0223                 else:
0224                     target_list.append(target_name)
0225         else:
0226             if os.getcwd() == CURRENT_OHOS_ROOT:
0227                 target_list = ['images']
0228             elif ComponentUtil.is_in_component_dir(os.getcwd()) and \
0229                     ComponentUtil.is_component_in_product(
0230                     ComponentUtil.get_component_name(os.getcwd()), Config().product):
0231                 component_name = ComponentUtil.get_component_name(os.getcwd())
0232                 LogUtil.write_log(Config().log_path, 'In the component "{}" directory,'
0233                                   'this compilation will compile only this component'.format(
0234                                       component_name),
0235                                   'warning')
0236                 target_list.append(component_name)
0237                 target_list.append(component_name + '_test')
0238             else:
0239                 component_name = ComponentUtil.get_component_name(os.getcwd())
0240                 component_name = os.path.basename(
0241                     os.getcwd()) if component_name == '' else component_name
0242                 raise OHOSException('There is no target component "{}" for the current product "{}"'
0243                                     .format(component_name, Config().product), "4001")
0244         build_executor.regist_arg('build_target', target_list)

215行,如果参数指明了构建目标,
216~222行, 如果目标是测试相关的处理
223~224行,普通目标直接记录下来
226~227行,当前运行在代码根目录,且没有指定目标,则直接生成系统镜像
228~237行,如果运行在某组件目录,且此产品含有这个组件,则构建对应的组件
238~243行,组件不属于产品,报错提示
244行, 记录下构建目标列表,本参数是一个列表类型参数,可以直接指定多个编译构建目标

ninja配置参数解析

build/hb/resolver/build_args_resolver.py

0440     @staticmethod
0441     @throw_exception
0442     def resolve_ninja_args(target_arg: Arg, build_module: BuildModuleInterface):
0443         """resolve '--ninja-args' arg
0444         :param target_arg: arg object which is used to get arg value.
0445         :param build_module [maybe unused]: build module object which is used to get other services.
0446         :phase: prebuild.
0447         :raise OHOSException: when the value of the ninja parameter does not use quotation marks.
0448         """
0449         build_executor = build_module.target_compiler
0450         ninja_args_list = []
0451         for ninja_arg in target_arg.arg_value:
0452             ninja_arg = re.sub("'", "", ninja_arg)
0453             ninja_args_list.append(ninja_arg)
0454         build_executor.regist_arg('ninja_args', ninja_args_list)

主要就是把所有额外添加的单引号删除掉,回归ninja参数本来面目
python程序指定ninja参数时,为了方便处理,针对ninjia参数额外加了单引号,传递给ninja前,去除单引号。

全量编译开关

build/hb/resolver/build_args_resolver.py

0371     @staticmethod
0372     def resolve_full_compilation(target_arg: Arg, build_module: BuildModuleInterface):
0373         """resolve '--full-compilation' arg
0374         :param target_arg: arg object which is used to get arg value.
0375         :param build_module [maybe unused]: build module object which is used to get other services.
0376         :phase: prebuild.
0377         """
0378         if target_arg.arg_value:
0379             build_executor = build_module.target_compiler
0380             target_list = build_executor.args_dict.get('build_target', None)
0381             if isinstance(target_list, list):
0382                 target_list.append('make_all')
0383                 target_list.append('make_test')
0384             else:
0385                 build_executor.regist_arg(
0386                     'build_target', ['make_all', 'make_test'])
0387             target_generator = build_module.target_generator
0388             target_generator.regist_arg('use_thin_lto', False)

在已有构建目标的基础上,增加全量构建目标,即会构建整个系统镜像。这个时候也没有必要指定build-target参数。

总结

本阶段主要准备target generator参数,

preload

build/hb/modules/ohos_build_module.py

0075     def _preload(self):
0076         self._run_phase(BuildPhase.PRE_LOAD)
0077         if self.args_dict.get('fast_rebuild', None) and not self.args_dict.get('fast_rebuild').arg_value:
0078             self.preloader.run()

76行:先运行preload。
77~78行:如果开启了fast_rebuild,则会跳过preloader。
目前在buildarg json文件中没有查询到preload阶段的参数,所以,76行代码为空操作
对于preloader, 继续查看如下代码
build/hb/services/interface/preload_interface.py

0045     def run(self):
0046         self.__post_init__()
0047         self._generate_build_prop()
0048         self._generate_build_config_json()
0049         self._generate_parts_json()
0050         self._generate_parts_config_json()
0051         self._generate_build_gnargs_prop()
0052         self._generate_features_json()
0053         self._generate_syscap_json()
0054         self._generate_exclusion_modules_json()
0055         self._generate_platforms_build()
0056         self._generate_subsystem_config_json()
0057         self._generate_systemcapability_json()
0058         self._generate_compile_standard_whitelist_json()
0059         self._generate_compile_env_allowlist_json()

可以看出,其主要作用是生成一系列的json文件,接下来逐一进行描述

preloader初始化

build/hb/services/preloader.py

0030     def __init__(self):
0031         super().__init__()
0032         self._dirs = ""
0033         self._outputs = ""
0034         self._product = ""
0035         self._os_level = ""
0036         self._target_cpu = ""
0037         self._target_os = ""
0038         self._toolchain_label = ""
0039         self._subsystem_info = {}
0040         self._all_parts = {}
0041         self._build_vars = {}
0042         self._compile_standard_whitelist_info = {}
0043         self._compile_env_allowlist_info = {}
0042 
0045     def __post_init__(self):
0046         self._dirs = Dirs(self._config)
0047         self._outputs = Outputs(self._dirs.preloader_output_dir)
0048         self._product = Product(self._dirs, self._config)
0049         self._all_parts = self._product._parts
0050         self._build_vars = self._product._build_vars
0051         self._os_level = self._build_vars.get('os_level')
0052         self._target_os = self._build_vars.get('target_os')
0053         self._target_cpu = self._build_vars.get('target_cpu')
0054         self._toolchain_label = self._build_vars.get('product_toolchain_label')
0055         self._subsystem_info = self._get_org_subsystem_info()
0056         self._compile_standard_whitelist_info = self._get_compile_standard_whitelist_info()
0057         self._compile_env_allowlist_info = self._get_compile_env_allowlist_info()
0285     def _get_org_subsystem_info(self) -> dict:
0286         subsystem_info = {}
0287         if self._os_level == "standard":
0288             subsystem_info = IoUtil.read_json_file(
0289                 self._dirs.subsystem_config_json)
0290         elif self._os_level == "mini" or self._os_level == "small":
0291             ohos_build_output_dir = os.path.join(self._dirs.preloader_output_dir,
0292                                                  '{}_system'.format(self._os_level))
0293             subsystem_info = parse_lite_subsystem_config(
0294                 self._dirs.lite_components_dir, ohos_build_output_dir,
0295                 self._dirs.source_root_dir, self._dirs.subsystem_config_json)
0296         return subsystem_info

preloader初始化时,__init__和__post_init__会依次调用。
46行: 记录一些输入目录和文件的路径
47行:记录需要输出的一些文件路径名
48行:记录下产品相关的信息,并解析
49~57行:记录下其它信息

记录输入目录和文件

build/hb/util/preloader/preloader_process_data.py

0052 class Dirs:
0053 
0054     def __init__(self, config):
0055         self.__post_init__(config)
0056 
0057     def __post_init__(self, config):
0058         self.source_root_dir = config.root_path
0059         self.built_in_product_dir = config.built_in_product_path
0060         self.productdefine_dir = os.path.join(
0061             self.source_root_dir, 'productdefine/common')
0062         self.built_in_base_dir = os.path.join(self.productdefine_dir, 'base')
0063 
0064         # Configs of vendor specified products are stored in ${vendor_dir} directory.
0065         self.vendor_dir = config.vendor_path
0066         # Configs of device specified products are stored in ${device_dir} directory.
0067         self.device_dir = os.path.join(config.root_path, 'device')
0068 
0069         self.subsystem_config_json = os.path.join(
0070             config.root_path, config.subsystem_config_json)
0071         self.subsystem_config_overlay_json = os.path.join(config.product_path,
0072                                                           'subsystem_config_overlay.json')
0073         self.lite_components_dir = os.path.join(
0074             config.root_path, 'build/lite/components')
0075 
0076         self.preloader_output_dir = os.path.join(
0077             config.root_path, 'out/preloader', config.product)

58行:记录源码根目录
59行:记录内置产品目录
60~61行:记录产品定义目录为源码根目录/productdefine/common
62行:记录内置产品基础定义目录为源码根目录/productdefine/common/base
65行:记录vendor目录
67行:记录device目录为源码根目录/device目录
69~70行:记录子系统配置文件为源码根目录/build/subsystem_config.json文件
71~72行:记录子系统修正配置文件为产品目录/subsystem_config_overlay.json文件
73~74行:记录轻量系统组件目录为源码根目录/build/lite/componenets
76~77行:记录preloader阶段产生的输出文件的目录为源码根目录/out/preloader目录
这里有一个知识点: overlay中的配置信息优先级高于默认的subsystem_config.json信息。

记录各输出文件的路径

build/hb/util/preloader/preloader_process_data.py

0025 class Outputs:
0026 
0027     def __init__(self, output_dir):
0028         self.__post_init__(output_dir)
0029 
0030     def __post_init__(self, output_dir):
0031         os.makedirs(output_dir, exist_ok=True)
0032         self.build_prop = os.path.join(output_dir, 'build.prop')
0033         self.build_config_json = os.path.join(output_dir, 'build_config.json')
0034         self.parts_json = os.path.join(output_dir, 'parts.json')
0035         self.parts_config_json = os.path.join(output_dir, 'parts_config.json')
0036         self.build_gnargs_prop = os.path.join(output_dir, 'build_gnargs.prop')
0037         self.features_json = os.path.join(output_dir, 'features.json')
0038         self.syscap_json = os.path.join(output_dir, 'syscap.json')
0039         self.exclusion_modules_json = os.path.join(output_dir,
0040                                                    'exclusion_modules.json')
0041         self.subsystem_config_json = os.path.join(output_dir,
0042                                                   'subsystem_config.json')
0043         self.subsystem_config_overlay_json = os.path.join(output_dir,
0044                                                   'subsystem_config_overlay.json')
0045         self.platforms_build = os.path.join(output_dir, 'platforms.build')
0046         self.systemcapability_json = os.path.join(
0047             output_dir, 'SystemCapability.json')
0048         self.compile_standard_whitelist_json = os.path.join(output_dir, 'compile_standard_whitelist.json')
0049         self.compile_env_allowlist_json = os.path.join(output_dir, 'compile_env_allowlist.json')

31行:创建输出目录
32~47行:记录各输出文件的路径

记录产品相关信息

build/hb/util/preloader/preloader_process_data.py

0080 class Product():
0081 
0082     def __init__(self, config_dirs: Dirs, ohos_config: Config):
0083         self._ohos_config = None
0084         self._dirs = None
0085         self._name = ""
0086         self._config = {}
0087         self._build_vars = {}
0088         self._parts = {}
0089         self._syscap_info = {}
0090         self._device_name = ""
0091         self._device_info = {}
0092         self._config_file = ""
0093         self._version = ''
0094         self.__post_init__(config_dirs, ohos_config)
0095 
0096     def __post_init__(self, config_dirs: Dirs, config: Config):
0097         self._ohos_config = config
0098         self._dirs = config_dirs
0099         self._name = config.product
0100         self._config_file = config.product_json
0101         self._config = self._get_full_product_config()
0102         self._version = self._config.get('version', '3.0')
0103         self._do_parse()
0104 
0105     # parse product configuration, then generate parts list and build vars
0106     def _do_parse(self):
0107         self._update_syscap_info()
0108         self._update_device()
0109         self._update_parts()
0110         self._update_build_vars()
0111         self._remove_excluded_components()

107~108行:解析产品信息
109行:生成部件列表信息
110行:生成build var列表
111行:移除不需要的组件

记录系统能力信息

build/hb/util/preloader/preloader_process_data.py

0115     # Update the syscap info
0116     def _update_syscap_info(self):
0117         product_name = self._config.get('product_name')
0118         if product_name is None:
0119             product_name = ""
0120         os_level = self._config.get('type')
0121         if os_level is None:
0122             os_level = ""
0123         api_version = self._config.get('api_version')
0124         if api_version is None:
0125             api_version = 0
0126         manufacturer_id = self._config.get('manufacturer_id')
0127         if manufacturer_id is None:
0128             manufacturer_id = 0
0129         self._syscap_info = {'product': product_name, 'api_version': api_version,
0130                              'system_type': os_level, 'manufacturer_id': manufacturer_id}

主要记录一些基础信息,如果没有配置,则记录成空字符串或0。

记录设备信息

build/hb/util/preloader/preloader_process_data.py

0132     # Update the _device_name and _device_info based on the product configuration in the vendor warehouse
0133     def _update_device(self):
0134         if self._version == "2.0":
0135             device_name = self._config.get('product_device')
0136             if device_name:
0137                 self._device_name = device_name
0138                 self._device_info = self._get_device_info_v2(
0139                     device_name, self._dirs.built_in_device_dir)
0140         else:
0141             device_name = self._config.get('board')
0142             if device_name:
0143                 self._device_name = device_name
0144                 self._device_info = self._get_device_info_v3(self._config)
0145         if self._ohos_config.target_cpu:
0146             self._device_info["target_cpu"] = self._ohos_config.target_cpu
0147         if self._ohos_config.target_os:
0148             self._device_info["target_os"] = self._ohos_config.target_os
0149         if self._ohos_config.compile_config:
0150             self._device_info[self._ohos_config["compile_config"]] = True

132~144行:记录device name和device info
146行:记录目标CPU
148行:记录目标OS

生成部件信息列表

build/hb/util/preloader/preloader_process_data.py

0152     # Update the _parts based on the product configuration in the vendor warehouse
0153     def _update_parts(self):
0154         if self._version == "1.0":
0155             _parts = {}
0156             self._parts = _parts
0157         else:
0158             # 1. inherit parts information from base config
0159             if self._version == "2.0":
0160                 os_level = self._config.get("type", "standard")
0161             else:
0162                 os_level = self._config.get("type", "mini")
0163             # 2. product config based on default minimum system
0164             based_on_mininum_system = self._config.get(
0165                 'based_on_mininum_system')
0166             if based_on_mininum_system == "true":
0167                 self._parts = self._get_base_parts(
0168                     self._dirs.built_in_base_dir, os_level)
0169             # 3. inherit parts information from inherit config
0170             inherit = self._config.get('inherit')
0171             if inherit:
0172                 self._parts.update(
0173                     self._get_inherit_parts(inherit, self._dirs.source_root_dir))
0174 
0175             # 4. chipset products relate system parts config
0176             sys_info_path = self._config.get('system_component')
0177             if sys_info_path:
0178                 sys_parts = self._get_sys_relate_parts(
0179                     sys_info_path, self._parts, self._dirs.source_root_dir)
0180                 self._parts.update(sys_parts)
0181             all_parts = {}
0182             if self._version == "2.0":
0183                 current_product_parts = self._config.get("parts")
0184                 if current_product_parts:
0185                     all_parts.update(current_product_parts)
0186             else:
0187                 all_parts.update(get_vendor_parts_list(self._config))
0188                 all_parts.update(self._get_product_specific_parts())
0189 
0190                 device_name = self._config.get('board')
0191                 if device_name:
0192                     all_parts.update(self._get_device_specific_parts())
0193             self._parts.update(all_parts)

166~168行:先生成最小系统的部件集合,目前搜索整个代码仓没有发现使用。但不排除有些商用发行版会使用到。
170~173行:从其它地方继承合并一些部件
176~180行:补充一些系统部件,目前搜索整个代码仓没有发现使用,但不排除有些商用发行版会使用到。
187~192行:补充只会属于本产品或开发板的一些部件

继承合并一些其它部件集合

build/hb/util/preloader/preloader_process_data.py

0366     def _get_inherit_parts(self, inherit, source_root_dir) -> dict:
0367         inherit_parts = {}
0368         for _config in inherit:
0369             _file = os.path.join(source_root_dir, _config)
0370             _info = IoUtil.read_json_file(_file)
0371             parts = _info.get('parts')
0372             if parts:
0373                 inherit_parts.update(parts)
0374             else:
0375                 inherit_parts.update(get_vendor_parts_list(_info))
0376         return inherit_parts

这里的inherit是一个列表,每个元素描述待继承的部件描述json文件路径(相对于代码根目录)
即从其它若干产品继承部件集合。
注意:由于历史原因,软件组件早期按子系统和组件模型划分,即系统拆分成若干子系统,每个子系统下面再拆分成若干组件,后来组件术语改变成部件(不再强调子系统概念)。即子系统与组件整体变更成部件概念。所以,这里get_vendor_parts_list就是从组件信息推导出部件信息。

获取产品部件集合

build/hb/util/preloader/parse_vendor_product_config.py

0108 def transform(config):
0109     subsystems = config.get('subsystems')
0110     if subsystems:
0111         config.pop('subsystems')
0112         parts = from_ss_to_parts(subsystems)
0113         config['parts'] = parts
0114     return config
0140 def get_vendor_parts_list(config):
0141     return transform(config).get('parts')

将config.json中的子系统转换成部件并记录

获取本产品额外部件

build/hb/util/preloader/preloader_process_data.py

0390     def _get_product_specific_parts(self) -> dict:
0391         part_name = 'product_{}'.format(self._name)
0392         subsystem_name = part_name
0393         info = {}
0394         info['{}:{}'.format(subsystem_name, part_name)] = {}
0395         return info

这里得到的info[‘product_xxx:product_xxx’]

增加本设备额外部件

build/hb/util/preloader/preloader_process_data.py

0340     def _get_device_specific_parts(self) -> dict:
0341         info = {}
0342         if self._device_info and self._device_info.get('device_build_path'):
0343             subsystem_name = 'device_{}'.format(self._device_name)
0344             part_name = subsystem_name
0345             info['{}:{}'.format(subsystem_name, part_name)] = {}
0346         return info

342~345行:增加设备额外的部件
这里得到的info[‘device_xxx:device_xxx’]

生成build var列表

build/hb/util/preloader/preloader_process_data.py

0195     # Update the _build_vars based on the product configuration in the vendor warehouse
0196     def _update_build_vars(self):
0197         config = self._config
0198         build_vars = {}
0199         if self._version == "1.0":
0200             build_vars = {"os_level": 'large'}
0201         else:
0202             if self._version == "2.0":
0203                 build_vars['os_level'] = config.get("type", "standard")
0204                 device_name = config.get('product_device')
0205                 if device_name:
0206                     build_vars['device_name'] = device_name
0207                 else:
0208                     build_vars['device_name'] = ''
0209                 build_vars['product_company'] = config.get('product_company')
0210             else:
0211                 build_vars['os_level'] = config.get('type', 'mini')
0212                 build_vars['device_name'] = config.get('board')
0213                 if config.get('product_company'):
0214                     build_vars['product_company'] = config.get(
0215                         'product_company')
0216                 elif os.path.dirname(self._config_file) != self._dirs.built_in_product_dir:
0217                     relpath = os.path.relpath(
0218                         self._config_file, self._dirs.vendor_dir)
0219                     build_vars['product_company'] = relpath.split('/')[0]
0220                 else:
0221                     build_vars['product_company'] = config.get(
0222                         'device_company')
0223             build_vars['product_name'] = config.get('product_name')
0224             if 'ext_root_proc_conf_path' in config:
0225                 ext_root_proc_conf_path = os.path.join(
0226                     self._dirs.source_root_dir, config.get('ext_root_proc_conf_path'))
0227                 if os.path.exists(ext_root_proc_conf_path):
0228                     build_vars['ext_root_proc_conf_path'] = ext_root_proc_conf_path
0229             if 'ext_critical_proc_conf_path' in config:
0230                 ext_critical_proc_conf_path = os.path.join(
0231                     self._dirs.source_root_dir, config.get('ext_critical_proc_conf_path'))
0232                 if os.path.exists(ext_critical_proc_conf_path):
0233                     build_vars['ext_critical_proc_conf_path'] = ext_critical_proc_conf_path
0234             if 'ext_sanitizer_check_list_path' in config:
0235                 ext_sanitizer_check_list_path = os.path.join(
0236                     self._dirs.source_root_dir, config.get('ext_sanitizer_check_list_path'))
0237                 if os.path.exists(ext_sanitizer_check_list_path):
0238                     build_vars['ext_sanitizer_check_list_path'] = ext_sanitizer_check_list_path
0239             _global_ext_var_file = os.path.join(
0240                 self._dirs.source_root_dir, "out/products_ext", "global_ext_var_file.gni")
0241             if os.path.exists(_global_ext_var_file):
0242                 build_vars['global_ext_var_file'] = _global_ext_var_file
0243             if 'enable_ramdisk' in config:
0244                 build_vars['enable_ramdisk'] = config.get('enable_ramdisk')
0245             if 'enable_absystem' in config:
0246                 build_vars['enable_absystem'] = config.get('enable_absystem')
0247             if 'build_selinux' in config:
0248                 build_vars['build_selinux'] = config.get('build_selinux')
0249             if 'build_seccomp' in config:
0250                 build_vars['build_seccomp'] = config.get('build_seccomp')
0251             if 'support_jsapi' in config:
0252                 build_vars['support_jsapi'] = config.get('support_jsapi')
0253             if 'chipprod_config_path' in config:
0254                 chipprod_config_path = os.path.join(
0255                     self._dirs.source_root_dir, config.get('chipprod_config_path'))
0256                 if os.path.exists(chipprod_config_path):
0257                     build_vars['chipprod_config_path'] = chipprod_config_path
0258             if 'ext_sdk_config_file' in config:
0259                 ext_sdk_config_file = os.path.join(
0260                     self._dirs.source_root_dir, config.get('ext_sdk_config_file'))
0261                 if os.path.exists(ext_sdk_config_file):
0262                     build_vars['ext_sdk_config_file'] = ext_sdk_config_file
0263             if 'ext_ndk_config_file' in config:
0264                 ext_ndk_config_file = os.path.join(
0265                     self._dirs.source_root_dir, config.get('ext_ndk_config_file'))
0266                 if os.path.exists(ext_ndk_config_file):
0267                     build_vars['ext_ndk_config_file'] = ext_ndk_config_file
0268             if 'ext_sign_hap_py_path' in config:
0269                 path = os.path.join(
0270                     self._dirs.source_root_dir, config.get('ext_sign_hap_py_path'))
0271                 if os.path.exists(path):
0272                     build_vars['ext_sign_hap_py_path'] = path
0273 
0274         build_vars.update(self._device_info)
0275         if build_vars['os_level'] == 'mini' or build_vars['os_level'] == 'small':
0276             toolchain_label = ""
0277         else:
0278             toolchain_label = '//build/toolchain/{0}:{0}_clang_{1}'.format(
0279                 self._device_info.get('target_os'), self._device_info.get('target_cpu'))
0280         build_vars['product_toolchain_label'] = toolchain_label
0281         self._build_vars = build_vars

记录下各种各样的build参数,当前只需要关注3.0相关的逻辑,不需要关注1.0和2.0

移除不需要的部件

build/hb/util/preloader/preloader_process_data.py

0283     # Remove excluded components
0284     def _remove_excluded_components(self):
0285         items_to_remove = []
0286         for part, val in self._parts.items():
0287             if "exclude" in val and val["exclude"] == "true":
0288                 items_to_remove.append(part)
0289         for item in items_to_remove:
0290             del self._parts[item]

这里的作用主要是在继承的基础上,少数部件不想包含在产品中,这个时候可以通过exclude属性进行排除。

生成build.prop

build/hb/services/preloader.py

0176     '''Description: generate build prop info to "out/preloader/product_name/build.prop"
0177     @parameter:none
0178     @return :none
0179     '''
0180 
0181     def _generate_build_prop(self):
0182         build_vars_list = []
0183         for key, value in self._build_vars.items():
0184             build_vars_list.append('{}={}'.format(key, value))
0185         with os.fdopen(os.open(self._outputs.build_prop,
0186                                os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), 'w') as fobj:
0187             fobj.write('\n'.join(build_vars_list))
0188         LogUtil.hb_info(
0189             'generated build prop info to {}/build.prop'.format(self._dirs.preloader_output_dir))

将所有的build参数写入build.prop文件

生成build_config.json

build/hb/services/preloader.py

0165     '''Description: generate build config info to "out/preloader/product_name/build_config.json"
0166     @parameter:none
0167     @return :none
0168     '''
0169 
0170     def _generate_build_config_json(self):
0171         IoUtil.dump_json_file(
0172             self._outputs.build_config_json, self._build_vars)
0173         LogUtil.hb_info(
0174             'generated build config info to {}/build_config.json'.format(self._dirs.preloader_output_dir))

仍然是build参数,不过以json文件格式记录下来

生成部件json

build/hb/services/preloader.py

0202     '''Description: generate parts to "out/preloader/product_name/parts.json"
0203     @parameter:none
0204     @return :none
0205     '''
0206 
0207     def _generate_parts_json(self):
0208         parts_info = {"parts": sorted(list(self._all_parts.keys()))}
0209         IoUtil.dump_json_file(self._outputs.parts_json, parts_info)
0210         LogUtil.hb_info(
0211             'generated product parts info to {}/parts.json'.format(
0212                 self._dirs.preloader_output_dir), mode=self.config.log_mode)

将部件列表信息按部件名排序后,写入json文件

生成部件配置json

build/hb/services/preloader.py

0214     '''Description: generate parts config to "out/preloader/product_name/parts_config.json"
0215     @parameter:none
0216     @return :none
0217     '''
0218 
0219     def _generate_parts_config_json(self):
0220         parts_config = {}
0221         for part in self._all_parts:
0222             part = part.replace(":", "_")
0223             part = part.replace("-", "_")
0224             part = part.replace(".", "_")
0225             part = part.replace("/", "_")
0226             parts_config[part] = True
0227         IoUtil.dump_json_file(self._outputs.parts_config_json, parts_config)
0228         LogUtil.hb_info(
0229             'generated parts config info to {}/parts_config.json'.format(
0230                 self._dirs.preloader_output_dir), mode=self.config.log_mode)

将部件中的名称进行规范化处理后写入json文件

生成gn参数属性文件

build/hb/services/preloader.py

0080     '''Description: generate build gnargs prop info to "out/preloader/{product_name}/build_gnargs.prop"
0081     @parameter:none
0082     @return :none
0083     '''
0084 
0085     def _generate_build_gnargs_prop(self):
0086         all_features = {}
0087         for _part_name, vals in self._all_parts.items():
0088             _features = vals.get('features')
0089             if _features:
0090                 all_features.update(_features)
0091         attr_list = []
0092         for key, val in all_features.items():
0093             _item = ''
0094             if isinstance(val, bool):
0095                 _item = f'{key}={str(val).lower()}'
0096             elif isinstance(val, int):
0097                 _item = f'{key}={val}'
0098             elif isinstance(val, str):
0099                 _item = f'{key}="{val}"'
0100             else:
0101                 raise Exception("part feature '{key}:{val}' type not support.")
0102             attr_list.append(_item)
0103         with os.fdopen(os.open(self._outputs.build_gnargs_prop,
0104                                os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), 'w') as fobj:
0105             fobj.write('\n'.join(attr_list))
0106         LogUtil.hb_info(
0107             'generated build gnargs prop info to {}/build_gnargs.prop'.format(
0108                 self._dirs.preloader_output_dir), mode=self.config.log_mode)

86~90行:读出所有部件的特性集合
91~102行:对特性字符串进行规范化处理,只支持整数,布尔类型和字符串
103~108行:写入规范化后的结果

生成特性集合

build/hb/services/preloader.py

0110     '''Description: generate features to "out/preloader/{product_name}/features.json"
0111     @parameter:none
0112     @return :none
0113     '''
0114 
0115     def _generate_features_json(self):
0116         all_features = {}
0117         part_feature_map = {}
0118         for _part_name, vals in self._all_parts.items():
0119             _features = vals.get('features')
0120             if _features:
0121                 all_features.update(_features)
0122             if _features:
0123                 part_feature_map[_part_name.split(
0124                     ':')[1]] = list(_features.keys())
0125         parts_feature_info = {
0126             "features": all_features,
0127             "part_to_feature": part_feature_map
0128         }
0129         IoUtil.dump_json_file(self._outputs.features_json, parts_feature_info)
0130         LogUtil.hb_info(
0131             'generated features info to {}/features.json'.format(
0132                 self._dirs.preloader_output_dir), mode=self.config.log_mode)

120~121行:汇总所有的特性
122~124行:记录每一个部件下的特性集合(部件名使用简称)
125~132行:结果写入文件

生成系统能力记录

build/hb/services/preloader.py

0134     '''Description: generate syscap to "out/preloader/product_name/syscap.json"
0135     @parameter:none
0136     @return :none
0137     '''
0138 
0139     def _generate_syscap_json(self):
0140         all_syscap = {}
0141         part_syscap_map = {}
0142         for _part_name, vals in self._all_parts.items():
0143             _syscap = vals.get('syscap')
0144             if _syscap:
0145                 all_syscap.update(_syscap)
0146                 part_syscap_map[_part_name.split(':')[1]] = _syscap
0147         parts_syscap_info = {
0148             "syscap": all_syscap,
0149             "part_to_syscap": part_syscap_map
0150         }
0151         IoUtil.dump_json_file(self._outputs.syscap_json, parts_syscap_info)
0152         LogUtil.hb_info(
0153             'generated syscap info to {}/syscap.json'.format(
0154                 self._dirs.preloader_output_dir), mode=self.config.log_mode)

140~146行:获取系统能力以及组件(去除子系统前缀)和能力集的映射
147~154行:然后写入结果

生成排除模块记录

build/hb/services/preloader.py

0156     '''Description: generate exclusion modules info to "out/preloader/product_name/exclusion_modules.json"
0157     @parameter:none
0158     @return :none
0159     '''
0160 
0161     def _generate_exclusion_modules_json(self):
0162         exclusions = {}
0163         for _part_name, vals in self._all_parts.items():
0164             _exclusions = vals.get('exclusions')
0165             if _exclusions:
0166                 pair = dict()
0167                 pair[_part_name] = _exclusions
0168                 exclusions.update(pair)
0169         IoUtil.dump_json_file(self._outputs.exclusion_modules_json, exclusions)
0170         LogUtil.hb_info(
0171             'generated exclusion modules info to {}/exclusion_modules.json'.format(
0172                 self._dirs.preloader_output_dir), mode=self.config.log_mode)

将各部件排除的模块整合起来
再记录到json文件中
本能力目前在各产品还没有看见使用。

生成子系统配置json

build/hb/services/preloader.py

0232     '''Description: generate subsystem config info to "out/preloader/product_name/subsystem_config.json"
0233     @parameter:none
0234     @return :none
0235     '''
0236 
0237     def _generate_subsystem_config_json(self):
0238         if self._subsystem_info:
0239             self._subsystem_info.update(
0240                 self._product._get_product_specific_subsystem())
0241             self._subsystem_info.update(
0242                 self._product._get_device_specific_subsystem())
0243         IoUtil.dump_json_file(
0244             self._outputs.subsystem_config_json, self._subsystem_info)
0245         LogUtil.hb_info(
0246             'generated subsystem config info to {}/subsystem_config.json'.format(
0247                 self._dirs.preloader_output_dir), mode=self.config.log_mode)

基于通用的子系统信息,汇总设备和产品的子系统配置信息,然后记录到json文件中

生成系统能力配置json

build/hb/services/preloader.py

0235     '''Description: generate systemcapability_json to "out/preloader/product_name/systemcapability.json"
0236     @parameter:none
0237     @return :none
0238     '''
0239 
0240     def _generate_systemcapability_json(self):
0241         IoUtil.dump_json_file(
0242             self._outputs.systemcapability_json, self._product._syscap_info)
0243         LogUtil.hb_info(
0244             'generated system capability info to {}/systemcapability.json'.format(self._dirs.preloader_output_dir))

写入当前的系统能力信息

load

build/hb/modules/ohos_build_module.py

0080     def _load(self):
0081         self._run_phase(BuildPhase.LOAD)
0082         if self.args_dict.get('fast_rebuild', None) and not self.args_dict.get('fast_rebuild').arg_value:
0083             self.loader.run()

81行:先支持LOAD阶段的处理
这些处理有

  • resolve_strict_mode
  • resolve_scalable_build
  • resolve_build_example
  • resolve_build_platform_name
  • resolve_build_xts
  • resolve_ignore_api_check
  • resolve_load_test_config
  • resolve_skip_partlist_check

82~83行:在没有开启fast_rebuild的情况下,还要运行loader

严格模式

build/hb/resolver/build_args_resolver.py

0456     @staticmethod
0457     @throw_exception
0458     def resolve_strict_mode(target_arg: Arg, build_module: BuildModuleInterface):
0459         """resolve '--strict-mode' arg.
0460         :param target_arg: arg object which is used to get arg value.
0461         :param build_module [maybe unused]: build module object which is used to get other services.
0462         :phase: load.
0463         :raise OHOSException: when preloader or loader results not correct
0464         """
0465         if target_arg.arg_value:
0466             preloader = build_module.preloader
0467             loader = build_module.loader
0468             if not preloader.outputs.check_outputs():
0469                 raise OHOSException('Preloader result not correct', "1001")
0470             if not loader.outputs.check_outputs():
0471                 raise OHOSException('Loader result not correct ', "2001")

严格模式下,检查preloader和loader阶段的产出,如果有问题,则终止构建。
目前没有看到check_outputs函数的实现。本选项默认关闭,也许未来会支持或者曾经支持过。

增量构建

build/hb/resolver/build_args_resolver.py

0473     @staticmethod
0474     def resolve_scalable_build(target_arg: Arg, build_module: BuildModuleInterface):
0475         """resolve '--scalable-build' arg.
0476         :param target_arg: arg object which is used to get arg value.
0477         :param build_module [maybe unused]: build module object which is used to get other services.
0478         :phase: load.
0479         """
0480         loader = build_module.loader
0481         loader.regist_arg("scalable_build", target_arg.arg_value)

记录增量构建开关

构建范例

build/hb/resolver/build_args_resolver.py

0483     @staticmethod
0484     def resolve_build_example(target_arg: Arg, build_module: BuildModuleInterface):
0485         """resolve '--build-example' arg.
0486         :param target_arg: arg object which is used to get arg value.
0487         :param build_module [maybe unused]: build module object which is used to get other services.
0488         :phase: load.
0489         """
0490         loader = build_module.loader
0491         loader.regist_arg("build_example", target_arg.arg_value)

记录build_example开关

构建平台名称

build/hb/resolver/build_args_resolver.py

0493     @staticmethod
0494     def resolve_build_platform_name(target_arg: Arg, build_module: BuildModuleInterface):
0495         """resolve '---build-platform-name' arg
0496         :param target_arg: arg object which is used to get arg value.
0497         :param build_module [maybe unused]: build module object which is used to get other services.
0498         :phase: load.
0499         """
0500         loader = build_module.loader
0501         loader.regist_arg("build_platform_name", target_arg.arg_value)

记录构建平台的名称

构建测试用例

build/hb/resolver/build_args_resolver.py

0503     @staticmethod
0504     def resolve_build_xts(target_arg: Arg, build_module: BuildModuleInterface):
0505         """resolve '--build-xts' arg
0506         :param target_arg: arg object which is used to get arg value.
0507         :param build_module [maybe unused]: build module object which is used to get other services.
0508         :phase: load.
0509         """
0510         loader = build_module.loader
0511         loader.regist_arg("build_xts", target_arg.arg_value)
0512         for gn_arg in build_module.args_dict['gn_args'].arg_value:
0513             if 'pr_path_list' in gn_arg:
0514                 build_module.args_dict['gn_args'].arg_value.append("precise_xts=true")
0515                 config = Config()
0516                 variable, value = gn_arg.split('=')
0517                 pyd_start_cmd = [
0518                     'python3',
0519                     '{}/test/xts/acts/get_dependency.py'.format(config.root_path),
0520                     value,
0521                 ]
0522                 subprocess.call(pyd_start_cmd)
0523             if 'build_xts' in gn_arg:
0524                 variable, value = gn_arg.split('=')
0525                 if str(value).lower() == 'false':
0526                     value = False
0527                 elif str(value).lower() == 'true':
0528                     value = True
0529                 loader.regist_arg(variable, value)

记录是否配置了build_xts选项
如果配置了pr_path_list, 则增加选项precise_xts=true, 且执行get_dependency.py脚本

忽略API检查

build/hb/resolver/build_args_resolver.py

0531     @staticmethod
0532     def resolve_ignore_api_check(target_arg: Arg, build_module: BuildModuleInterface):
0533         """resolve '--ignore-api-check' arg
0534         :param target_arg: arg object which is used to get arg value.
0535         :param build_module [maybe unused]: build module object which is used to get other services.
0536         :phase: load.
0537         """
0538         loader = build_module.loader
0539         if len(target_arg.arg_value):
0540             loader.regist_arg("ignore_api_check", target_arg.arg_value)
0541         else:
0542             loader.regist_arg("ignore_api_check", [
0543                               'xts', 'common', 'testfwk'])

记录用户指定的忽略api检查的部件集合
如果用户没有指定,默认有3个部件不检查API

加载测试配置

build/hb/resolver/build_args_resolver.py

0545     @staticmethod
0546     def resolve_load_test_config(target_arg: Arg, build_module: BuildModuleInterface):
0547         """resolve '--load-test-config' arg
0548         :param target_arg: arg object which is used to get arg value.
0549         :param build_module [maybe unused]: build module object which is used to get other services.
0550         :phase: load.
0551         """
0552         loader = build_module.loader
0553         loader.regist_arg("load_test_config", target_arg.arg_value)

记录下对应开关值

loader

build/hb/services/interface/load_interface.py

0045     def run(self):
0046         self.__post_init__()
0047         self._execute_loader_args_display()
0048         self._check_parts_config_info()
0049         self._generate_subsystem_configs()
0050         self._generate_target_platform_parts()
0051         self._generate_system_capabilities()
0052         self._generate_stub_targets()
0053         self._generate_platforms_part_by_src()
0054         self._generate_target_gn()
0055         self._generate_phony_targets_build_file()
0056         self._generate_required_parts_targets()
0057         self._generate_required_parts_targets_list()
0058         self._generate_src_flag()
0059         self._generate_auto_install_part()
0060         self._generate_platforms_list()
0061         self._generate_part_different_info()
0062         self._generate_infos_for_testfwk()
0063         self._check_product_part_feature()
0064         self._generate_syscap_files()

loader初始化

build/hb/services/loader.py

0059     def __post_init__(self):
0060         self.source_root_dir = self.config.root_path + '/'
0061         self.gn_root_out_dir = self.config.out_path if not self.config.out_path.startswith(
0062             '/') else os.path.relpath(self.config.out_path, self.config.root_path)
0063         self.os_level = self.config.os_level if self.config.os_level else "standard"
0064         self.target_cpu = self.config.target_cpu if self.config.target_cpu else "arm"
0065         self.target_os = self.config.target_os if self.config.target_os else "ohos"
0066         self.config_output_relpath = os.path.join(
0067             self.gn_root_out_dir, 'build_configs')
0068         self.config_output_dir = os.path.join(
0069             self.source_root_dir, self.config_output_relpath)
0070         self.target_arch = '{}_{}'.format(self.target_os, self.target_cpu)
0071         self.subsystem_config_file = os.path.join(
0072             self.config.root_path, 'out/preloader', self.config.product, 'subsystem_config.json')
0073         self.platforms_config_file = os.path.join(
0074             self.config.root_path, 'out/preloader', self.config.product, 'platforms.build')
0075         self.exclusion_modules_config_file = os.path.join(
0076             self.config.root_path, 'out/preloader', self.config.product, 'exclusion_modules.json')
0077         self.example_subsystem_file = os.path.join(
0078             self.config.root_path, 'build', 'subsystem_config_example.json')
0079 
0080         compile_standard_allow_file = os.path.join(
0081             self.config.root_path, 'out/preloader', self.config.product, 'compile_standard_whitelist.json')
0082         compile_standard_allow_info = read_json_file(compile_standard_allow_file)
0083         bundle_subsystem_allow_list = compile_standard_allow_info.get("bundle_subsystem_error", [])
0084 
0085         # check config args
0086         self._check_args()
0087 
0088         self.build_example = self.args_dict.get('build_example')
0089         if not self.build_example:
0090             self.example_subsystem_file = ""
0091         self.scalable_build = self.args_dict.get('scalable_build')
0092         self.build_platform_name = self.args_dict.get('build_platform_name')
0093         self.build_xts = self.args_dict.get('build_xts')
0094         self.ignore_api_check = self.args_dict.get('ignore_api_check')
0095         self.load_test_config = self.args_dict.get('load_test_config')
0096         self.skip_partlist_check = self.args_dict.get('skip_partlist_check')
0097 
0098         self._subsystem_info = subsystem_info.get_subsystem_info(
0099             self.subsystem_config_file,
0100             self.example_subsystem_file,
0101             self.source_root_dir,
0102             self.config_output_relpath,
0103             self.os_level)
0104         overrided_components = self._override_components()
0105 
0106         self._platforms_info = platforms_loader.get_platforms_info(
0107             self.platforms_config_file,
0108             self.source_root_dir,
0109             self.gn_root_out_dir,
0110             self.target_arch,
0111             self.config_output_relpath,
0112             self.scalable_build)
0113         self.variant_toolchains = self._platforms_info.get(
0114             'variant_toolchain_info').get('platform_toolchain')
0115         self._all_platforms = self.variant_toolchains.keys()
0116         self.build_platforms = self._get_build_platforms()
0117         self.parts_config_info = load_ohos_build.get_parts_info(
0118             self.source_root_dir,
0119             self.config_output_relpath,
0120             self._subsystem_info,
0121             self.variant_toolchains,
0122             self.target_arch,
0123             self.ignore_api_check,
0124             self.exclusion_modules_config_file,
0125             self.load_test_config,
0126             overrided_components,
0127             bundle_subsystem_allow_list,
0128             self.skip_partlist_check,
0129             self.build_xts)
0130         self.parts_targets = self.parts_config_info.get('parts_targets')
0131         self.phony_targets = self.parts_config_info.get('phony_target')
0132         self.parts_info = self.parts_config_info.get('parts_info')
0133         self.target_platform_parts = self._get_platforms_all_parts()
0134         self.target_platform_stubs = self._get_platforms_all_stubs()
0135         self.required_parts_targets_list = self._get_required_build_parts_list()
0136         self.required_phony_targets = self._get_required_phony_targets()
0137         self.required_parts_targets = self._get_required_build_targets()

60~96行:记录本阶段解析好的命令行参数,以及一些文件的路径
98~103行:扫描subsystem_config.json和subsystem_config_example.json内的所有subsystem,含ohos.build或bundle.json文件的,即为合法的部件,否则为不参与编译的源码。将扫描出的subsystem写入json文件,并返回扫描结果
out/productname/build_configs/subsystem_info/src_subsystem_info.json
out/productname/build_configs/subsystem_info/no_src_subsystem_info.json
out/productname/build_configs/subsystem_info/subsystem_build_config.json
104行:处理被替换的部件, 厂商自己实现的部件替换掉系统默认实现的部件
106~112行:获取平台信息
113~114行:获取平台对应的工具链信息
115行:获取本次编译的所有平台,当前只支持phone
116行:获取本次构建的平台,仅支持phone
117~129行:获取ohos.build相关的部件信息
130~131行:记录部件编译目标和伪目标
132~137行:记录相关的需要构建的目标

扫描已知子系统中的部件

build/hb/util/loader/subsystem_scan.py

0075 def scan(subsystem_config_file, example_subsystem_file, source_root_dir):
0076     subsystem_infos = _read_config(subsystem_config_file,
0077                                    example_subsystem_file)
0078     # add common subsystem info
0079     subsystem_infos.update(_default_subsystem)
0080 
0081     no_src_subsystem = {}
0082     _build_configs = {}
0083     for key, val in subsystem_infos.items():
0084         _all_build_config_files = []
0085         if not isinstance(val, list):
0086             val = [val]
0087         else:
0088             if not _check_path_prefix(val):
0089                 raise OHOSException(
0090                     "subsystem '{}' path configuration is incorrect.".format(
0091                         key), "2013")
0092         _info = {'path': val}
0093         for _path in val:
0094             _subsystem_path = os.path.join(source_root_dir, _path)
0095             _build_config_files = _scan_build_file(_subsystem_path)
0096             _all_build_config_files.extend(_build_config_files)
0097         if _all_build_config_files:
0098             _info['build_files'] = _all_build_config_files
0099             _build_configs[key] = _info
0100         else:
0101             no_src_subsystem[key] = val
0102 
0103     scan_result = {
0104         'source_path': source_root_dir,
0105         'subsystem': _build_configs,
0106         'no_src_subsystem': no_src_subsystem
0107     }
0108     LogUtil.hb_info('subsytem config scan completed')
0109     return scan_result

76行:读取已知子系统的名称和路径列表
79行:添加common子系统
83行:遍历所有子系统
85~86行: path只有一项,直接存入
87~91行:path是一个列表,那么列表中最多只能含一个(device或vendor前缀)的路径
93~96行:遍历path列表,在这些路径中找出软件包配置文件
97~99行:记录下这些配置文件
100~101行:无bundle.json或ohos.build的子系统也记录一下
103~109行:记录结果并返回

根据子系统路径配置读取子系统信息

build/hb/util/loader/subsystem_scan.py

0027 @throw_exception
0028 def _read_config(subsystem_config_file, example_subsystem_file):
0029     if not os.path.exists(subsystem_config_file):
0030         raise OHOSException(
0031             "config file '{}' doesn't exist.".format(subsystem_config_file), "2013")
0032     subsystem_config = read_json_file(subsystem_config_file)
0033     if subsystem_config is None:
0034         raise OHOSException("read file '{}' failed.".format(
0035             subsystem_config_file), "2013")
0036 
0037     # example subsystem
0038     if example_subsystem_file:
0039         example_subsystem_config = read_json_file(example_subsystem_file)
0040         if example_subsystem_config is not None:
0041             subsystem_config.update(example_subsystem_config)
0042 
0043     subsystem_info = {}
0044     for key, val in subsystem_config.items():
0045         if 'path' not in val:
0046             raise OHOSException(
0047                 "subsystem '{}' not config path.".format(key), "2013")
0048         subsystem_info[key] = val.get('path')
0049     return subsystem_info

48行:将每个子系统的名称和路径记录到新的字典并返回

检查非法路径

build/hb/util/loader/subsystem_scan.py

0066 def _check_path_prefix(paths):
0067     allow_path_prefix = ['vendor', 'device']
0068     result = list(
0069         filter(lambda x: x is False,
0070                map(lambda p: p.split('/')[0] in allow_path_prefix, paths)))
0071     return len(result) <= 1

在路径列表中,最多只能出现一次不是device或vendor开头的路径
map表示针对对每条路径按/切分获取首个字符串,并判断字符串是否device或vendor
filter表示,将map计算结果为False的进行汇总
最终结果就是路径列表中允许出现device和vendor开头,其它开头的最多只能一条。

寻找部件

build/hb/util/loader/subsystem_scan.py

0052 def _scan_build_file(subsystem_path):
0053     _files = []
0054     _bundle_files = []
0055     for root, dirs, files in os.walk(subsystem_path):
0056         for name in files:
0057             if name == 'ohos.build':
0058                 _files.append(os.path.join(root, name))
0059             elif name == 'bundle.json':
0060                 _bundle_files.append(os.path.join(root, name))
0061     if _bundle_files:
0062         _files.extend(_bundle_files)
0063     return _files

遍历子系统目录,寻找obos.build或bundle.json文件,返回文件路径集合。

获取存在部件的子系统列表

build/hb/util/loader/subsystem_info.py

0065 def get_subsystem_info(subsystem_config_file, example_subsystem_file,
0066                        source_root_dir, config_output_path, os_level):
0067     if not subsystem_config_file:
0068         subsystem_config_file = 'build/subsystem_config.json'
0069 
0070     subsystem_configs = {}
0071     output_dir_realpath = os.path.join(source_root_dir, config_output_path)
0072     subsystem_configs = subsystem_scan.scan(subsystem_config_file,
0073                                             example_subsystem_file,
0074                                             source_root_dir)
0075     config = Config()
0076     subsystem_config_overlay_file = os.path.join(
0077         config.product_path, "subsystem_config_overlay.json")
0078     if os.path.isfile(subsystem_config_overlay_file):
0079         subsystem_config_overlay = {}
0080         subsystem_config_overlay = subsystem_scan.scan(subsystem_config_overlay_file,
0081                                                        example_subsystem_file,
0082                                                        source_root_dir)
0083         merge_subsystem_overlay(subsystem_configs, subsystem_config_overlay, 'subsystem')
0084         merge_subsystem_overlay(subsystem_configs, subsystem_config_overlay, 'no_src_subsystem')
0085 
0086     _output_subsystem_configs(output_dir_realpath, subsystem_configs)
0087     return subsystem_configs.get('subsystem')

79~84行:如果存在overlay配置,则需要将overlay相关子系统也考虑进来
86行:保存查找到的部件信息
87行:返回部件信息

保存部件信息

build/hb/util/loader/subsystem_info.py

0024 def _output_subsystem_configs(output_dir, subsystem_configs):
0025     build_config_file_name = "subsystem_build_config.json"
0026     build_config_file = os.path.join(output_dir, 'subsystem_info',
0027                                      build_config_file_name)
0028     write_json_file(build_config_file, subsystem_configs)
0029 
0030     src_output_file_name = "src_subsystem_info.json"
0031     no_src_output_file_name = "no_src_subsystem_info.json"
0032 
0033     src_subsystem = {}
0034     for key, val in subsystem_configs.get('subsystem').items():
0035         src_subsystem[key] = val.get('path')
0036     src_output_file = os.path.join(output_dir, 'subsystem_info',
0037                                    src_output_file_name)
0038     write_json_file(src_output_file, src_subsystem)
0039 
0040     no_src_output_file = os.path.join(output_dir, 'subsystem_info',
0041                                       no_src_output_file_name)
0042     write_json_file(no_src_output_file,
0043                     subsystem_configs.get('no_src_subsystem'))

25~28行:保存完整部件信息
33~38行:保存含部件的子系统路径映射表
40~43行:保存不含部件的子系统列表

替换冗余部件

build/hb/services/loader.py

0817     def _override_components(self):
0818         '''Description: Check whether there are components that need to be replaced, and if so, 
0819             replace the component configuration file bundle.json in subsystem_info and update 
0820             the component list generated by the preloader.
0821         @parameter:none
0822         @return :overrided_components
0823         '''
0824         parts_file = self.platforms_config_file.replace(
0825             "platforms.build", "parts.json")
0826         all_parts = read_json_file(parts_file)
0827         if "parts" not in all_parts:
0828             LogUtil.hb_warning("{} does not contain parts!".format(parts_file))
0829             return {}
0830         overrided = False
0831         overrided_components = {}
0832         all_parts = all_parts["parts"]
0833         component_override_map = {}
0834         all_component_override_map = {}
0835         for subsystem_name, build_config_info in self._subsystem_info.items():
0836             if "build_files" not in build_config_info:
0837                 continue
0838 
0839             # scan all bundle.json or ohos.build files with named groups
0840             for build_file in build_config_info["build_files"]:
0841 
0842                 # ohos.build does not support overrided components
0843                 if not build_file.endswith('bundle.json'):
0844                     continue
0845 
0846                 # Only device or vendor components can do named groups extensions
0847                 if (not build_file.startswith(self.source_root_dir + 'device/')) \
0848                         and (not build_file.startswith(self.source_root_dir + 'vendor/')):
0849                     continue
0850 
0851                 # "subsystem", "name" and "override" is required
0852                 component = read_json_file(build_file).get("component")
0853 
0854                 if (not component) or (not all(key in component for key in ("subsystem", "name", "override"))):
0855                     continue
0856 
0857                 full_part_name = f"{component.get('subsystem')}:{component.get('name')}"
0858                 if full_part_name not in all_parts:
0859                     LogUtil.hb_warning("{} was not configured for this product: {}".format(
0860                         build_file, full_part_name))
0861                     continue
0862 
0863                 if self._override_one_component(self._subsystem_info, component, build_file, all_parts, overrided_components, component_override_map):
0864                     overrided = True
0865 
0866                 if overrided:
0867                     # Update parts.json and parts_config.json generated by preloader
0868                     write_json_file(parts_file, {"parts": all_parts})
0869                     parts_file = self.platforms_config_file.replace(
0870                         "platforms.build", "parts_config.json")
0871                     self._output_parts_config_json(all_parts, parts_file)
0872                     all_component_override_map.update(component_override_map)
0873         write_json_file(
0874             f"{self.config_output_dir}/component_override_map.json", all_component_override_map)
0875         return overrided_components

824~834行:读取部件列表
835行:遍历子系统
840行:遍历子系统中部件列表
843~844行:只处理bundle.json描述的部件,它才能支持部件替换
847~849行:只处理device或vendor开头的部件,只有产品特定部件才能覆盖通用部件
851~855行:部件中必须包含subsystem, name, override属性,表示可以替换同名的通用部件
857~861行:子系统与组件名和部件名需匹配
863~864行:移除被本组件替换(覆盖)的组件
866~874行:更新相关文件,并记录下覆盖关系
875行:返回被覆盖的部件集合

替换一个部件

build/hb/services/loader.py

0877     def _override_one_component(self, subsystem_info: dict, component: dict, build_file: str, all_parts: dict, overrided_components: dict, component_override_map: dict):
0878         '''Description: Perform a replacement of a single component and return the component list update result.
0879         @parameter:subsystem_info, component, build_file, all_parts, overrided_components
0880         @return :True or False(Whether replacement has been performed)
0881         '''
0882         splits = component["override"].split(":")
0883         if len(splits) != 2:
0884             LogUtil.hb_warning(
0885                 "{} override value is invalid format. Skip override process".format(build_file))
0886             return False
0887         overrided_subsystem = splits[0]
0888         overrided_component = splits[1]
0889         if overrided_subsystem not in subsystem_info:
0890             LogUtil.hb_warning(
0891                 "{} override invalid subsystem. Skip override process".format(build_file))
0892             return False
0893 
0894         founded_bundle = ""
0895 
0896         for bundle in subsystem_info[overrided_subsystem]["build_files"]:
0897             if not bundle.endswith('bundle.json'):
0898                 continue
0899 
0900             bundle_obj = read_json_file(bundle)
0901 
0902             if bundle_obj.get("component", {}).get("name") == overrided_component:
0903                 founded_bundle = bundle
0904                 break
0905 
0906         if founded_bundle:
0907             origin_component = read_json_file(build_file).get('component')
0908             LogUtil.hb_warning(
0909                 f"You are trying to override \"{component['override']}\" with \"{origin_component.get('subsystem')}:{origin_component.get('name')}\". \nPlease ensure that the modules in \"{component['override']}\" only rely on the interfaces of other components through \"external_deps\"")
0910 
0911             # replace bundle.json in subsystem_info's build_files
0912             subsystem_info[overrided_subsystem]["build_files"].remove(
0913                 founded_bundle)
0914 
0915             # Update parts.json generated by preloader, which means that new added components will not be installed
0916             # Ensure that the overrided components will be installed
0917             full_partname = f"{overrided_subsystem}:{overrided_component}"
0918             if full_partname in all_parts:
0919                 all_parts.remove(full_partname)
0920 
0921             overrided_components[f"{component['subsystem']}:{component['name']}"] = {
0922                 'subsystem': overrided_subsystem,
0923                 'partName': overrided_component
0924             }
0925             component_override_map[overrided_component] = component["name"]
0926             return True
0927         LogUtil.hb_warning(
0928             "{}:{} is not configured in product, \new add component will be installed!".format(
0929                 overrided_subsystem, overrided_component))
0930         return False

882~888行:解析被替换的子系统和组件名
889~892行:子系统不存在
896行:遍历子系统中的部件
897~898行:只支持bundle.json类型的部件
900~904行:找到匹配的部件
907~909行:获取原始组件,并输出覆盖动作日志
912~919行:移除被覆盖的部件
921~925行:记录下部件覆盖关系

获取平台信息

build/hb/util/loader/platforms_loader.py

0183 def get_platforms_info(platforms_config_file, source_root_dir, root_build_dir,
0184                        target_arch, config_output_relpath, scalable_build):
0185     platform_loader = PlatformsLoader(platforms_config_file, source_root_dir,
0186                                       root_build_dir, target_arch,
0187                                       scalable_build)
0188 
0189     platforms_info_output_dir = 'platforms_info'
0190     all_parts = platform_loader.get_all_parts()
0191     all_parts_file = os.path.join(source_root_dir, config_output_relpath,
0192                                   platforms_info_output_dir, "all_parts.json")
0193     write_json_file(all_parts_file, all_parts)
0194     LogUtil.hb_info(
0195         "generate all parts of platforms info to '{}'".format(all_parts_file))
0196 
0197     # variant to toolchain and toolchain to variant
0198     toolchain_to_variant_dict = platform_loader.platforms_toolchain()
0199     toolchain_variant_info_file = os.path.join(source_root_dir,
0200                                                config_output_relpath,
0201                                                platforms_info_output_dir,
0202                                                "toolchain_to_variant.json")
0203     write_json_file(toolchain_variant_info_file,
0204                     toolchain_to_variant_dict,
0205                     check_changes=True)
0206     LogUtil.hb_info("generate toolchain to variant of platforms info to '{}'".format(
0207         toolchain_variant_info_file))
0208 
0209     result = {}
0210     result['all_parts'] = all_parts
0211     result['all_stubs'] = platform_loader.get_all_stubs()
0212     result['variant_toolchain_info'] = toolchain_to_variant_dict
0213     return result

189~196行:将所有部件信息换一种形式存储起来
198~207行:将平台与工具链之间的映射信息存储起来
209~213行:记录并返回上述信息

获取平台和工具链的映射

build/hb/util/loader/platforms_loader.py

0168     def platforms_toolchain(self):
0169         self._loading()
0170         platform_toolchain = {}
0171         toolchain_platform = {}
0172         for key, val in self._platforms_info.items():
0173             _toolchain = val.get('toolchain')
0174             platform_toolchain[key] = _toolchain
0175             toolchain_platform[_toolchain] = key
0176         _result = {
0177             "platform_toolchain": platform_toolchain,
0178             "toolchain_platform": toolchain_platform
0179         }
0180         return _result

174行:从平台到工具链的映射
175行:从工具链到平台的映射
177~178行:记录下上述2个映射表

显示loader参数

build/hb/services/loader.py

0792     def _execute_loader_args_display(self):
0793         LogUtil.hb_info('Loading configuration file...')
0794         args = []
0795         args.append('platforms_config_file="{}"'.format(
0796             self.platforms_config_file))
0797         args.append('subsystem_config_file="{}"'.format(
0798             self.subsystem_config_file))
0799         args.append('example_subsystem_file="{}"'.format(
0800             self.example_subsystem_file))
0801         args.append('exclusion_modules_config_file="{}"'.format(
0802             self.exclusion_modules_config_file))
0803         args.append('source_root_dir="{}"'.format(self.source_root_dir))
0804         args.append('gn_root_out_dir="{}"'.format(self.gn_root_out_dir))
0805         args.append('build_platform_name={}'.format(self.build_platform_name))
0806         args.append('build_xts={}'.format(self.build_xts))
0807         args.append('load_test_config={}'.format(self.load_test_config))
0808         args.append('target_os={}'.format(self.target_os))
0809         args.append('target_cpu={}'.format(self.target_cpu))
0810         args.append('os_level={}'.format(self.os_level))
0811         args.append('ignore_api_check={}'.format(self.ignore_api_check))
0812         args.append('scalable_build={}'.format(self.scalable_build))
0813         args.append('skip_partlist_check={}'.format(self.skip_partlist_check))
0814         LogUtil.write_log(self.config.log_path,
0815                           'loader args:{}'.format(args), 'info')

以日志形式将loader阶段的参数显示出来

检查部件配置信息

build/hb/services/loader.py

0215     @throw_exception
0216     def _check_parts_config_info(self):
0217         LogUtil.hb_info("Checking parts config...")
0218         if not ('parts_info' in self.parts_config_info
0219                 and 'subsystem_parts' in self.parts_config_info
0220                 and 'parts_variants' in self.parts_config_info
0221                 and 'parts_kits_info' in self.parts_config_info
0222                 and 'parts_inner_kits_info' in self.parts_config_info
0223                 and 'parts_targets' in self.parts_config_info):
0224             raise OHOSException(
0225                 "Loading ohos.build information is incorrect.", "2007")

部件配置信息必须完整,否则报错

生成子系统配置

build/hb/services/loader.py

0557     '''Description: output system configs info into three json files:[
0558         (/out/${product_name}/build_configs/subsystem_info/subsystem_build_config.json),
0559         (/out/${product_name}/build_configs/subsystem_info/src_subsystem_info.json),
0560         (/out/${product_name}/build_configs/subsystem_info/no_src_subsystem_info.json)]
0561     @parameter: none
0562     @return :none
0563     '''
0564 
0565     def _generate_subsystem_configs(self):
0566 
0567         # The function has been implemented in module util/loader/subsystem_info.py
0568         LogUtil.hb_info(
0569             "generated subsystem build config to '{}/subsystem_info/subsystem_build_config.json'".format(
0570                 self.config_output_dir), mode=self.config.log_mode)
0571         LogUtil.hb_info(
0572             "generated src subsystem info to '{}/subsystem_info/src_subsystem_info.json'".format(
0573                 self.config_output_dir), mode=self.config.log_mode)
0574         LogUtil.hb_info(
0575             "generated no src subsystem info to '{}/subsystem_info/no_src_subsystem_info.json'".format(
0576                 self.config_output_dir), mode=self.config.log_mode)

已经由util/loader/subsystem_info.py实现,这里不再需要实现

生成目标平台部件

build/hb/services/loader.py

0362     '''Description: output all target platform parts into a json file \
0363         (/out/${product_name}/build_configs/target_platforms_parts.json)
0364     @parameter:none
0365     @return :none
0366     '''
0367 
0368     def _generate_target_platform_parts(self):
0369         target_platform_parts_file = os.path.join(self.config_output_dir,
0370                                                   "target_platforms_parts.json")
0371         write_json_file(target_platform_parts_file,
0372                         self.target_platform_parts,
0373                         check_changes=True)
0374         LogUtil.hb_info("generate target platform parts to '{}'".format(
0375             target_platform_parts_file), mode=self.config.log_mode)

生成系统能力

build/hb/services/loader.py

0528     '''Description: output system capabilities into a json file. \
0529         (/out/${product_name}/build_configs/${platform}_system_capabilities.json)
0530     @parameter: none
0531     @return :none
0532     '''
0533 
0534     def _generate_system_capabilities(self):
0535         for platform in self.build_platforms:
0536             platform_parts = self.target_platform_parts.get(platform)
0537             platform_capabilities = []
0538             for _, origin in platform_parts.items():
0539                 # parts_info.get() might be None if the part is a binary package
0540                 all_parts_variants = self.parts_info.get(origin)
0541                 if all_parts_variants is None:
0542                     continue
0543                 part = all_parts_variants[0]
0544                 if part.get('system_capabilities'):
0545                     entry = part.get('system_capabilities')
0546                     if len(entry) > 0:
0547                         platform_capabilities.extend(entry)
0548             platform_part_json_file = os.path.join(
0549                 self.config_output_dir, "{0}_system_capabilities.json".format(platform))
0550             write_json_file(platform_part_json_file,
0551                             sorted(platform_capabilities),
0552                             check_changes=True)
0553             LogUtil.hb_info(
0554                 "generated system capabilities to '{}/{}_system_capabilities.json'".format(
0555                     self.config_output_dir, platform), mode=self.config.log_mode)

生成桩目标

build/hb/services/loader.py

0515     '''Description: output system configs info into 2 files:[
0516         (/out/${product_name}/build_configs/subsystem_info/${platform}-stub/BUILG.gn),
0517         (/out/${product_name}/build_configs/subsystem_info/${platform}-stub/zframework_stub_exists.gni)]
0518     @parameter: none
0519     @return :none
0520     '''
0521 
0522     def _generate_stub_targets(self):
0523         generate_targets_gn.gen_stub_targets(
0524             self.parts_config_info.get('parts_kits_info'),
0525             self.target_platform_stubs,
0526             self.config_output_dir)

基于源码生成平台部件

build/hb/services/loader.py

0474     '''Description: output platforms part by src into a json file. \
0475         (/out/${product_name}/build_configs/platforms_parts_by_src.json)
0476     @parameter: none
0477     @return :none
0478     '''
0479 
0480     def _generate_platforms_part_by_src(self):
0481         platforms_parts_by_src = self._get_platforms_parts()
0482         platforms_parts_by_src_file = os.path.join(self.source_root_dir,
0483                                                    self.config_output_relpath,
0484                                                    "platforms_parts_by_src.json")
0485         write_json_file(platforms_parts_by_src_file,
0486                         platforms_parts_by_src,
0487                         check_changes=True)
0488         LogUtil.hb_info("generated platforms parts by src to '{}'".format(
0489             platforms_parts_by_src_file), mode=self.config.log_mode)

生成目标gn

build/hb/services/loader.py

0491     '''Description: output system configs info into 4 files:[
0492         (/out/${product_name}/build_configs/subsystem_info/parts_list.gni),
0493         (/out/${product_name}/build_configs/subsystem_info/inner_kits_list.gni),
0494         (/out/${product_name}/build_configs/subsystem_info/system_kits_list.gni),
0495         (/out/${product_name}/build_configs/subsystem_info/parts_test_list.gni),
0496         (/out/${product_name}/build_configs/subsystem_info/BUILD.gn)]
0497     @parameter: none
0498     @return :none
0499     '''
0500 
0501     def _generate_target_gn(self):
0502         generate_targets_gn.gen_targets_gn(self.required_parts_targets,
0503                                            self.config_output_dir)

生成伪目标构建文件

build/hb/services/loader.py

0505     '''Description: output phony targets build file. \
0506         (/out/${product_name}/build_configs/phony_target/BUILD.gn)
0507     @parameter: none
0508     @return :none
0509     '''
0510 
0511     def _generate_phony_targets_build_file(self):
0512         generate_targets_gn.gen_phony_targets(self.required_phony_targets,
0513                                               self.config_output_dir)

生成必要的部件目标

build/hb/services/loader.py

0461     '''Description: output build target info into a json file. \
0462         (/out/${product_name}/build_configs/required_parts_targets.json)
0463     @parameter: none
0464     @return: none
0465     '''
0466 
0467     def _generate_required_parts_targets(self):
0468         build_targets_info_file = os.path.join(self.config_output_dir,
0469                                                "required_parts_targets.json")
0470         write_json_file(build_targets_info_file, self.required_parts_targets)
0471         LogUtil.hb_info("generate required parts targets to '{}'".format(
0472             build_targets_info_file), mode=self.config.log_mode)

生成必要的部件目标列表

build/hb/services/loader.py

0447     '''Description: output build target list into a json file.\
0448         (/out/${product_name}/build_configs/required_parts_targets_list.json)
0449     @parameter: none
0450     @return :none
0451     '''
0452 
0453     def _generate_required_parts_targets_list(self):
0454         build_targets_list_file = os.path.join(self.config_output_dir,
0455                                                "required_parts_targets_list.json")
0456         write_json_file(build_targets_list_file,
0457                         list(self.required_parts_targets.values()))
0458         LogUtil.hb_info("generate build targets list file to '{}'".format(
0459             build_targets_list_file), mode=self.config.log_mode)

生成源码对应的标识

build/hb/services/loader.py

0431     '''Description: output src flag into a json file. \
0432         (/out/${product_name}/build_configs/parts_src_flag.json)
0433     @parameter: none
0434     @return :none
0435     '''
0436 
0437     def _generate_src_flag(self):
0438         parts_src_flag_file = os.path.join(self.config_output_dir,
0439                                            "parts_src_flag.json")
0440         write_json_file(parts_src_flag_file,
0441                         self._get_parts_src_list(),
0442                         check_changes=True)
0443         LogUtil.hb_info(
0444             "generated parts src flag to '{}/subsystem_info/parts_src_flag.json'".format(
0445                 self.config_output_dir), mode=self.config.log_mode)

生成自动安装部件

build/hb/services/loader.py

0412     '''Description: output auto install part into a json file. \
0413         (/out/${product_name}/build_configs/auto_install_parts.json)
0414     @parameter: none
0415     @return: none
0416     '''
0417 
0418     def _generate_auto_install_part(self):
0419         parts_path_info = self.parts_config_info.get("parts_path_info")
0420         auto_install_part_list = []
0421         for part, path in parts_path_info.items():
0422             if str(path).startswith("drivers/interface") or \
0423                     str(path).startswith("third_party"):
0424                 auto_install_part_list.append(part)
0425         auto_install_list_file = os.path.join(
0426             self.config_output_dir, "auto_install_parts.json")
0427         write_json_file(auto_install_list_file, auto_install_part_list)
0428         LogUtil.hb_info("generate auto install part to '{}'".format(
0429             auto_install_list_file), mode=self.config.log_mode)

生成平台列表

build/hb/services/loader.py

0393     '''Description: output platforms list into a gni file. \
0394         (/out/${product_name}/build_configs/platforms_list.gni)
0395     @parameter: none
0396     @return: none
0397     '''
0398 
0399     def _generate_platforms_list(self):
0400         platforms_list_gni_file = os.path.join(self.config_output_dir,
0401                                                "platforms_list.gni")
0402         _platforms = set(self.build_platforms)
0403         _gni_file_content = ['target_platform_list = [', '  "{}"'.format('",\n  "'.join(_platforms)), ']',
0404                              'kits_platform_list = [', '  "{}",'.format('",\n  "'.join(_platforms))]
0405         if 'phone' not in self.build_platforms:
0406             _gni_file_content.append('  "phone"')
0407         _gni_file_content.append(']')
0408         write_file(platforms_list_gni_file, '\n'.join(_gni_file_content))
0409         LogUtil.hb_info("generate platforms list to '{}'".format(
0410             platforms_list_gni_file), mode=self.config.log_mode)

生成部件差异化信息

build/hb/services/loader.py

0377     '''Description: Generate parts differences in different platforms, using phone as base. \
0378         (/out/${product_name}/build_configs/parts_different_info.json)
0379     @parameter: none
0380     @return :none
0381     '''
0382 
0383     def _generate_part_different_info(self):
0384         parts_different_info = self._get_parts_by_platform()
0385         parts_different_info_file = os.path.join(self.config_output_dir,
0386                                                  "parts_different_info.json")
0387         write_json_file(parts_different_info_file,
0388                         parts_different_info,
0389                         check_changes=True)
0390         LogUtil.hb_info("generate part different info to '{}'".format(
0391             parts_different_info_file), mode=self.config.log_mode)

生成测试框架信息

build/hb/services/loader.py

0339     '''Description: output infos for testfwk into a json file. \
0340         (/out/${product_name}/build_configs/infos_for_testfwk.json)
0341     @parameter:none
0342     @return :none
0343     '''
0344 
0345     def _generate_infos_for_testfwk(self):
0346         infos_for_testfwk_file = os.path.join(self.config_output_dir,
0347                                               "infos_for_testfwk.json")
0348         parts_info = self.parts_config_info.get('parts_info')
0349         parts_info_dict = {}
0350         for _part_name, _parts in parts_info.items():
0351             for _info in _parts:
0352                 parts_info_dict[_info.get('part_name')] = _info
0353         _output_infos = {}
0354         for _platform, _parts in self.target_platform_parts.items():
0355             result = self._output_infos_by_platform(_parts, parts_info_dict)
0356             _output_infos[_platform] = result
0357         write_json_file(infos_for_testfwk_file,
0358                         _output_infos, check_changes=True)
0359         LogUtil.hb_info("generate infos for testfwk to '{}'".format(
0360             infos_for_testfwk_file), mode=self.config.log_mode)

检查产品部件特性

build/hb/services/loader.py

0176     @throw_exception
0177     def _check_product_part_feature(self):
0178         LogUtil.hb_info("Checking all product features...")
0179         product_preloader_dir = os.path.dirname(self.platforms_config_file)
0180         _preloader_feature_file = os.path.join(product_preloader_dir,
0181                                                'features.json')
0182         _preloader_feature_info = read_json_file(_preloader_feature_file)
0183         part_to_feature = _preloader_feature_info.get('part_to_feature')
0184         _feature_whitelist_file = os.path.join(
0185             self.source_root_dir, "out/products_ext", "component_feature_whitelist.json"
0186         )
0187         if not os.path.exists(_feature_whitelist_file):
0188             _feature_whitelist_file = os.path.join(
0189                 self.source_root_dir, "build/", "component_feature_whitelist.json"
0190             )
0191         _feature_whitelist_info = read_json_file(_feature_whitelist_file)
0192         _feature_whitelist_list = []
0193         if _feature_whitelist_info:
0194             _feature_whitelist_list = list(_feature_whitelist_info.keys())
0195         for key, vals in part_to_feature.items():
0196             part = self.parts_info.get(key)
0197             if part is None:
0198                 continue
0199             _p_info = part[0]
0200             def_feature_list = _p_info.get('feature_list')
0201             if vals and not def_feature_list:
0202                 message = "The product use a feature vals='{}', but that is not defined " \
0203                       "in this part bundle.json file, part_name='{}'".format(vals, key)
0204                 if key not in _feature_whitelist_list:
0205                     raise OHOSException(message, "2006")
0206                 LogUtil.hb_warning(message)
0207                 continue
0208             for _f_name in vals:
0209                 if _f_name not in def_feature_list:
0210                     raise OHOSException(
0211                         "The product use a feature that is not supported"
0212                         " by this part, part_name='{}', feature='{}'".format(
0213                             key, _f_name), "2006")

生成系统能力文件

build/hb/services/loader.py

0227 # generate method
0228 
0229     '''Description: Generate SystemCapability.json & syscap.json & syscap.para, dir:[
0230         (//out/preloader/${product_name}/system/etc/SystemCapability.json),
0231         (//out/preloader/${product_name}/system/etc/syscap.json),
0232         (//out/preloader/${product_name}/system/etc/param/syscap.para)]
0233     @parameter:none
0234     @return :none
0235     '''
0236     @throw_exception
0237     def _generate_syscap_files(self):
0238         pre_syscap_info_path = os.path.dirname(self.platforms_config_file)
0239         system_path = os.path.join(self.source_root_dir, os.path.join(
0240             os.path.dirname(self.platforms_config_file), "system/"))
0241         syscap_product_dict = read_json_file(
0242             os.path.join(pre_syscap_info_path, "syscap.json"))
0243         syscap_info_list = self.parts_config_info.get('syscap_info')
0244         target_syscap_with_part_name_list = []
0245         target_syscap_list = []
0246         target_syscap_for_init_list = []
0247         all_syscap_list = []
0248         for syscap in syscap_info_list:
0249             if syscap['component'] not in self.required_parts_targets_list:
0250                 continue
0251             if 'syscap' not in syscap or syscap['syscap'] is None \
0252                     or len(syscap['syscap']) == 0 or syscap['syscap'] == [""]:
0253                 continue
0254             for syscap_string in syscap['syscap']:
0255                 all_syscap_list.append(syscap_string.split('=')[0].strip())
0256 
0257         for key, value in syscap_product_dict['part_to_syscap'].items():
0258             for syscap in value:
0259                 if syscap not in all_syscap_list:
0260                     raise OHOSException(
0261                         "In config.json of part [{}],the syscap[{}] is incorrect, \
0262                         please check the syscap name".format(key, syscap), "2008")
0263 
0264         for syscap in syscap_info_list:
0265             remove_list = []
0266             if syscap['component'] not in self.required_parts_targets_list:
0267                 continue
0268             if 'syscap' not in syscap or syscap['syscap'] is None \
0269                     or len(syscap['syscap']) == 0 or syscap['syscap'] == [""]:
0270                 continue
0271             for syscap_string in syscap['syscap']:
0272                 if syscap_string.startswith("SystemCapability.") is True:
0273                     target_syscap_init_str = "const."
0274                     syscap_name = syscap_string.split('=')[0].strip()
0275                     all_syscap_product = syscap_product_dict['syscap']
0276                     if syscap_name in all_syscap_product and not all_syscap_product[syscap_name]:
0277                         remove_list.append(syscap_string)
0278                         continue
0279                     elif syscap_name in all_syscap_product and all_syscap_product[syscap_name]:
0280                         target_syscap_init_str += syscap_name + '=true\n'
0281                     else:
0282                         if syscap_string.endswith('true'):
0283                             target_syscap_init_str += syscap_name + '=true\n'
0284                         elif syscap_string.endswith('false'):
0285                             remove_list.append(syscap_string)
0286                             continue
0287                         else:
0288                             target_syscap_init_str += syscap_string + "=true\n"
0289                     if target_syscap_init_str not in target_syscap_for_init_list:
0290                         target_syscap_for_init_list.append(
0291                             target_syscap_init_str)
0292                 else:
0293                     raise OHOSException("""In bundle.json of part [{}], The syscap string [{}] is incorrect,
0294                     need start with \"SystemCapability.\"""".format(syscap['component'], syscap_string), "2009")
0295 
0296             for remove_str in remove_list:
0297                 syscap['syscap'].remove(remove_str)
0298             for i in range(len(syscap['syscap'])):
0299                 if syscap['syscap'][i].endswith('true') or syscap['syscap'][i].endswith('false'):
0300                     syscap['syscap'][i] = syscap['syscap'][i].split('=')[
0301                         0].strip()
0302 
0303             syscap['syscap'].sort()
0304             target_syscap_with_part_name_list.append(syscap)
0305             target_syscap_list.extend(syscap['syscap'])
0306 
0307         # Generate SystemCapability.json & syscap.json & syscap.para
0308         target_syscap_list.sort()
0309         syscap_info_dict = read_json_file(os.path.join(
0310             pre_syscap_info_path, "SystemCapability.json"))
0311         syscap_info_dict.update({'syscap': {'os': target_syscap_list}})
0312         system_etc_path = os.path.join(system_path, "etc/")
0313         if not os.path.exists(system_path):
0314             os.mkdir(system_path)
0315         if not os.path.exists(system_etc_path):
0316             os.mkdir(system_etc_path)
0317         syscap_info_json = os.path.join(
0318             system_etc_path, "SystemCapability.json")
0319         write_json_file(syscap_info_json, syscap_info_dict)
0320         LogUtil.hb_info(
0321             "generate syscap info file to '{}'".format(syscap_info_json), mode=self.config.log_mode)
0322         target_syscap_with_part_name_list.sort(
0323             key=lambda syscap: syscap['component'])
0324         syscap_info_with_part_name_file = os.path.join(
0325             system_etc_path, "syscap.json")
0326         write_json_file(syscap_info_with_part_name_file, {
0327             'components': target_syscap_with_part_name_list})
0328         LogUtil.hb_info("generate syscap info with part name list to '{}'".format(
0329             syscap_info_with_part_name_file), mode=self.config.log_mode)
0330         if not os.path.exists(os.path.join(system_etc_path, "param/")):
0331             os.mkdir(os.path.join(system_etc_path, "param/"))
0332         target_syscap_for_init_file = os.path.join(
0333             system_etc_path, "param/syscap.para")
0334         with open(target_syscap_for_init_file, "w") as file:
0335             file.writelines(sorted(target_syscap_for_init_list))
0336         LogUtil.hb_info("generate target syscap for init list to '{}'".format(
0337             target_syscap_for_init_file), mode=self.config.log_mode)

gn前处理

目前在buildargs.json中没有支持PRE_TARGET_GENERATE阶段的处理

gn处理

build/hb/modules/ohos_build_module.py

0088     def _target_generate(self):
0089         self._run_phase(BuildPhase.TARGET_GENERATE)
0090         if not self.args_dict.get("build_only_load").arg_value and not self.args_dict.get("fast_rebuild").arg_value:
0091             self.target_generator.run()

89行:先处理buildargs.json中登记的处理函数
90~91行:没有开启fast_rebuild选项的情况下才进行gn处理

gn阶段选项处理

build/resources/args/default/buildargs.json

0265     "resolve_function": "resolve_build_type",
0280     "resolve_function": "resolve_log_level",
0292     "resolve_function": "resolve_export_para",
0302     "resolve_function": "resolve_test",
0312     "resolve_function": "resolve_gn_args",
0322     "resolve_function": "resolve_gn_flags",
0334     "resolve_function": "resolve_compiler",
0344     "resolve_function": "resolve_fast_rebuild",
0360     "resolve_function": "resolve_root_perf_main",
0376     "resolve_function": "resolve_runtime_mode",

构建类型

日志等级

测试集选择

gn参数

gn flag

快速重建

gn执行

gn后处理

ninja前处理

ninja处理

ninja错误后继续

只生成gn

ninja执行

ninja后处理

root或user构建

设备类型

打包镜像

rom尺寸统计

ccache性能度量

汇总编译警告

ninja性能度量

计算overlap rate

依赖分析清理

postbuild

清理构建参数

附录A 重要的配置参数

product json

产品配置文件是所有编译配置的总入口,产品分为内置产品和一般产品
内置产品由productdefine/common/products/{product}.json来配置
一般产品由vendor/{company}/{product}/config.json来配置。它们的配置参数是类似的
下面以config.json来说明。这里company指开发这款产品的公司名,product为开发的产品名。

product_name

在config.json中指定,在配置./build.py -p product_name时,product_name参数需要与此信息进行匹配。
hb build -p product_name 或者 ./build.sh -p product_name同理。
一般情况下与vendor/{company}目录下的{product}子目录同名,也可以指定不同的名字。

board

开发板的名称

device_company

生产此开发板的公司

board_path

具体开发板相关的目录。
如果没有指定board_path, 则board_path可以通过
/device/{device_company}/{board}

/device/board/{device_company}/{board}
来生成。

target_cpu

产品使用的cpu架构,如arm, arm64, loongarch, x86等。

type

产品对应的系统类型,分别是

  • mini–轻量系统
  • small–小型系统
  • standard–标准系统

version

此config.json文件的版本号,目前是3.0版本

api version

API版本号,本产品的APP依赖的API版本

enable_ramdisk

是否启用虚拟磁盘(ramdisk)

enable_absystem

是否启用ab分区双启动能力

build_selinux

是否编译安全Linux内核

build_seccomp

TBD

inherit

继承其它json文件的配置信息列表,由于产品是由很多的部件构成,大多数产品可以复用大部分的公共部件,这些公共部件集合使用单独的json文件描述,然后在这里继承,则可以减小config.json文件的大小以及复杂度。

subsystems

本产品包含的子系统列表,当然还需要补充上inherit从其它json文件继承而来的子系统列表。

subsystem

子系统列表中的某个子系统

components

子系统中的部件列表

component

部件列表中的具体某部件名称

features

对应部件所包含的特性集合

bundle.json

早期仅用ohos.build来描述组件(部件),后来引入了bundle.json来描述。后者更易扩展和维护,ohos.build会逐渐退出历史舞台。

name

部件名称。一般使用 “@组织名/部件名” 的形式。

version

此部件的版本号

description

此部件的简要描述信息,让读者快速了解部件

homepage

与此部件相关的主页

license

此部件的版权

repository

此部件所在的代码仓库

domain

此部件所涉及的领域

language

此部件涉及的语言

publishAs

此部件以什么形式发布。如"code-segment"为以代码形式发布

private

是否私有部件

scripts

所涉及的脚本。TBD

tags

与此部件相关的标签列表

keywords

本部件关键字列表

envs

本部件环境变量列表

dirs

本部件相关的目录列表

author

本部件的作者,含姓名,邮件地址,主页信息

contributors

本部件的主要贡献者列表,含姓名,邮件地址,主页信息

segment

本部件的代码所在的目录位置

component

本部件进一步的详细信息

name

部件的名称

subsystem

部件所属的子系统,一个部件只会属于一个子系统

syscap

本部件所定义的能力列表

features

本部件的特性列表

adapted_system_type

本部件适配的系统类型

rom

本部件的rom占用估计

ram

本部件的ram占用估计

deps

本部件依赖的其他部件列表

build

构建本部件需要提供的更进一步信息

sub_component

子组件列表,每一个子组件关联一个BUILD.gn文件的编译目标

inner_kits

本部件提供给其它部件的接口描述信息

name

实现接口共享库的名称

heaer_file

提供给其它部件使用的头文件名称

header_base

头文件所在的目录

test

本部件相关的测试代码的编译目标列表

subsystem_config.json

未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值