1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2021 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import os 17import sys 18import argparse 19 20 21sys.path.append( 22 os.path.dirname(os.path.dirname(os.path.dirname( 23 os.path.abspath(__file__))))) 24from scripts.util.file_utils import read_json_file # noqa: E402 25 26 27def check_third_party_deps(args, dep_part: str, parts_deps_info: dict, _tips_info: dict, third_deps_allow_list: list): 28 """check whether the three-party dependency is in the part declaration""" 29 if args.part_name == dep_part: 30 return 31 part_deps_info = parts_deps_info.get(args.part_name) 32 if not part_deps_info: 33 _warning_info = f"{_tips_info} {args.part_name}." 34 elif not part_deps_info.get('third_party') or \ 35 not dep_part in part_deps_info.get('third_party'): 36 _warning_info = f"{_tips_info} {part_deps_info.get('build_config_file')}." 37 else: 38 _warning_info = "" 39 40 if _warning_info != "": 41 if args.target_path in third_deps_allow_list: 42 print(f"[0/0] WARNING: {_warning_info}") 43 else: 44 raise Exception(_warning_info) 45 46 return 47 48 49def load_part_info(depfiles: list): 50 """load part path info from parts_info""" 51 # load parts path info file 52 parts_path_file = 'build_configs/parts_info/parts_path_info.json' 53 parts_path_info = read_json_file(parts_path_file) 54 if parts_path_info is None: 55 raise Exception("read pre_build parts_path_info failed.") 56 depfiles.append(parts_path_file) 57 58 # load path to parts info file 59 path_parts_file = 'build_configs/parts_info/path_to_parts.json' 60 path_parts_info = read_json_file(path_parts_file) 61 if path_parts_info is None: 62 raise Exception("read pre_build path to parts failed.") 63 depfiles.append(path_parts_file) 64 65 return parts_path_info, path_parts_info 66 67 68def get_path_from_label(label: str): 69 """get part path from target label, the format is //path:module""" 70 return label.lstrip('//').split(':')[0] 71 72 73def get_path_from_module_list(cur_part_name: str, depfiles: list) -> list: 74 parts_module_lists = [] 75 parts_modules_file = "build_configs/parts_info/parts_modules_info.json" 76 parts_modules_info = read_json_file(parts_modules_file) 77 if parts_modules_info is None: 78 raise Exception("read pre_build parts module info failed.") 79 depfiles.append(parts_modules_file) 80 81 for parts_module in parts_modules_info.get("parts"): 82 if parts_module.get("part_name") == cur_part_name: 83 parts_module_lists = parts_module["module_list"] 84 break 85 parts_path = [get_path_from_label(x) for x in parts_module_lists] 86 87 return parts_path 88 89 90def get_part_pattern(cur_part_name: str, parts_path_info: dict, path_parts_info: dict, depfiles: list, 91 component_path_file: str) -> list: 92 """get all part path from part info""" 93 part_pattern = [] 94 part_path = parts_path_info.get(cur_part_name) 95 if part_path is None: 96 return part_pattern 97 98 path_to_part = path_parts_info.get(part_path, []) 99 if len(path_to_part) == 1: 100 part_pattern.append(part_path) 101 else: 102 part_pattern.extend(get_path_from_module_list(cur_part_name, depfiles)) 103 104 if not os.path.exists(component_path_file): 105 return part_pattern 106 107 component_path_info: dict[str, list[str]] = read_json_file(component_path_file) 108 module_paths = component_path_info.get(part_path, []) 109 part_pattern.extend(module_paths) 110 111 return part_pattern 112 113 114def get_dep_part(dep_path: str, third_part_info: dict) -> str: 115 """gets the part by the longest path match""" 116 for part_info in third_part_info: 117 path = part_info[0] 118 part = part_info[1][0] 119 if dep_path.find(path) != -1: 120 return part 121 return "" 122 123 124def check_part_deps(args, part_pattern: str, path_parts_info: dict, compile_standard_allow_info: dict, depfiles: list): 125 deps_allow_list = compile_standard_allow_info.get("deps_added_external_part_module", []) 126 third_deps_allow_list = compile_standard_allow_info.get("third_deps_bundle_not_add", []) 127 parts_deps_file = 'build_configs/parts_info/parts_deps.json' 128 parts_deps_info = read_json_file(parts_deps_file) 129 if parts_deps_info is None: 130 raise Exception("read pre_build parts_deps failed.") 131 depfiles.append(parts_deps_file) 132 133 parts_src_flag_file = "build_configs/parts_src_flag.json" 134 parts_src_info = read_json_file(parts_src_flag_file) 135 third_party_allow_list = os.path.join(args.source_root_dir, "out/products_ext/third_party_allow_list.json") 136 if not os.path.exists(third_party_allow_list): 137 third_party_allow_list = os.path.join(args.source_root_dir, "build/third_party_allow_list.json") 138 third_party_allow_info = read_json_file(third_party_allow_list) 139 140 # filter third_party part info, sort by longest path match 141 third_party_info = [x for x in path_parts_info.items() if x[0].find('third_party') != -1] 142 third_party_info.reverse() 143 for dep in args.deps: 144 dep_path = get_path_from_label(dep) 145 if dep_path.find('third_party/rust/crates') != -1: 146 continue 147 if dep_path.find('third_party') != -1: 148 dep_part = get_dep_part(dep_path, third_party_info) 149 150 if dep_part not in parts_src_info and dep_part in third_party_allow_info: 151 print(f"[0/0] WARNING: deps third_party '{dep_part}' not configured in part config json, " 152 f"target: '{args.target_path}', deps: '{dep}'") 153 elif dep_part not in parts_src_info and dep_part not in third_party_allow_info: 154 raise Exception( 155 f"deps third_party '{dep_part}' not configured in part config json, " 156 f"target: '{args.target_path}', deps: '{dep}'") 157 158 tips_info = "{} depend part {}, need set part deps {} info to".format( 159 args.target_path, dep, dep_part) 160 check_third_party_deps(args, dep_part, parts_deps_info, tips_info, third_deps_allow_list) 161 continue 162 163 match_flag = False 164 for pattern in part_pattern: 165 if dep_path.startswith(pattern): 166 match_flag = True 167 break 168 if match_flag is False: 169 message = "deps validation part_name: '{}', target: '{}', dep: '{}' failed!!!".format( 170 args.part_name, args.target_path, dep) 171 if args.target_path in deps_allow_list: 172 print(f"[0/0] WARNING:{message}") 173 else: 174 raise Exception(message) 175 176 177def check(args) -> list: 178 depfiles = [] 179 # ignore test related parts 180 part_allow_set = {'libc-test', 'libc-test-lib', 'libc-gtest-lib'} 181 if args.part_name in part_allow_set: 182 return depfiles 183 184 compile_standard_allow_file = args.compile_standard_allow_file 185 compile_standard_allow_info = read_json_file(compile_standard_allow_file) 186 parts_path_info, path_parts_info = load_part_info(depfiles) 187 component_path_file = os.path.join(args.source_root_dir, "out/products_ext/component_multipath_dict.json") 188 189 part_pattern = get_part_pattern(args.part_name, parts_path_info, path_parts_info, depfiles, component_path_file) 190 if not part_pattern: 191 gn_allow_list = compile_standard_allow_info.get("gn_part_or_subsystem_error", []) 192 message = "part_name: '{}' path is not exist, please check target: '{}'".format( 193 args.part_name, args.target_path) 194 if args.target_path in gn_allow_list: 195 print(f"[0/0] {message}") 196 return depfiles 197 else: 198 raise Exception(message) 199 200 check_part_deps(args, part_pattern, path_parts_info, compile_standard_allow_info, depfiles) 201 202 return depfiles 203 204 205def main(): 206 parser = argparse.ArgumentParser() 207 parser.add_argument('--deps', nargs='*', required=True) 208 parser.add_argument('--part-name', required=True) 209 parser.add_argument('--target-path', required=True) 210 parser.add_argument('--source-root-dir', required=True) 211 args = parser.parse_args() 212 213 check(args) 214 215 return 0 216 217 218if __name__ == '__main__': 219 sys.exit(main()) 220