#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) 2021 Huawei Device Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys import os import argparse sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from scripts.util.file_utils import read_json_file, read_file, write_file # noqa: E402 E501 def _read_subninja_build(build_dir: str, subninja_build_file: str): subninja_build_file = os.path.join(build_dir, subninja_build_file) if not os.path.exists(subninja_build_file): raise Exception("file '{}' doesn't exist.".format(subninja_build_file)) subninja_build = read_file(subninja_build_file) if subninja_build is None: raise Exception("read file '{}' failed.".format(subninja_build_file)) support_lib_type = ['.a', '.so', ''] label_name = '' label_target = '' for _line in subninja_build: if _line.startswith('label_name = '): label_name = _line[len('label_name = '):] elif _line.startswith('build '): _build_info = _line.split(':')[0] build_label = _build_info.split(' ')[1] _, extension = os.path.splitext(build_label) if extension in support_lib_type: label_target = build_label if label_target != '': if label_name == '': target_filename = os.path.basename(label_target) label_name, _ = os.path.splitext(target_filename) return {label_name: label_target} return None def _parse_target_label(build_label_list: list, toolchain_name: str): phony_targets_dict = {} for build_label in build_label_list: target_filename = os.path.basename(build_label) label_name, _ = os.path.splitext(target_filename) if label_name: phony_targets_dict[label_name] = build_label start_index = len('{}/obj/'.format(toolchain_name)) _path = os.path.dirname(build_label)[start_index:] if _path: phony_targets_dict[_path] = build_label if label_name and _path: _label_path = '{}$:{}'.format(_path, label_name) phony_targets_dict[_label_path] = build_label return phony_targets_dict def _read_toolchain_ninja(build_dir: str, toolchain_ninja_file: str, toolchain_name: str): if not os.path.exists(toolchain_ninja_file): raise Exception( "file '{}' doesn't exist.".format(toolchain_ninja_file)) toolchain_ninja_rules = read_file(toolchain_ninja_file) if toolchain_ninja_rules is None: raise Exception("read file '{}' failed.".format(toolchain_ninja_file)) build_label_list = [] subninja_targets = {} for _ninja_rule in toolchain_ninja_rules: if _ninja_rule.startswith('build '): _tmp = _ninja_rule.split(':')[0] _label = _tmp[len('build '):] if not _label.endswith('.stamp'): continue build_label_list.append(_label) if _ninja_rule.startswith('subninja '): sbuninja_file = _ninja_rule[len('subninja '):] subninja_target_info = _read_subninja_build( build_dir, sbuninja_file) if subninja_target_info: subninja_targets.update(subninja_target_info) build_phony_targets = _parse_target_label(build_label_list, toolchain_name) build_phony_targets.update(subninja_targets) return build_phony_targets def _read_variants_toolchain_info(variants_toolchain_info_file: str): if not os.path.exists(variants_toolchain_info_file): raise Exception( "file '{}' doesn't exist.".format(variants_toolchain_info_file)) variants_toolchain_info = read_json_file(variants_toolchain_info_file) if variants_toolchain_info is None: raise Exception( "read file '{}' failed.".format(variants_toolchain_info_file)) platform_toolchain = variants_toolchain_info.get('platform_toolchain') return platform_toolchain def _read_build_ninja(build_ninja_file: str): if not os.path.exists(build_ninja_file): raise Exception("file '{}' doesn't exist.".format(build_ninja_file)) ninja_targets = read_file(build_ninja_file) if ninja_targets is None: raise Exception("read file '{}' failed.".format(build_ninja_file)) return ninja_targets def generate_phony_targets(build_dir: str, toolchain_ninja_file: str, platform: str, toolchain_name: str, default_targets_name: str): build_phony_targets = _read_toolchain_ninja(build_dir, toolchain_ninja_file, toolchain_name) targets_list = [] for key, build_label in build_phony_targets.items(): targets_list.append('build {}/{}: phony {}'.format( platform, key, build_label)) _diff_targets = set(default_targets_name).difference( set(build_phony_targets.keys())) for _diff_target in _diff_targets: targets_list.append('build {}/{}: phony {}'.format( platform, _diff_target, _diff_target)) build_file = os.path.join(os.path.dirname(toolchain_ninja_file), '{}_build.ninja'.format(platform)) write_file(build_file, '{}\n\n'.format('\n'.join(targets_list))) return build_file def _update_build_ninja(build_dir: str, include_files: str): try: ninja_build_file = os.path.join(build_dir, 'build.ninja') if not os.path.exists(ninja_build_file): raise Exception( "file '{}' doesn't exist.".format(ninja_build_file)) with open(ninja_build_file, 'a+') as _file: data = [] for line in _file.readlines(): _line = line.rstrip('\n') if _line.startswith('subninja '): data.append(_line) for include_file in include_files: include_info = 'subninja {}'.format( os.path.relpath(include_file, build_dir)) if include_info in data: continue _file.write('{}\n'.format(include_info)) _file.flush() except: # noqa E722 raise def update(build_dir: str, variants_toolchain_info_file: str): variants_toolchain_info_file = os.path.join(build_dir, variants_toolchain_info_file) platform_toolchain = _read_variants_toolchain_info( variants_toolchain_info_file) ninja_build_file = os.path.join(build_dir, 'build.ninja') default_ninja_targets = _read_build_ninja(ninja_build_file) default_targets_name = [] for _ninja_target in default_ninja_targets: if not _ninja_target.startswith('build '): continue _ninja_target = _ninja_target.split(': ')[0] default_targets_name.append(_ninja_target[len('build '):]) include_files = [] for platform, toolchain_label in platform_toolchain.items(): if platform == 'phone': continue toolchain_name = toolchain_label.split(':')[1] toolchain_ninja_file = os.path.join(build_dir, toolchain_name, 'toolchain.ninja') if not os.path.exists(toolchain_ninja_file): continue _build_file = generate_phony_targets(build_dir, toolchain_ninja_file, platform, toolchain_name, default_targets_name) include_files.append(_build_file) _update_build_ninja(build_dir, include_files) def main(): parser = argparse.ArgumentParser() parser.add_argument('--source-root-dir', required=True) parser.add_argument('--root-build-dir', required=True) parser.add_argument('--variants-toolchain-info-file', required=True) args = parser.parse_args() source_root_dir = args.source_root_dir if not os.path.exists(os.path.join(source_root_dir, '.gn')): print('source root dir incorrect.') return 1 build_dir = os.path.join(source_root_dir, args.root_build_dir) update(build_dir, args.variants_toolchain_info_file) return 0 if __name__ == '__main__': sys.exit(main())