• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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