OpenHarmony做为复杂的操作系统软件,其编译构建也比较复杂。本文主要记录作者自己在阅读编译脚本代码后对其的认识,不代表官方观点,不足之处还请批评指正。
本文顺着编译流程阅读和解释编译脚本代码,即执行时间靠前的代码先解释,执行时间靠后的代码后解释。
本文参考代码为2023年3月30日master分支代码。
为了方便查看OpenHarmony代码,作者搭建了一个服务器,欢迎使用https://www.lengqinjie.xyz。
对于master分支代码,此服务器上的代码每日更新一次,所以当你看到时,可能和本文有差异,属于正常情况。
你也可以自行从gitee下载此版本代码,使用如下命令,git工具下载方法请参考互联网。
git clone https://gitee.com/openharmony/build.git -b master
cd build
git reset --hard 9574463e6f568f81b8d89134306052782c1f4c14
共有3种形式的编译入口
3种形式的编译入口最终都汇总到hb build。即内部实际上是统一的。下面以build.py为例进行讲解。
源码根目录下有很多子目录,这些子目录大多数为OpenHarmony各软件子系统的实现,其中1个目录为build目录,即本文重点讨论的内容,此目录内主要为编译构建脚本,主要是python语言实现。
为了提供方便,源码根目录下有2个符号链接,引用了编译构建的脚本文件
build.py -> build/lite/build.py
build.sh -> build/build_scripts/build.sh
因此在源码根目录下执行这2个文件中的一个就能够启动编译构建过程。还有一种启动方式是hb命令,稍后再讲解。
以build.py为例,在源码根目录下执行
./build.py -p rk3568
这里-p rk3568意思是编译构建产品(rk3568)。当然还支持很多其它参数,为了简化,这里只列出这个最重要的参数
build/lite/build.py
0071 def main():
0072 root_path = os.path.dirname(os.path.abspath(__file__))
0073 ret_code = set_root_path(root_path)
0074 if ret_code != 0:
0075 return ret_code
0076 return build(root_path, sys.argv[1:])
0077
0078
0079 if __name__ == "__main__":
0080 sys.exit(main())
注意72行代码中的__file__指的是源码根目录下的build.py,而不是build/lite/build.py
因为是在源码根目录下执行的,所以root_path就是源码根目录。
build/lite/build.py
0053 def set_root_path(path):
0054 sys.path.insert(0, os.path.join(path, 'build/lite'))
0055 module = importlib.import_module('hb_internal.set.set')
0056 return module.set_root_path(root_path=path)
然后将build/lite加入python程序搜索路径,方便后续python脚本查找自定义模块,55行代码就用上了54行代码的结果,在build/lite子目录内查找hb_internal.set.set模块并导入,最后执行此模块的设置源码根目录的方法
build/lite/hb_internal/set/set.py
0048 def set_root_path(root_path=None):
0049 config = Config()
0050 if root_path is None:
0051 try:
0052 hb_info(f'hb root path: {config.root_path}')
0053 return 0
0054 except OHOSException:
0055 root_path = get_input('[OHOS INFO] Input code path: ')
0056 config.root_path = root_path
0057 return 0
这里49行为一个配置对象,整个编译构建系统只有1个config对象(后续再解释)。这里就把root_path记录到config对象中(56行)。
build/lite/build.py
0059 def build(path, args_list):
0060 python_executable = get_python()
0061 cmd = [python_executable, 'build/hb/main.py', 'build'] + args_list
0062 for args in args_list:
0063 if "using_hb_new=false" in args:
0064 cmd = [python_executable, 'build/lite/hb/__main__.py', 'build'] + args_list
0065 break
0066 if not os.path.exists(os.path.join(path,'build/hb/main.py')):
0067 cmd = [python_executable, 'build/lite/hb/__main__.py', 'build'] + args_list
0068 return check_output(cmd, cwd=path)
源码根目录记录成功以后,开始进行系统构建(build)。首先获取python解释器(60行), 然后再构造命令行参数(61行)。紧接着进行新老版本构建的判断,分别构造不同的命令行。本文不解释老版本构建方式,即不考虑(62到67行之间的内容)。最后执行构建命令并检查执行结果(68行)。
从61行代码可以看出,这里等价于执行
hb build args_list
前提条件是对build/hb目录中python代码进行了安装动作
pip install build/hb
build/lite/build.py
0025 def get_python():
0026 hb_main = importlib.import_module("hb.__main__")
0027 topdir = hb_main.find_top()
0028 python_base_dir = os.path.join(topdir, 'prebuilts/python')
0029 if os.path.exists(python_base_dir):
0030 python_dir = hb_main.search(python_base_dir, 'python3')
0031 return os.path.join(python_dir, 'python3')
0032 else:
0033 print("please execute build/prebuilts_download.sh")
0034 sys.exit()
这里的主要目的是判断源码根目录下是否有prebuilts/python这个目录,如果没有的话,则需要执行
build/prebuilts_download.sh(当然是在源码根目录执行了)去下载对应的python解释器程序文件,当然这里还会下载其它一些工具,
这样做的目的是减少用户自行安装软件失败的可能性,因为这些软件对版本有要求,随着openharmony代码的更新,可能需要新的工具软件或者软件的新版本。通过上述脚本下载的工具软件都会存放在prebuilts目录下。
上述程序返回的是python解释器程序的文件路径。
build/lite/hb/__main__.py
0035 def find_top():
0036 cur_dir = os.getcwd()
0037 while cur_dir != "/":
0038 hb_internal = os.path.join(cur_dir, 'build/lite/hb_internal')
0039 if os.path.exists(hb_internal):
0040 return cur_dir
0041 cur_dir = os.path.dirname(cur_dir)
0042 raise Exception("Please call hb utilities inside source root directory")
上述代码的意思是从build/lite/hb目录开始一直往父目录查找,如果找到一个目录,其内部存在build/lite/hb_internal目录,则此目录为源码根目录,最后返回源码根目录(40行)
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解释器成功获取后,就会使用构造的参数调用hb主程序
build/hb/main.py
0104 @staticmethod
0105 @throw_exception
0106 def main():
0107 main = Main()
0108 module_type = sys.argv[1]
0109 if module_type == 'build':
0110 module = main._init_build_module()
0111 elif module_type == 'set':
0112 module = main._init_set_module()
0113 elif module_type == 'env':
0114 module = main._init_env_module()
0115 elif module_type == 'clean':
0116 module = main._init_clean_module()
0117 elif module_type == 'tool':
0118 module = main._init_tool_module()
0119 elif module_type == 'help':
0120 for all_module_type in ModuleType:
0121 LogUtil.hb_info(Separator.long_line)
0122 LogUtil.hb_info(Arg.get_help(all_module_type))
0123 exit()
0124 else:
0125 raise OHOSException(
0126 'There is no such option {}'.format(module_type), '0018')
0127 try:
0128 module.run()
0129 except KeyboardInterrupt:
0130 for file in os.listdir(ARGS_DIR):
0131 if file.endswith('.json') and os.path.exists(os.path.join(ARGS_DIR, file)):
0132 os.remove(os.path.join(ARGS_DIR, file))
0133 print('User abort')
0134 return -1
0135 else:
0136 return 0
0137
0138
0139
0140 if __name__ == "__main__":
0141 sys.exit(Main.main())
本程序有2种使用方式
pip install build/hb
第二种方式即上文介绍的方式,参考build/lite/build.py第61行。
build/hb/main.py
0062 def _init_build_module(self) -> BuildModuleInterface:
0063 args_dict = Arg.parse_all_args(ModuleType.BUILD)
0064
0065 if args_dict.get("product_name").arg_value != '':
0066 set_args_dict = Arg.parse_all_args(ModuleType.SET)
0067 set_args_resolver = SetArgsResolver(set_args_dict)
0068 ohos_set_module = OHOSSetModule(set_args_dict, set_args_resolver, "")
0069 ohos_set_module.set_product()
0070
0071 preloader = OHOSPreloader()
0072 loader = OHOSLoader()
0073 generate_ninja = Gn()
0074 ninja = Ninja()
0075 build_args_resolver = BuildArgsResolver(args_dict)
0076
0077 return OHOSBuildModule(args_dict, build_args_resolver, preloader, loader, generate_ninja, ninja)
63行先将所有build能识别的参数进行解析,并存入args_dict字典中。
如果参数里面有"product_name", 那么还要执行set的逻辑(65到69行)。本文中的-p参数就是product_name的简写,所以这段逻辑会执行。
71到75行初始化几个对象,代表编译构建的几个阶段:预加载、加载、生成ninja脚本,执行ninja脚本,以及初始化build阶段各参数的解析器。
最后(77行)用这些对象初始化构建模块
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))
0071 LogUtil.hb_info('Cost time: {}'.format(self.build_time))
run函数实际上使用的是父类的函数,如下
hb/modules/interface/build_module_interface.py
0061 def run(self):
0062 try:
0063 self._prebuild()
0064 self._preload()
0065 self._load()
0066 self._pre_target_generate()
0067 self._target_generate()
0068 self._post_target_generate()
0069 self._pre_target_compilation()
0070 self._target_compilation()
0071 except OHOSException as exception:
0072 raise exception
0073 else:
0074 self._post_target_compilation()
0075 finally:
0076 self._post_build()
可以看到实际上run定义了一个编译构造的流程,分别是
后续会详细讲这10个流程
hb/containers/arg.py
0230 @staticmethod
0231 def parse_all_args(module_type: ModuleType) -> dict:
0232 args_dict = {}
0233 parser = argparse.ArgumentParser()
0234 all_args = Arg.read_args_file(module_type)
0235
0236 for arg in all_args.values():
0237 arg = dict(arg)
0238 ArgsFactory.genetic_add_option(parser, arg)
0239 oh_arg = Arg.create_instance_by_dict(arg)
0240 args_dict[oh_arg.arg_name] = oh_arg
0241
0242 parser.usage = 'hb {} [option]'.format(module_type.name.lower())
0243 parser_args = parser.parse_known_args(sys.argv[2:])
0244
0245 for oh_arg in args_dict.values():
0246 if isinstance(oh_arg, Arg):
0247 assigned_value = parser_args[0].__dict__[oh_arg.arg_name]
0248 if oh_arg.arg_type == ArgType.LIST:
0249 convert_assigned_value = TypeCheckUtil.tile_list(assigned_value)
0250 convert_assigned_value = list(set(convert_assigned_value))
0251 elif oh_arg.arg_type == ArgType.SUBPARSERS:
0252 convert_assigned_value = TypeCheckUtil.tile_list(assigned_value)
0253 if len(convert_assigned_value):
0254 convert_assigned_value = list(set(convert_assigned_value))
0255 convert_assigned_value.extend(parser_args[1])
0256 convert_assigned_value.sort(key=sys.argv[2:].index)
0257 elif oh_arg.arg_type == ArgType.BOOL or oh_arg.arg_type == ArgType.GATE:
0258 if str(assigned_value).lower() == 'false':
0259 convert_assigned_value = False
0260 elif str(assigned_value).lower() == 'true':
0261 convert_assigned_value = True
0262 else:
0263 convert_assigned_value = assigned_value
0264
0265 if oh_arg.arg_attribute.get('deprecated', None) and oh_arg.arg_value != convert_assigned_value:
0266 LogUtil.hb_warning(
0267 'compile option "{}" will be deprecated, \
0268 please consider use other options'.format(oh_arg.arg_name))
0269 oh_arg.arg_value = convert_assigned_value
0270 Arg.write_args_file(
0271 oh_arg.arg_name, oh_arg.arg_value, module_type)
0272
0273 return args_dict
231行: 参数类型可以是build, set或者其它类型,
232行: 解析完的参数存储在此字典,并最后返回
233行:通用的参数解析器对象
234行:读取此类型支持的参数列表,由json配置文件存储(这些文件在build中的某个地方,后续分析)
236行:遍历本模块(如build)支持的参数
237行:每个参数的描述信息也是一个字典形式
238行:命令行加入对此参数的识别能力
239行:创建一个描述本参数的软件对象
240行:记录下参数名称与参数对象的映射关系
243行:先解析此模块能识别的参数,比如本文的“-p rk3568”,注意参数解析后,原来的参数表(sys.argv)并不会减少,下一个模块还能继续解析。
245行:遍历支持的参数表中的所有参数
247行:读取已解析出的参数值,如本例的rk3568,针对用户没有配置的参数,则读取的是默认值
248~263行:对参数值进行规范化处理
265~268行:如果使用了一个过期的参数,且和默认值不同,即用户指定了其他值,则打印警告提示日志
269~270行:更新参数对象的值并刷新参数配置文件
273行:最后返回参数对象表。
hb/containers/arg.py
0297 @staticmethod
0298 @throw_exception
0299 def read_args_file(module_type: ModuleType):
0300 args_file_path = ''
0301 default_file_path = ''
0302 if module_type == ModuleType.BUILD:
0303 args_file_path = CURRENT_BUILD_ARGS
0304 default_file_path = DEFAULT_BUILD_ARGS
0305 elif module_type == ModuleType.SET:
0306 args_file_path = CURRENT_SET_ARGS
0307 default_file_path = DEFAULT_SET_ARGS
0308 elif module_type == ModuleType.CLEAN:
0309 args_file_path = CURRENT_CLEAN_ARGS
0310 default_file_path = DEFAULT_CLEAN_ARGS
0311 elif module_type == ModuleType.ENV:
0312 args_file_path = CURRENT_ENV_ARGS
0313 default_file_path = DEFAULT_ENV_ARGS
0314 elif module_type == ModuleType.TOOL:
0315 args_file_path = CURRENT_TOOL_ARGS
0316 default_file_path = DEFAULT_TOOL_ARGS
0317 else:
0318 raise OHOSException(
0319 'You are trying to read args file, but there is no corresponding module "{}" args file'
0320 .format(module_type.name.lower()), "0018")
0321 if not os.path.exists(CURRENT_ARGS_DIR):
0322 os.makedirs(CURRENT_ARGS_DIR)
0323 if not os.path.exists(args_file_path):
0324 IoUtil.copy_file(src=default_file_path, dst=args_file_path)
0325 return IoUtil.read_json_file(args_file_path)
以build类型的参数文件为例,其它类似。
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 CURRENT_ARGS_DIR = os.path.join(CURRENT_OHOS_ROOT, 'out/hb_args')
0042 CURRENT_BUILD_ARGS = os.path.join(
0043 CURRENT_ARGS_DIR, 'buildargs.json')
0044 CURRENT_SET_ARGS = os.path.join(
0045 CURRENT_ARGS_DIR, 'setargs.json')
0046 CURRENT_CLEAN_ARGS = os.path.join(
0047 CURRENT_ARGS_DIR, 'cleanargs.json')
0048 CURRENT_ENV_ARGS = os.path.join(
0049 CURRENT_ARGS_DIR, 'envargs.json')
0050 CURRENT_TOOL_ARGS = os.path.join(
0051 CURRENT_ARGS_DIR, 'toolargs.json')
hb build, hb set, hb clean等参数文件位置和文件名称定义如上
以build为例
hb/containers/arg.py
0275 @staticmethod
0276 @throw_exception
0277 def write_args_file(key: str, value, module_type: ModuleType):
0278 args_file_path = ''
0279 if module_type == ModuleType.BUILD:
0280 args_file_path = CURRENT_BUILD_ARGS
0281 elif module_type == ModuleType.SET:
0282 args_file_path = CURRENT_SET_ARGS
0283 elif module_type == ModuleType.CLEAN:
0284 args_file_path = CURRENT_CLEAN_ARGS
0285 elif module_type == ModuleType.ENV:
0286 args_file_path = CURRENT_ENV_ARGS
0287 elif module_type == ModuleType.TOOL:
0288 args_file_path = CURRENT_TOOL_ARGS
0289 else:
0290 raise OHOSException(
0291 'You are trying to write args file, but there is no corresponding module "{}" args file'
0292 .format(module_type), "0002")
0293 args_file = Arg.read_args_file(module_type)
0294 args_file[key]["argDefault"] = value
0295 IoUtil.dump_json_file(args_file_path, args_file)
上述函数根据用户配置的参数值,刷新参数配置文件。
参考build/lite/hb_internal/set/set.py的第49行,这里是config对象的首次创建,我们来看看其怎么初始化的
build/lite/hb_internal/common/config.py
0034 class Config(metaclass=Singleton):
0035 def __init__(self):
0036 self.config_json = CONFIG_JSON
0037
0038 config_content = read_json_file(self.config_json)
0039 self._root_path = config_content.get('root_path', None)
0040 self._board = config_content.get('board', None)
0041 self._kernel = config_content.get('kernel', None)
0042 self._product = config_content.get('product', None)
0043 self._product_path = config_content.get('product_path', None)
0044 self._device_path = config_content.get('device_path', None)
0045 self._device_company = config_content.get('device_company', None)
0046 self._patch_cache = config_content.get('patch_cache', None)
0047 self._version = config_content.get('version', '3.0')
0048 self._os_level = config_content.get('os_level', 'small')
0049 self._product_json = config_content.get('product_json', None)
0050 self._target_cpu = config_content.get('target_cpu', None)
0051 self._target_os = config_content.get('target_os', None)
0052 self._out_path = config_content.get('out_path', None)
0053 self._compile_config = config_content.get('compile_config', None)
0054 self._component_type = config_content.get('component_type', None)
0055 self._device_config_path = config_content.get('device_config_path',
0056 None)
0057 self._product_config_path = config_content.get('product_config_path',
0058 None)
0059 self._subsystem_config_json = config_content.get(
0060 'subsystem_config_json', None)
0061 self._subsystem_config_overlay_json = ''
0062 self.fs_attr = set()
0063 self.platform = platform.system()
34行:Config类是一个单实例类,即只会存在一个Config类型的对象
36行:记录config配置文件的位置
38行:读取config配置文件
39~60行:将读取到的值记录到config对象,如果值不存在,则记录为None。
61~62行:初始化2个空的配置属性
63行:记录当前平台类型,如windows, linux, macos等
build/lite/hb_internal/__init__.py
0022 def get_config_path():
0023 search_path = os.getcwd()
0024 root_path = os.path.abspath(os.sep)
0025 while search_path != root_path:
0026 config_path = os.path.join(search_path, 'ohos_config.json')
0027 if os.path.isfile(config_path):
0028 return config_path
0029 search_path = os.path.dirname(search_path)
0030 return os.path.abspath(
0031 os.path.join(os.path.dirname(__file__), 'common', 'config.json'))
0032
0033
0034 CONFIG_JSON = get_config_path()
从上述代码可知:
如果根目录下存在ohos_config.json,则配置文件为ohos_config.json
否则配置文件为build/lite/hb_internal/common/config.json
刚开始时ohos_config.json不存在,什么时候生成的呢?
请看如下代码逻辑
build/lite/hb_internal/common/config.py
0137 @root_path.setter
0138 def root_path(self, value):
0139 self._root_path = os.path.abspath(value)
0140 if not os.path.isdir(self._root_path):
0141 raise OHOSException(f'{self._root_path} is not a valid path')
0142
0143 config_path = os.path.join(self._root_path, 'ohos_config.json')
0144 if not os.path.isfile(config_path):
0145 self.config_create(config_path)
0146 self.config_update('root_path', self._root_path)
0384 def config_create(self, config_path):
0385 dump_json_file(config_path, CONFIG_STRUCT)
0386 self.config_json = config_path
build/lite/hb_internal/init.py
0035 CONFIG_STRUCT = {
0036 "root_path": None,
0037 "board": None,
0038 "kernel": None,
0039 "product": None,
0040 "product_path": None,
0041 "device_path": None,
0042 "device_company": None,
0043 "os_level": None,
0044 "version": None,
0045 "patch_cache": None
0046 }
build/lite/hb_internal/set/set.py
0056 config.root_path = root_path
56行: 触发setter的执行,从而创建ohos_config.json
刚开始时,除了在配置对象中设置了源码根目录的位置,其它数据都是None, 那么某产品(如rk3568)的实际配置是怎么获取到的呢
让我们回到build/hb/main.py第69行,这里会刷新产品信息
build/hb/main.py
0069 ohos_set_module.set_product()
然后我们看看set_product的实现
build/hb/modules/ohos_set_module.py
0046 def set_product(self):
0047 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 product_info.get('name'), ModuleType.SET)
41行:产品名称为空,则弹出菜单让用户选择产品并获取产品配置
43行:产品名称含@符号,则用公司名和产品名一起查找产品配置
47行:直接用产品名查找产品配置
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
0181 @property
0182 def device_company(self):
0183 if self._device_company is None:
0184 raise OHOSException('Failed to init compile config', '0019')
0185 return self._device_company
0186
0187 @device_company.setter
0188 def device_company(self, value):
0189 self._device_company = value
0190 self.config_update('device_company', self._device_company)
如上。setter方法用于修改device_company属性时,property方法用于读取device_company属性时。而config_update会刷新配置文件
build/hb/resources/config.py
0378 def config_update(self, key, value):
0379 config_content = IoUtil.read_json_file(self.config_json)
0380 config_content[key] = value
0381 IoUtil.dump_json_file(self.config_json, config_content)
因此ohos_config.json会得到及时的刷新
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
0011 "resolve_function": "resolve_product",
0028 "resolve_function": "resolve_target_cpu",
0048 "resolve_function": "resolve_ccache",
0058 "resolve_function": "resolve_pycache",
0094 "resolve_function": "resolve_build_target",
0104 "resolve_function": "resolve_ninja_args",
0116 "resolve_function": "resolve_full_compilation",
还有2个参数标记成deprecated(过期)状态,这里没有列出
build/hb/resolver/build_args_resolver.py
0049 @staticmethod
0050 def resolve_product(target_arg: Arg, build_module: BuildModuleInterface):
0051 """resolve '--product-name' arg.
0052 :param target_arg: arg object which is used to get arg value.
0053 :param build_module [maybe unused]: build module object which is used to get other services.
0054 :phase: prebuild.
0055 """
0056 config = Config()
0057 target_generator = build_module.target_generator
0058 target_generator.regist_arg('product_name', config.product)
0059 target_generator.regist_arg('product_path', config.product_path)
0060 target_generator.regist_arg(
0061 'product_config_path', config.product_config_path)
0062
0063 target_generator.regist_arg('device_name', config.board)
0064 target_generator.regist_arg('device_path', config.device_path)
0065 target_generator.regist_arg('device_company', config.device_company)
0066 target_generator.regist_arg(
0067 'device_config_path', config.device_config_path)
0068
0069 target_generator.regist_arg('target_cpu', config.target_cpu)
0070 target_generator.regist_arg(
0071 'is_{}_system'.format(config.os_level), True)
0072
0073 target_generator.regist_arg('ohos_kernel_type', config.kernel)
0074 target_generator.regist_arg('ohos_build_compiler_specified',
0075 ProductUtil.get_compiler(config.device_path))
0076
0077 target_generator.regist_arg('ohos_build_time',
0078 SystemUtil.get_current_time(time_type='timestamp'))
0079 target_generator.regist_arg('ohos_build_datetime',
0080 SystemUtil.get_current_time(time_type='datetime'))
0081
0082 features_dict = ProductUtil.get_features_dict(config.product_json)
0083 for key, value in features_dict.items():
0084 target_generator.regist_arg(key, value)
0085
0086 if ProductUtil.get_compiler(config.device_path) == 'clang':
0087 target_generator.regist_arg(
0088 'ohos_build_compiler_dir', config.clang_path)
0089
0090 if target_arg.arg_value == 'ohos-sdk':
0091 target_generator = build_module.target_generator
0092 target_generator.regist_arg('build_ohos_sdk', True)
0093 target_generator.regist_arg('build_ohos_ndk', True)
0094 if len(build_module.args_dict['build_target'].arg_value) == 0:
0095 build_module.args_dict['build_target'].arg_value = [
0096 'build_ohos_sdk']
0097 build_module.args_dict['target_cpu'].arg_value = 'arm64'
56~80行:将config配置对象的数据读出并记录到target_generator对象中(后续gn程序会使用到),这些config配置对象在之前hb set 处理中已设置好。
82~84行:读取产品对应的特性配置,对于rk3568来说,其在vendor/hihope/rk3568/config.json中,并记录下来
86~88行:读取并记录clang编译器的目录位置
90~97行:如果编译的产品是openharmony sdk, 则需要记录一些特殊的参数
build/hb/resolver/build_args_resolver.py
0099 @staticmethod
0100 def resolve_target_cpu(target_arg: Arg, build_module: BuildModuleInterface):
0101 """resolve '--target-cpu' arg.
0102 :param target_arg: arg object which is used to get arg value.
0103 :param build_module [maybe unused]: build module object which is used to get other services.
0104 :phase: prebuild.
0105 """
0106 config = Config()
0107 default_build_args = IoUtil.read_json_file(DEFAULT_BUILD_ARGS)
0108 if config.target_cpu == "":
0109 config.target_cpu = target_arg.arg_value
0110 elif target_arg.arg_value != default_build_args.get("target_cpu").get("argDefault"):
0111 config.target_cpu = target_arg.arg_value
target_cpu表示此代码最终编译出来后在什么cpu架构上执行。
对于./build.py -p rk3568场景
由于hb set逻辑已经设置了target_cpu所以,这里if条件不满足
110~111行:用户指定了target_cpu参数,且与默认值不同,则更新一下。
build/hb/resolver/build_args_resolver.py
0163 @staticmethod
0164 def resolve_ccache(target_arg: Arg, build_module: BuildModuleInterface):
0165 """resolve '--ccache' arg
0166 :param target_arg: arg object which is used to get arg value.
0167 :param build_module [maybe unused]: build module object which is used to get other services.
0168 :phase: prebuild.
0169 """
0170 if target_arg.arg_value:
0171 config = Config()
0172 ccache_path = find_executable('ccache')
0173 if ccache_path is None:
0174 LogUtil.hb_warning('Failed to find ccache, ccache disabled.')
0175 return
0176 else:
0177 target_generator = build_module.target_generator
0178 target_generator.regist_arg(
0179 'ohos_build_enable_ccache', target_arg.arg_value)
0180
0181 ccache_local_dir = os.environ.get('CCACHE_LOCAL_DIR')
0182 ccache_base = os.environ.get('CCACHE_BASE')
0183 if not ccache_local_dir:
0184 ccache_local_dir = '.ccache'
0185 if not ccache_base:
0186 ccache_base = os.environ.get('HOME')
0187 ccache_base = os.path.join(ccache_base, ccache_local_dir)
0188 if not os.path.exists(ccache_base):
0189 os.makedirs(ccache_base)
0190
0191 ccache_log_suffix = os.environ.get('CCACHE_LOG_SUFFIX')
0192 if ccache_log_suffix:
0193 logfile = os.path.join(
0194 ccache_base, "ccache.{}.log".format(ccache_log_suffix))
0195 else:
0196 logfile = os.path.join(ccache_base, "ccache.log")
0197 if os.path.exists(logfile):
0198 oldfile = os.path.join(ccache_base, '{}.old'.format(logfile))
0199 if os.path.exists(oldfile):
0200 os.unlink(oldfile)
0201 os.rename(logfile, oldfile)
0202
0203 os.environ['CCACHE_EXEC'] = ccache_path
0204 os.environ['CCACHE_LOGFILE'] = logfile
0205 os.environ['USE_CCACHE'] = '1'
0206 os.environ['CCACHE_DIR'] = ccache_base
0207 os.environ['CCACHE_UMASK'] = '002'
0208 os.environ['CCACHE_BASEDIR'] = config.root_path
0209 ccache_max_size = os.environ.get('CCACHE_MAXSIZE')
0210 if not ccache_max_size:
0211 ccache_max_size = '50G'
0212
0213 cmd = ['ccache', '-M', ccache_max_size]
0214
0215 SystemUtil.exec_command(cmd, log_path=config.log_path)
ccache是一个工具,可以缓存C/C++编译过程的中间结果,这样下次编译的时候可以从中间结果继续往下执行,加快编译速度。
本段代码处理ccache功能打开或关闭的逻辑。当前此功能默认打开。
172~175行:查找ccache工具程序路径
176~179行:记录ccache功能开关
181~189行:查找或创建ccache输出文件夹
191~201行:ccache功能日志文件创建或重命名
203~208行:将ccache一些参数记入环境变量
209~215行:设置ccache的缓冲区尺寸
pycache是一个可以加快python程序性能的工具,默认是关闭的
build/hb/resolver/build_args_resolver.py
0217 @staticmethod
0218 def resolve_pycache(target_arg: Arg, build_module: BuildModuleInterface):
0219 """resolve '--enable-pycache' arg
0220 :param target_arg: arg object which is used to get arg value.
0221 :param build_module [maybe unused]: build module object which is used to get other services.
0222 :phase: prebuild.
0223 """
0224 if target_arg.arg_value:
0225 config = Config()
0226 pycache_dir = os.environ.get('CCACHE_BASE')
0227 # The default value is HOME for local users
0228 if not pycache_dir:
0229 pycache_dir = os.environ.get('HOME')
0230 pycache_dir = os.path.join(pycache_dir, '.pycache')
0231 os.environ['PYCACHE_DIR'] = pycache_dir
0232 pyd_start_cmd = [
0233 'python3',
0234 '{}/build/scripts/util/pyd.py'.format(config.root_path),
0235 '--root',
0236 pycache_dir,
0237 '--start',
0238 ]
0239 cmd = ['/bin/bash', '-c', ' '.join(pyd_start_cmd), '&']
0240 subprocess.Popen(cmd)
224行:如果开启了pycache功能,则执行后面的逻辑
主要目标就是执行build/scripts/util/pyd.py脚本,此脚本不做分析,读者自己阅读代码即可。
操作系统编译构建可以有很多目标,如果不做指定,则会是整个操作系统镜像(非常大),也可以指定成一些具体的软件模块(相对较小)-比如二进制程序或者共享库等。可以通过hb tool查看当前支持哪些构建目标。
build/hb/resolver/build_args_resolver.py
0113 @staticmethod
0114 @throw_exception
0115 def resolve_build_target(target_arg: Arg, build_module: BuildModuleInterface):
0116 """resolve '--build-target' arg.
0117 :param target_arg: arg object which is used to get arg value.
0118 :param build_module [maybe unused]: build module object which is used to get other services.
0119 :phase: prebuild.
0120 :raise OHOSException: when build target not exist in compiling product.
0121 """
0122 config = Config()
0123 build_executor = build_module.target_compiler
0124 target_list = []
0125 if len(target_arg.arg_value):
0126 target_list = target_arg.arg_value
0127 else:
0128 if os.getcwd() == CURRENT_OHOS_ROOT:
0129 target_list = ['images']
0130 elif ComponentUtil.is_in_component_dir(os.getcwd()) and \
0131 ComponentUtil.is_component_in_product(
0132 ComponentUtil.get_component_name(os.getcwd()), Config().product):
0133 component_name = ComponentUtil.get_component_name(os.getcwd())
0134 LogUtil.write_log(Config().log_path, 'In the component "{}" directory,'
0135 'this compilation will compile only this component'.format(
0136 component_name),
0137 'warning')
0138 target_list.append(component_name)
0139 target_list.append(component_name + '_test')
0140 else:
0141 component_name = ComponentUtil.get_component_name(os.getcwd())
0142 component_name = os.path.basename(
0143 os.getcwd()) if component_name == '' else component_name
0144 raise OHOSException('There is no target component "{}" for the current product "{}"'
0145 .format(component_name, Config().product), "4001")
0146 build_executor.regist_arg('build_target', target_list)
125~126行,如果参数指明了构建目标,则直接记录下来
否则
128~129行,当前在代码根目录,则直接生成系统镜像
130~139行,如果在某组件目录,且此产品含有这个组件,则构建对应的组件
140~145行,组件不属于产品,报错提示
build/hb/resolver/build_args_resolver.py
0309 @staticmethod
0310 @throw_exception
0311 def resolve_ninja_args(target_arg: Arg, build_module: BuildModuleInterface):
0312 """resolve '--ninja-args' arg
0313 :param target_arg: arg object which is used to get arg value.
0314 :param build_module [maybe unused]: build module object which is used to get other services.
0315 :phase: prebuild.
0316 :raise OHOSException: when the value of the ninja parameter does not use quotation marks.
0317 """
0318 build_executor = build_module.target_compiler
0319 ninja_args_list = []
0320 for ninja_arg in target_arg.arg_value:
0321 ninja_arg = re.sub("'", "", ninja_arg)
0322 ninja_args_list.append(ninja_arg)
0323 build_executor.regist_arg('ninja_args', ninja_args_list)
主要就是把所有额外添加的单引号删除掉,回归ninja参数本来面目
build/hb/resolver/build_args_resolver.py
0242 @staticmethod
0243 def resolve_full_compilation(target_arg: Arg, build_module: BuildModuleInterface):
0244 """resolve '--full-compilation' arg
0245 :param target_arg: arg object which is used to get arg value.
0246 :param build_module [maybe unused]: build module object which is used to get other services.
0247 :phase: prebuild.
0248 """
0249 if target_arg.arg_value:
0250 build_executor = build_module.target_compiler
0251 target_list = build_executor.args_dict.get('build_target', None)
0252 if isinstance(target_list, list):
0253 target_list.append('make_all')
0254 target_list.append('make_test')
0255 else:
0256 build_executor.regist_arg(
0257 'build_target', ['make_all', 'make_test'])
在已有构建目标的基础上,增加全量构建目标,即会构建整个系统镜像
build/hb/modules/ohos_build_module.py
0076 def _preload(self):
0077 self._run_phase(BuildPhase.PRE_LOAD)
0078 if self.args_dict.get('fast_rebuild', None) and not self.args_dict.get('fast_rebuild').arg_value:
0079 self.preloader.run()
77行:先运行preload。
78~79行:如果开启了fast_rebuild,则会跳过preloader。
目前在buildarg json文件中没有查询到preload阶段的参数,所以,77行代码为空操作
对于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()
可以看出,其主要作用是生成一系列的json文件,接下来逐一进行描述
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
0043 def __post_init__(self):
0044 self._dirs = Dirs(self._config)
0045 self._outputs = Outputs(self._dirs.preloader_output_dir)
0046 self._product = Product(self._dirs, self._config)
0047 self._all_parts = self._product._parts
0048 self._build_vars = self._product._build_vars
0049 self._os_level = self._build_vars.get('os_level')
0050 self._target_os = self._build_vars.get('target_os')
0051 self._target_cpu = self._build_vars.get('target_cpu')
0052 self._toolchain_label = self._build_vars.get('product_toolchain_label')
0053 self._subsystem_info = self._get_org_subsystem_info()
preloader初始化时,上述2个函数会依次调用。
44行: 记录一些输入目录和文件的路径
45行:记录需要输出的一些文件路径名
46行:记录下产品相关的信息,并解析
47~53行:记录下其它信息
build/hb/util/preloader/preloader_process_data.py
0050 class Dirs:
0051
0052 def __init__(self, config):
0053 self.__post_init__(config)
0054
0055 def __post_init__(self, config):
0056 self.source_root_dir = config.root_path
0057 self.built_in_product_dir = config.built_in_product_path
0058 self.productdefine_dir = os.path.join(
0059 self.source_root_dir, 'productdefine/common')
0060 self.built_in_base_dir = os.path.join(self.productdefine_dir, 'base')
0061
0062 # Configs of vendor specified products are stored in ${vendor_dir} directory.
0063 self.vendor_dir = config.vendor_path
0064 # Configs of device specified products are stored in ${device_dir} directory.
0065 self.device_dir = os.path.join(config.root_path, 'device')
0066
0067 self.subsystem_config_json = os.path.join(
0068 config.root_path, config.subsystem_config_json)
0069 self.subsystem_config_overlay_json = os.path.join(config.product_path,
0070 'subsystem_config_overlay.json')
0071 self.lite_components_dir = os.path.join(
0072 config.root_path, 'build/lite/components')
0073
0074 self.preloader_output_dir = os.path.join(
0075 config.root_path, 'out/preloader', config.product)
56~75行:各种目录的记录
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')
31行:创建输出目录
32~47行:记录各输出文件的路径
build/hb/util/preloader/preloader_process_data.py
0078 class Product():
0079
0080 def __init__(self, config_dirs: Dirs, ohos_config: Config):
0081 self._ohos_config = None
0082 self._dirs = None
0083 self._name = ""
0084 self._config = {}
0085 self._build_vars = {}
0086 self._parts = {}
0087 self._syscap_info = {}
0088 self._device_name = ""
0089 self._device_info = {}
0090 self._config_file = ""
0091 self._version = ''
0092 self.__post_init__(config_dirs, ohos_config)
0093
0094 def __post_init__(self, config_dirs: Dirs, config: Config):
0095 self._ohos_config = config
0096 self._dirs = config_dirs
0097 self._name = config.product
0098 self._config_file = config.product_json
0099 self._config = self._get_full_product_config()
0100 self._version = self._config.get('version', '3.0')
0101 self._do_parse()
0102
0103 # parse product configuration, then generate parts list and build vars
0104 def _do_parse(self):
0105 self._update_syscap_info()
0106 self._update_device()
0107 self._update_parts()
0108 self._update_build_vars()
0109 self._remove_excluded_components()
105~106行:解析产品信息
107行:生成部件列表信息
108行:生成build var列表
109行:移除不需要的组件
build/hb/util/preloader/preloader_process_data.py
0114 def _update_syscap_info(self):
0115 product_name = self._config.get('product_name')
0116 if product_name is None:
0117 product_name = ""
0118 os_level = self._config.get('type')
0119 if os_level is None:
0120 os_level = ""
0121 api_version = self._config.get('api_version')
0122 if api_version is None:
0123 api_version = 0
0124 manufacturer_id = self._config.get('manufacturer_id')
0125 if manufacturer_id is None:
0126 manufacturer_id = 0
0127 self._syscap_info = {'product': product_name, 'api_version': api_version,
0128 'system_type': os_level, 'manufacturer_id': manufacturer_id}
主要记录一些基础信息,如果没有配置,则记录成空字符串或0。
build/hb/util/preloader/preloader_process_data.py
0131 def _update_device(self):
0132 if self._version == "2.0":
0133 device_name = self._config.get('product_device')
0134 if device_name:
0135 self._device_name = device_name
0136 self._device_info = self._get_device_info_v2(
0137 device_name, self._dirs.built_in_device_dir)
0138 else:
0139 device_name = self._config.get('board')
0140 if device_name:
0141 self._device_name = device_name
0142 self._device_info = self._get_device_info_v3(self._config)
0143 if self._ohos_config.target_cpu:
0144 self._device_info["target_cpu"] = self._ohos_config.target_cpu
0145 if self._ohos_config.compile_config:
0146 self._device_info[self._ohos_config["compile_config"]] = True
132~142行:记录device name和device info
144行:记录目标CPU
145行:记录编译工具类型
build/hb/util/preloader/preloader_process_data.py
0149 def _update_parts(self):
0150 if self._version == "1.0":
0151 _parts = {}
0152 self._parts = _parts
0153 else:
0154 # 1. inherit parts information from base config
0155 if self._version == "2.0":
0156 os_level = self._config.get("type", "standard")
0157 else:
0158 os_level = self._config.get("type", "mini")
0159 # 2. product config based on default minimum system
0160 based_on_mininum_system = self._config.get(
0161 'based_on_mininum_system')
0162 if based_on_mininum_system == "true":
0163 self._parts = self._get_base_parts(
0164 self._dirs.built_in_base_dir, os_level)
0165 # 3. inherit parts information from inherit config
0166 inherit = self._config.get('inherit')
0167 if inherit:
0168 self._parts.update(
0169 self._get_inherit_parts(inherit, self._dirs.source_root_dir))
0170
0171 # 4. chipset products relate system parts config
0172 sys_info_path = self._config.get('system_component')
0173 if sys_info_path:
0174 sys_parts = self._get_sys_relate_parts(
0175 sys_info_path, self._parts, self._dirs.source_root_dir)
0176 self._parts.update(sys_parts)
0177 all_parts = {}
0178 if self._version == "2.0":
0179 current_product_parts = self._config.get("parts")
0180 if current_product_parts:
0181 all_parts.update(current_product_parts)
0182 else:
0183 all_parts.update(get_vendor_parts_list(self._config))
0184 all_parts.update(self._get_product_specific_parts())
0185
0186 device_name = self._config.get('board')
0187 if device_name:
0188 all_parts.update(self._get_device_specific_parts())
0189 self._parts.update(all_parts)
160~164行:先生成最小系统的部件集合,目前搜索整个代码仓没有发现使用。
166~169行:从某些地方继承合并一些部件
172~176行:补充一些系统部件,目前搜索整个代码仓没有发现使用。
178~188行:补充只属于本产品的一些部件
build/hb/util/preloader/preloader_process_data.py
0337 def _get_inherit_parts(self, inherit, source_root_dir) -> dict:
0338 inherit_parts = {}
0339 for _config in inherit:
0340 _file = os.path.join(source_root_dir, _config)
0341 _info = IoUtil.read_json_file(_file)
0342 parts = _info.get('parts')
0343 if parts:
0344 inherit_parts.update(parts)
0345 else:
0346 inherit_parts.update(get_vendor_parts_list(_info))
0347 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
0361 def _get_product_specific_parts(self) -> dict:
0362 part_name = 'product_{}'.format(self._name)
0363 subsystem_name = part_name
0364 info = {}
0365 info['{}:{}'.format(subsystem_name, part_name)] = {}
0366 return info
这里得到的info[‘product_xxx:product_xxx’]
build/hb/util/preloader/preloader_process_data.py
0316 def _get_device_specific_parts(self) -> dict:
0317 info = {}
0318 if self._device_info and self._device_info.get('device_build_path'):
0319 subsystem_name = 'device_{}'.format(self._device_name)
0320 part_name = subsystem_name
0321 info['{}:{}'.format(subsystem_name, part_name)] = {}
0322 return info
319~321行:增加设备额外的部件
build/hb/util/preloader/preloader_process_data.py
0192 def _update_build_vars(self):
0193 config = self._config
0194 build_vars = {}
0195 if self._version == "1.0":
0196 build_vars = {"os_level": 'large'}
0197 else:
0198 if self._version == "2.0":
0199 build_vars['os_level'] = config.get("type", "standard")
0200 device_name = config.get('product_device')
0201 if device_name:
0202 build_vars['device_name'] = device_name
0203 else:
0204 build_vars['device_name'] = ''
0205 build_vars['product_company'] = config.get('product_company')
0206 else:
0207 build_vars['os_level'] = config.get('type', 'mini')
0208 build_vars['device_name'] = config.get('board')
0209 if config.get('product_company'):
0210 build_vars['product_company'] = config.get(
0211 'product_company')
0212 elif os.path.dirname(self._config_file) != self._dirs.built_in_product_dir:
0213 relpath = os.path.relpath(
0214 self._config_file, self._dirs.vendor_dir)
0215 build_vars['product_company'] = relpath.split('/')[0]
0216 else:
0217 build_vars['product_company'] = config.get(
0218 'device_company')
0219 build_vars['product_name'] = config.get('product_name')
0220 if 'enable_ramdisk' in config:
0221 build_vars['enable_ramdisk'] = config.get('enable_ramdisk')
0222 if 'enable_absystem' in config:
0223 build_vars['enable_absystem'] = config.get('enable_absystem')
0224 if 'build_selinux' in config:
0225 build_vars['build_selinux'] = config.get('build_selinux')
0226 if 'build_seccomp' in config:
0227 build_vars['build_seccomp'] = config.get('build_seccomp')
0228 if 'support_jsapi' in config:
0229 build_vars['support_jsapi'] = config.get('support_jsapi')
0230 if 'chipprod_config_path' in config:
0231 chipprod_config_path = os.path.join(
0232 self._dirs.source_root_dir, config.get('chipprod_config_path'))
0233 if os.path.exists(chipprod_config_path):
0234 build_vars['chipprod_config_path'] = chipprod_config_path
0235 if 'ext_sdk_config_file' in config:
0236 ext_sdk_config_file = os.path.join(
0237 self._dirs.source_root_dir, config.get('ext_sdk_config_file'))
0238 if os.path.exists(ext_sdk_config_file):
0239 build_vars['ext_sdk_config_file'] = ext_sdk_config_file
0240 if 'ext_ndk_config_file' in config:
0241 ext_ndk_config_file = os.path.join(
0242 self._dirs.source_root_dir, config.get('ext_ndk_config_file'))
0243 if os.path.exists(ext_ndk_config_file):
0244 build_vars['ext_ndk_config_file'] = ext_ndk_config_file
0245 build_vars.update(self._device_info)
0246 if build_vars['os_level'] == 'mini' or build_vars['os_level'] == 'small':
0247 toolchain_label = ""
0248 else:
0249 toolchain_label = '//build/toolchain/{0}:{0}_clang_{1}'.format(
0250 self._device_info.get('target_os'), self._device_info.get('target_cpu'))
0251 build_vars['product_toolchain_label'] = toolchain_label
0252 self._build_vars = build_vars
记录下各种各样的build参数
build/hb/util/preloader/preloader_process_data.py
0255 def _remove_excluded_components(self):
0256 items_to_remove = []
0257 for part, val in self._parts.items():
0258 if "exclude" in val and val["exclude"] == "true":
0259 items_to_remove.append(part)
0260 for item in items_to_remove:
0261 del self._parts[item]
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/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文件格式记录下来
build/hb/services/preloader.py
0191 '''Description: generate parts to "out/preloader/product_name/parts.json"
0192 @parameter:none
0193 @return :none
0194 '''
0195
0196 def _generate_parts_json(self):
0197 parts_info = {"parts": sorted(list(self._all_parts.keys()))}
0198 IoUtil.dump_json_file(self._outputs.parts_json, parts_info)
0199 LogUtil.hb_info(
0200 'generated product parts info to {}/parts.json'.format(self._dirs.preloader_output_dir))
将部件列表信息按部件名排序后,写入json文件
build/hb/services/preloader.py
0202 '''Description: generate parts config to "out/preloader/product_name/parts_config.json"
0203 @parameter:none
0204 @return :none
0205 '''
0206
0207 def _generate_parts_config_json(self):
0208 parts_config = {}
0209 for part in self._all_parts:
0210 part = part.replace(":", "_")
0211 part = part.replace("-", "_")
0212 part = part.replace(".", "_")
0213 part = part.replace("/", "_")
0214 parts_config[part] = True
0215 IoUtil.dump_json_file(self._outputs.parts_config_json, parts_config)
0216 LogUtil.hb_info(
0217 'generated parts config info to {}/parts_config.json'.format(self._dirs.preloader_output_dir))
将部件中的名称进行规范化处理后写入json文件
build/hb/services/preloader.py
0075 '''Description: generate build gnargs prop info to "out/preloader/{product_name}/build_gnargs.prop"
0076 @parameter:none
0077 @return :none
0078 '''
0079
0080 def _generate_build_gnargs_prop(self):
0081 all_features = {}
0082 for _part_name, vals in self._all_parts.items():
0083 _features = vals.get('features')
0084 if _features:
0085 all_features.update(_features)
0086 attr_list = []
0087 for key, val in all_features.items():
0088 _item = ''
0089 if isinstance(val, bool):
0090 _item = f'{key}={str(val).lower()}'
0091 elif isinstance(val, int):
0092 _item = f'{key}={val}'
0093 elif isinstance(val, str):
0094 _item = f'{key}="{val}"'
0095 else:
0096 raise Exception("part feature '{key}:{val}' type not support.")
0097 attr_list.append(_item)
0098 with os.fdopen(os.open(self._outputs.build_gnargs_prop,
0099 os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), 'w') as fobj:
0100 fobj.write('\n'.join(attr_list))
0101 LogUtil.hb_info(
0102 'generated build gnargs prop info to {}/build_gnargs.prop'.format(self._dirs.preloader_output_dir))
81~85行:读出所有部件的特性集合
86~97行:对特性字符串进行规范化处理,只支持整数,布尔类型和字符串
98~102行:写入规范化后的结果
build/hb/services/preloader.py
0104 '''Description: generate features to "out/preloader/{product_name}/features.json"
0105 @parameter:none
0106 @return :none
0107 '''
0108
0109 def _generate_features_json(self):
0110 all_features = {}
0111 part_feature_map = {}
0112 for _part_name, vals in self._all_parts.items():
0113 _features = vals.get('features')
0114 if _features:
0115 all_features.update(_features)
0116 if _features:
0117 part_feature_map[_part_name.split(
0118 ':')[1]] = list(_features.keys())
0119 parts_feature_info = {
0120 "features": all_features,
0121 "part_to_feature": part_feature_map
0122 }
0123 IoUtil.dump_json_file(self._outputs.features_json, parts_feature_info)
0124 LogUtil.hb_info(
0125 'generated features info to {}/features.json'.format(self._dirs.preloader_output_dir))
114~115行:汇总所有的特性
116~118行:记录每一个部件下的特性集合(部件名使用简称)
119~125行:结果写入文件
build/hb/services/preloader.py
0127 '''Description: generate syscap to "out/preloader/product_name/syscap.json"
0128 @parameter:none
0129 @return :none
0130 '''
0131
0132 def _generate_syscap_json(self):
0133 all_syscap = {}
0134 part_syscap_map = {}
0135 for _part_name, vals in self._all_parts.items():
0136 _syscap = vals.get('syscap')
0137 if _syscap:
0138 all_syscap.update(_syscap)
0139 part_syscap_map[_part_name.split(':')[1]] = _syscap
0140 parts_syscap_info = {
0141 "syscap": all_syscap,
0142 "part_to_syscap": part_syscap_map
0143 }
0144 IoUtil.dump_json_file(self._outputs.syscap_json, parts_syscap_info)
0145 LogUtil.hb_info(
0146 'generated syscap info to {}/syscap.json'.format(self._dirs.preloader_output_dir))
136~139行:获取系统能力以及组件和能力集的映射
然后写入结果
build/hb/services/preloader.py
0148 '''Description: generate exclusion modules info to "out/preloader/product_name/exclusion_modules.json"
0149 @parameter:none
0150 @return :none
0151 '''
0152
0153 def _generate_exclusion_modules_json(self):
0154 exclusions = {}
0155 for _part_name, vals in self._all_parts.items():
0156 _exclusions = vals.get('exclusions')
0157 if _exclusions:
0158 pair = dict()
0159 pair[_part_name] = _exclusions
0160 exclusions.update(pair)
0161 IoUtil.dump_json_file(self._outputs.exclusion_modules_json, exclusions)
0162 LogUtil.hb_info(
0163 'generated exclusion modules info to {}/exclusion_modules.json'.format(self._dirs.preloader_output_dir))
将各部件排除的模块整合起来
再记录到json文件中
本能力目前在各产品还没有看见使用。
build/hb/services/preloader.py
0219 '''Description: generate subsystem config info to "out/preloader/product_name/subsystem_config.json"
0220 @parameter:none
0221 @return :none
0222 '''
0223
0224 def _generate_subsystem_config_json(self):
0225 if self._subsystem_info:
0226 self._subsystem_info.update(
0227 self._product._get_product_specific_subsystem())
0228 self._subsystem_info.update(
0229 self._product._get_device_specific_subsystem())
0230 IoUtil.dump_json_file(
0231 self._outputs.subsystem_config_json, self._subsystem_info)
0232 LogUtil.hb_info(
0233 'generated subsystem config info to {}/subsystem_config.json'.format(self._dirs.preloader_output_dir))
汇总设备和产品的配置信息,然后记录到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))
写入当前的系统能力信息
build/hb/modules/ohos_build_module.py
0081 def _load(self):
0082 self._run_phase(BuildPhase.LOAD)
0083 if self.args_dict.get('fast_rebuild', None) and not self.args_dict.get('fast_rebuild').arg_value:
0084 self.loader.run()
82行:先支持LOAD阶段的处理
这些处理有
82~84行:在没有开启fast_rebuild的情况下,还要运行loader
build/hb/resolver/build_args_resolver.py
0327 def resolve_strict_mode(target_arg: Arg, build_module: BuildModuleInterface):
0328 """resolve '--strict-mode' 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: load.
0332 :raise OHOSException: when preloader or loader results not correct
0333 """
0334 if target_arg.arg_value:
0335 preloader = build_module.preloader
0336 loader = build_module.loader
0337 if not preloader.outputs.check_outputs():
0338 raise OHOSException('Preloader result not correct', "1001")
0339 if not loader.outputs.check_outputs():
0340 raise OHOSException('Loader result not correct ', "2001")
目前这2个check_outputs函数没有看到定义的地方
这个命令行参数当前不可用,后续可能会支持
build/hb/resolver/build_args_resolver.py
0343 def resolve_scalable_build(target_arg: Arg, build_module: BuildModuleInterface):
0344 """resolve '--scalable-build' arg.
0345 :param target_arg: arg object which is used to get arg value.
0346 :param build_module [maybe unused]: build module object which is used to get other services.
0347 :phase: load.
0348 """
0349 loader = build_module.loader
0350 loader.regist_arg("scalable_build", target_arg.arg_value)
记录增量构建开关
build/hb/resolver/build_args_resolver.py
0353 def resolve_build_example(target_arg: Arg, build_module: BuildModuleInterface):
0354 """resolve '--build-example' arg.
0355 :param target_arg: arg object which is used to get arg value.
0356 :param build_module [maybe unused]: build module object which is used to get other services.
0357 :phase: load.
0358 """
0359 loader = build_module.loader
0360 loader.regist_arg("build_example", target_arg.arg_value)
记录build_example开关
build/hb/resolver/build_args_resolver.py
0363 def resolve_build_platform_name(target_arg: Arg, build_module: BuildModuleInterface):
0364 """resolve '---build-platform-name' arg
0365 :param target_arg: arg object which is used to get arg value.
0366 :param build_module [maybe unused]: build module object which is used to get other services.
0367 :phase: load.
0368 """
0369 loader = build_module.loader
0370 loader.regist_arg("build_platform_name", target_arg.arg_value)
记录构建平台的名称
build/hb/resolver/build_args_resolver.py
0373 def resolve_build_xts(target_arg: Arg, build_module: BuildModuleInterface):
0374 """resolve '--build-xts' arg
0375 :param target_arg: arg object which is used to get arg value.
0376 :param build_module [maybe unused]: build module object which is used to get other services.
0377 :phase: load.
0378 """
0379 loader = build_module.loader
0380 for gn_arg in build_module.args_dict['gn_args'].arg_value:
0381 if 'build_xts' in gn_arg:
0382 variable, value = gn_arg.split('=')
0383 if str(value).lower() == 'false':
0384 value = False
0385 elif str(value).lower() == 'true':
0386 value = True
0387 loader.regist_arg(variable, value)
0388 return
0389 loader.regist_arg("build_xts", target_arg.arg_value)
如果在gn_args参数里面配置了build_xts, 则优先使用gn_args里面的开关
否则使用外面这个开关
build/hb/resolver/build_args_resolver.py
0392 def resolve_ignore_api_check(target_arg: Arg, build_module: BuildModuleInterface):
0393 """resolve '--ignore-api-check' arg
0394 :param target_arg: arg object which is used to get arg value.
0395 :param build_module [maybe unused]: build module object which is used to get other services.
0396 :phase: load.
0397 """
0398 loader = build_module.loader
0399 if len(target_arg.arg_value):
0400 loader.regist_arg("ignore_api_check", target_arg.arg_value)
0401 else:
0402 loader.regist_arg("ignore_api_check", [
0403 'xts', 'common', 'developertest'])
记录用户指定的忽略api检查的部件集合
如果用户没有指定,默认有3个部件不检查API
build/hb/resolver/build_args_resolver.py
0406 def resolve_load_test_config(target_arg: Arg, build_module: BuildModuleInterface):
0407 """resolve '--load-test-config' arg
0408 :param target_arg: arg object which is used to get arg value.
0409 :param build_module [maybe unused]: build module object which is used to get other services.
0410 :phase: load.
0411 """
0412 loader = build_module.loader
0413 loader.regist_arg("load_test_config", target_arg.arg_value)
记录下对应开关值
build/hb/services/interface/load_interface.py
0044 def run(self):
0045 self.__post_init__()
0046 self._execute_loader_args_display()
0047 self._check_parts_config_info()
0048 self._generate_subsystem_configs()
0049 self._generate_target_platform_parts()
0050 self._generate_system_capabilities()
0051 self._generate_stub_targets()
0052 self._generate_platforms_part_by_src()
0053 self._generate_target_gn()
0054 self._generate_phony_targets_build_file()
0055 self._generate_required_parts_targets()
0056 self._generate_required_parts_targets_list()
0057 self._generate_src_flag()
0058 self._generate_auto_install_part()
0059 self._generate_platforms_list()
0060 self._generate_part_different_info()
0061 self._generate_infos_for_testfwk()
0062 self._check_product_part_feature()
0063 self._generate_syscap_files()
build/hb/services/loader.py
0058 def __post_init__(self):
0059 self.source_root_dir = self.config.root_path + '/'
0060 self.gn_root_out_dir = self.config.out_path if not self.config.out_path.startswith(
0061 '/') else os.path.relpath(self.config.out_path, self.config.root_path)
0062 self.os_level = self.config.os_level if self.config.os_level else "standard"
0063 self.target_cpu = self.config.target_cpu if self.config.target_cpu else "arm"
0064 self.target_os = self.config.target_os if self.config.target_os else "ohos"
0065 self.config_output_relpath = os.path.join(
0066 self.gn_root_out_dir, 'build_configs')
0067 self.config_output_dir = os.path.join(
0068 self.source_root_dir, self.config_output_relpath)
0069 self.target_arch = '{}_{}'.format(self.target_os, self.target_cpu)
0070 self.subsystem_config_file = os.path.join(
0071 self.config.root_path, 'out/preloader', self.config.product, 'subsystem_config.json')
0072 self.platforms_config_file = os.path.join(
0073 self.config.root_path, 'out/preloader', self.config.product, 'platforms.build')
0074 self.exclusion_modules_config_file = os.path.join(
0075 self.config.root_path, 'out/preloader', self.config.product, 'exclusion_modules.json')
0076 self.example_subsystem_file = os.path.join(
0077 self.config.root_path, 'build', 'subsystem_config_example.json')
0078
0079 # check config args
0080 self._check_args()
0081
0082 self.build_example = self.args_dict.get('build_example')
0083 if not self.build_example:
0084 self.example_subsystem_file = ""
0085 self.scalable_build = self.args_dict.get('scalable_build')
0086 self.build_platform_name = self.args_dict.get('build_platform_name')
0087 self.build_xts = self.args_dict.get('build_xts')
0088 self.ignore_api_check = self.args_dict.get('ignore_api_check')
0089 self.load_test_config = self.args_dict.get('load_test_config')
0090 self.subsystem_configs = subsystem_scan.scan(self.subsystem_config_file,
0091 self.example_subsystem_file,
0092 self.source_root_dir)
0093
0094 self._subsystem_info = subsystem_info.get_subsystem_info(
0095 self.subsystem_config_file,
0096 self.example_subsystem_file,
0097 self.source_root_dir,
0098 self.config_output_relpath,
0099 self.os_level)
0100 overrided_components = self._override_components()
0101
0102 self._platforms_info = platforms_loader.get_platforms_info(
0103 self.platforms_config_file,
0104 self.source_root_dir,
0105 self.gn_root_out_dir,
0106 self.target_arch,
0107 self.config_output_relpath,
0108 self.scalable_build)
0109 self.variant_toolchains = self._platforms_info.get(
0110 'variant_toolchain_info').get('platform_toolchain')
0111 self._all_platforms = self.variant_toolchains.keys()
0112 self.build_platforms = self._get_build_platforms()
0113 self.parts_config_info = load_ohos_build.get_parts_info(
0114 self.source_root_dir,
0115 self.config_output_relpath,
0116 self._subsystem_info,
0117 self.variant_toolchains,
0118 self.target_arch,
0119 self.ignore_api_check,
0120 self.exclusion_modules_config_file,
0121 self.load_test_config,
0122 overrided_components,
0123 self.build_xts)
0124 self.parts_targets = self.parts_config_info.get('parts_targets')
0125 self.phony_targets = self.parts_config_info.get('phony_target')
0126 self.parts_info = self.parts_config_info.get('parts_info')
0127 self.target_platform_parts = self._get_platforms_all_parts()
0128 self.target_platform_stubs = self._get_platforms_all_stubs()
0129 self.required_parts_targets_list = self._get_required_build_parts_list()
0130 self.required_phony_targets = self._get_required_phony_targets()
0131 self.required_parts_targets = self._get_required_build_targets()
59~89行:记录本阶段解析好的命令行参数,以及一些文件的路径
90~92行:扫描subsystem_config.json和subsystem_config_example.json内的所有subsystem,含ohos.build或bundle.json文件的,即为合法的部件,否则为不参与编译的源码。
94~99行:将扫描出的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
100行:处理被替换的部件, 厂商自己实现的部件替换掉系统默认实现的部件
102~108行:获取平台信息
109~110行:获取平台对应的工具链信息
111行:获取本次编译的所有平台,当前只支持phone
112行:获取本次构建的品台,仅支持phone
113~123行:获取ohos.build两个的部件信息
124~125行:记录部件编译目标和伪目标
126~131行:记录相关的需要构建的目标
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行:无软件包的子系统也记录一下
103~109行:记录结果并返回
build/hb/util/loader/subsystem_scan.py
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
0046 def get_subsystem_info(subsystem_config_file, example_subsystem_file,
0047 source_root_dir, config_output_path, os_level):
0048 if not subsystem_config_file:
0049 subsystem_config_file = 'build/subsystem_config.json'
0050
0051 subsystem_configs = {}
0052 output_dir_realpath = os.path.join(source_root_dir, config_output_path)
0053 subsystem_configs = subsystem_scan.scan(subsystem_config_file,
0054 example_subsystem_file,
0055 source_root_dir)
0056 config = Config()
0057 subsystem_config_overlay_file = os.path.join(
0058 config.product_path, "subsystem_config_overlay.json")
0059 if os.path.isfile(subsystem_config_overlay_file):
0060 subsystem_config_overlay = {}
0061 subsystem_config_overlay = subsystem_scan.scan(subsystem_config_overlay_file,
0062 example_subsystem_file,
0063 source_root_dir)
0064 subsystem_configs['subsystem'].update(
0065 subsystem_config_overlay['subsystem'])
0066 subsystem_configs['no_src_subsystem'].update(
0067 subsystem_config_overlay['no_src_subsystem'])
0068 _output_subsystem_configs(output_dir_realpath, subsystem_configs)
0069 return subsystem_configs.get('subsystem')
57~67行:如果存在overlay配置,则需要将overlay相关子系统也考虑进来
68行:保存查找到的部件信息
69行:返回部件信息
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
0794 def _override_components(self):
0795 '''Description: Check whether there are components that need to be replaced, and if so,
0796 replace the component configuration file bundle.json in subsystem_info and update
0797 the component list generated by the preloader.
0798 @parameter:none
0799 @return :overrided_components
0800 '''
0801 parts_file = self.platforms_config_file.replace(
0802 "platforms.build", "parts.json")
0803 all_parts = read_json_file(parts_file)
0804 if "parts" not in all_parts:
0805 LogUtil.hb_warning("{} does not contain parts!".format(parts_file))
0806 return {}
0807 overrided = False
0808 overrided_components = {}
0809 all_parts = all_parts["parts"]
0810 component_override_map = {}
0811 for subsystem_name, build_config_info in self._subsystem_info.items():
0812 if "build_files" not in build_config_info:
0813 continue
0814
0815 # scan all bundle.json or ohos.build files with named groups
0816 for build_file in build_config_info["build_files"]:
0817
0818 # ohos.build does not support overrided components
0819 if not build_file.endswith('bundle.json'):
0820 continue
0821
0822 # Only device or vendor components can do named groups extensions
0823 if (not build_file.startswith(self.source_root_dir + 'device/')) \
0824 and (not build_file.startswith(self.source_root_dir + 'vendor/')):
0825 continue
0826
0827 # "subsystem", "name" and "override" is required
0828 component = read_json_file(build_file).get("component")
0829
0830 if (not component) or (not all(key in component for key in ("subsystem", "name", "override"))):
0831 continue
0832
0833 full_part_name = f"{component.get('subsystem')}:{component.get('name')}"
0834 if full_part_name not in all_parts:
0835 LogUtil.hb_warning("{} was not configured for this product: {}".format(
0836 build_file, full_part_name))
0837 continue
0838
0839 if self._override_one_component(self._subsystem_info, component, build_file, all_parts, overrided_components, component_override_map):
0840 overrided = True
0841
0842 if overrided:
0843 # Update parts.json and parts_config.json generated by preloader
0844 write_json_file(parts_file, {"parts": all_parts})
0845 parts_file = self.platforms_config_file.replace(
0846 "platforms.build", "parts_config.json")
0847 self._output_parts_config_json(all_parts, parts_file)
0848
0849 write_json_file(
0850 f"{self.config_output_dir}/component_override_map.json", component_override_map)
0851 return overrided_components
801~810行:读取部件列表
811行:遍历子系统
816行:遍历子系统中部件列表
819~820行:只处理bundle.json描述的部件
823~825行:只处理device或vendor开头的部件
828~831行:部件中必须包含subsystem, name, override属性
833~837行:子系统与组件名和部件名需匹配
839~840行:移除被本组件替换(覆盖)的组件
842~850行:更新相关文件,并记录下覆盖关系
851行:返回被覆盖的部件集合
build/hb/services/loader.py
0853 def _override_one_component(self, subsystem_info, component, build_file, all_parts, overrided_components, component_override_map):
0854 '''Description: Perform a replacement of a single component and return the component list update result.
0855 @parameter:subsystem_info, component, build_file, all_parts, overrided_components
0856 @return :True or False(Whether replacement has been performed)
0857 '''
0858 splits = component["override"].split(":")
0859 if len(splits) != 2:
0860 LogUtil.hb_warning(
0861 "{} override value is invalid format. Skip override process".format(build_file))
0862 return False
0863 overrided_subsystem = splits[0]
0864 overrided_component = splits[1]
0865 if overrided_subsystem not in subsystem_info:
0866 LogUtil.hb_warning(
0867 "{} override invalid subsystem. Skip override process".format(build_file))
0868 return False
0869
0870 founded_bundle = ""
0871
0872 for bundle in subsystem_info[overrided_subsystem]["build_files"]:
0873 if not bundle.endswith('bundle.json'):
0874 continue
0875
0876 bundle_obj = read_json_file(bundle)
0877
0878 if bundle_obj.get("component", {}).get("name") == overrided_component:
0879 founded_bundle = bundle
0880 break
0881
0882 if founded_bundle:
0883 origin_component = read_json_file(build_file).get('component')
0884 LogUtil.hb_warning(
0885 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\"")
0886
0887 # replace bundle.json in subsystem_info's build_files
0888 subsystem_info[overrided_subsystem]["build_files"].remove(
0889 founded_bundle)
0890
0891 # Update parts.json generated by preloader, which means that new added components will not be installed
0892 # Ensure that the overrided components will be installed
0893 full_partname = f"{overrided_subsystem}:{overrided_component}"
0894 if full_partname in all_parts:
0895 all_parts.remove(full_partname)
0896
0897 overrided_components[f"{component['subsystem']}:{component['name']}"] = {
0898 'subsystem': overrided_subsystem,
0899 'partName': overrided_component
0900 }
0901 component_override_map[overrided_component] = component["name"]
0902 return True
0903 LogUtil.hb_warning(
0904 "{}:{} is not configured in product, \new add component will be installed!".format(
0905 overrided_subsystem, overrided_component))
0906 return False
858~864行:解析被替换的子系统和组件名
865~868行:子系统不存在
872行:遍历子系统中的包
873~874行:只支持bundle.json类型的包
878~880行:找到匹配的包
883~885行:获取原始组件,并输出覆盖动作日志
888~889行:移除被覆盖的bundle.json路径
893~895行:移除对应的部件名
897~900行:记录下部件覆盖关系–覆盖了谁
901行:记录下部件覆盖关系–被谁覆盖
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个映射表
未完待续