详细深入分析OpenHarmony编译流程
前言
OpenHarmony做为复杂的操作系统软件,其编译构建也比较复杂。本文主要记录作者自己在阅读编译脚本代码后对其的认识,不代表官方观点,不足之处还请批评指正。
本文顺着编译流程阅读和解释编译脚本代码,即执行时间靠前的代码先解释,执行时间靠后的代码后解释。
本文参考代码为2024年7月8日master分支代码。
为了方便查看OpenHarmony代码,作者搭建了一个服务器,欢迎使用https://lengqinjie.xyz。
对于master分支代码,此服务器上的代码每小时更新一次,所以当你看到时,可能和本文有差异,属于正常情况。
编译入口
共有3种形式的编译入口
- hb build
- ./build.sh
- ./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种使用方式
- hb (build | set | clean | env | tool | help | … ) args
- 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定义了一个编译构造的流程,分别是
- prebuild
- preload
- load
- pre_target_generate
- target_generate
- post_target_generate
- pre_target_compilation
- target_compilation
- post_target_compilation
- 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
未完待续