• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright (c) 2024 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import json
19import os
20import re
21import fnmatch
22import sys
23from abc import ABC, abstractmethod
24import xml.etree.ElementTree as ET
25
26from Utils import ChangeFileEntity, XTSTargetUtils, PathUtils, MatchConfig, HOME
27
28
29class Ci_Manager(ABC):
30
31    @abstractmethod
32    def get_targets_from_change(self, change_list: list):
33        pass
34
35    def write_result(self, target_path_set: set, target_set: set):
36        print(f"{self.__class__.__name__} 增加 build_targets : {self._build_targets}")
37        print(f"{self.__class__.__name__} 增加 build_paths : {self._build_paths}")
38
39        xts_root_target = PathUtils.get_root_target(self._xts_root_dir)
40        if xts_root_target in target_set:
41            print("编译全量代码")
42            return
43        if xts_root_target in self._build_targets:
44            target_set.add(xts_root_target)
45            target_path_set.clear()
46            print("编译全量代码")
47            return
48
49        target_set.update(set(self._build_targets))
50        target_path_set.update(set(self._build_paths))
51
52
53class ComponentManager(Ci_Manager):
54
55    def __init__(self, xts_root_dir, code_root_dir):
56        self._xts_root_dir = xts_root_dir
57        self._code_root_dir = code_root_dir
58        self._build_paths = []
59        self._build_targets = []
60
61    def get_targets_from_change(self, change_list):
62        for changeFileEntity in change_list:
63            if changeFileEntity.path in MatchConfig.get_escape_list():
64                print(f"{changeFileEntity.name} 仓逃生,使用旧精准目标")
65                continue
66            if changeFileEntity.path not in MatchConfig.get_xts_path_list():
67                ret = self.getTargetsPaths(changeFileEntity)
68                if ret == 1:
69                    return 1
70        return 0
71
72    def getTargetsPaths(self, change_file_entity: ChangeFileEntity):
73        # 获取部件名
74        try:
75            bundle_name = self.getBundleName(change_file_entity.path)
76        except Exception as e:
77            print(f"读取{change_file_entity.name}部件仓bundle_name失败")
78            return 1
79        print(f"{self.__class__.__name__} 增加 bundle_name : {bundle_name}")
80        # 部件名(partname)获取paths
81        paths = XTSTargetUtils.getPathsByBundle([bundle_name], self._xts_root_dir)
82        if paths:
83            change_file_entity.set_already_match_utils(True)
84            self._build_paths += paths
85        return 0
86
87    def getBundleName(self, path) -> str:
88        with open(os.path.join(HOME, path, "bundle.json"), 'r') as file:
89            data = json.load(file)
90        bundle_name = data['component']['name']
91        return bundle_name
92
93
94class XTSManager(Ci_Manager):
95
96    def __init__(self, xts_root_dir, code_root_dir):
97        self._xts_root_dir = xts_root_dir
98        self._code_root_dir = code_root_dir
99        self._build_paths = []
100        self._build_targets = []
101        self._need_all = False
102
103    def get_targets_from_change(self, change_list):
104        for changeFileEntity in change_list:
105            # tools仓修改,编译全量
106            if changeFileEntity.path == "test/xts/tools":
107                self._need_all = True
108                changeFileEntity.set_already_match_utils(True)
109            if changeFileEntity.path in MatchConfig.get_xts_path_list() and changeFileEntity.path in self._xts_root_dir:
110                # 只有当前编译的xts仓修改参与计算
111                ret = self.getTargetsPaths(changeFileEntity)
112                if ret == 1:
113                    print(f"{changeFileEntity.name}仓修改解析失败")
114                    return 1
115        if self._need_all:
116            self._build_targets += PathUtils.get_all_build_target(self._xts_root_dir)
117        return 0
118
119    # 获取path接口
120    def getTargetsPaths(self, changeFileEntity: ChangeFileEntity):
121        # 修改和新增
122        for file in changeFileEntity.add + changeFileEntity.modified:
123            # file转为绝对路径
124            file = os.path.join(self._code_root_dir, file)
125            # 筛选掉例外的目录
126            if PathUtils.isMatchRules(file, MatchConfig.get_exception_path()):
127                continue
128            # 当前文件路径或父已存在,跳过
129            if PathUtils.isTargetContains(self._build_paths, file):
130                continue
131            # 当前file对应BUILD.gn路径
132            build_File = XTSTargetUtils.get_current_Build(self._xts_root_dir, file)
133            # 计算到根目录或指定目录,直接编译全量
134            if (os.path.dirname(build_File) == self._xts_root_dir or
135                    PathUtils.isMatchRules(file, MatchConfig.get_all_com_path())):
136                self._need_all = True
137            else:
138                self._build_paths.append(os.path.dirname(build_File))
139        # 删除
140        for file in changeFileEntity.delete:
141            # file转为绝对路径
142            file = os.path.join(self._code_root_dir, file)
143            # 筛选掉例外的目录
144            if PathUtils.isMatchRules(file, MatchConfig.get_exception_path()):
145                continue
146            # 当前文件路径或父已存在,跳过
147            if PathUtils.isTargetContains(self._build_paths, file):
148                continue
149            # 当前存在的最外层路径
150            exist_path = PathUtils.get_current_exist(self._xts_root_dir, os.path.dirname(file))
151            build_File = XTSTargetUtils.get_current_Build(self._xts_root_dir, exist_path)
152            # 计算到根目录或指定目录,直接编译全量
153            if (os.path.dirname(build_File) == self._xts_root_dir or
154                    PathUtils.isMatchRules(file, MatchConfig.get_all_com_path())):
155                self._need_all = True
156            else:
157                self._build_paths.append(os.path.dirname(build_File))
158        return 0
159
160
161class WhitelistManager(Ci_Manager):
162
163    def __init__(self, xts_root_dir, code_root_dir):
164        self._xts_root_dir = xts_root_dir
165        self._code_root_dir = code_root_dir
166        self._build_paths = []
167        self._build_targets = []
168        self.full_impact_flag = "FULL_IMPACT"
169
170    def get_targets_from_change(self, change_list):
171        for changeFileEntity in change_list:
172            if changeFileEntity.path in MatchConfig.get_escape_list():
173                print(f"{changeFileEntity.name} 仓逃生,使用旧精准目标")
174                continue
175            if changeFileEntity.path not in MatchConfig.get_xts_path_list():
176                ret = self.getTargetsandPaths(changeFileEntity)
177                if ret == 1:
178                    return 1
179        return 0
180
181    def getTargetsandPaths(self, change_file_entity):
182        white_list = MatchConfig.get_white_list_repo()
183        if change_file_entity.path not in white_list:
184            return 0
185        bundles = white_list[change_file_entity.path]["add_bundle"]
186        targets = white_list[change_file_entity.path]["add_target"]
187        if targets and targets[0] == self.full_impact_flag:
188            targets = PathUtils.get_all_build_target(self._xts_root_dir)
189        change_file_entity.set_already_match_utils(True)
190        if bundles:
191            paths = XTSTargetUtils.getPathsByBundle(bundles, self._xts_root_dir)
192            if paths:
193                self._build_paths += paths
194        if targets:
195            self._build_targets += targets
196        return 0
197
198
199class OldPreciseManager(Ci_Manager):
200
201    def __init__(self, xts_root_dir, code_root_dir):
202        self._xts_root_dir = xts_root_dir
203        self._code_root_dir = code_root_dir
204        self._build_paths = []
205        self._build_targets = []
206        self._precise_compilation_file = os.path.join(HOME, "test", "xts", "tools", "config",
207                                                      "precise_compilation.json")
208        self._init_old_precise_map()
209
210    def _init_old_precise_map(self):
211        self._old_precise_map = {}
212        with open(self._precise_compilation_file, 'r') as file:
213            data_list = json.load(file)
214        for item in data_list:
215            name = item['name']
216            build_target = item['buildTarget']
217            self._old_precise_map[name] = build_target
218
219    def get_targets_from_change(self, change_list):
220        for changeFileEntity in change_list:
221            if changeFileEntity.path not in MatchConfig.get_xts_path_list() and \
222                    not changeFileEntity.get_already_match_utils():
223                if self._xts_root_dir.endswith("acts"):
224                    ret = self.getTargets(changeFileEntity)
225                    if ret == 1:
226                        pass
227                else:
228                    self._build_targets += PathUtils.get_all_build_target(self._xts_root_dir)
229        return 0
230
231    # 获取path接口
232    def getTargets(self, changeFileEntity: ChangeFileEntity):
233        # 获取开源仓名
234        repo_name = self.search_repo_name(changeFileEntity.path)
235        # precise_compilation.json配置文件中获取对应目标
236        if repo_name in self._old_precise_map:
237            self._build_targets.append(self._old_precise_map[repo_name])
238        return 0
239
240    def getTargetsbyRepoName(self, repo_name):
241        with open(repo_name, 'r') as file:
242            data_list = json.load(file)
243        # 遍历列表中的每个字典
244        for item in data_list:
245            # 获取 name 和 buildTarget 的值
246            name = item['name']
247            build_target = item['buildTarget']
248            # 打印结果
249            print(f'Name: {name}, Build Target: {build_target}')
250
251    def search_repo_name(self, repo_path, directory=os.path.join(HOME, ".repo", "manifests")):
252        for root, dirs, files in os.walk(directory):
253            for filename in fnmatch.filter(files, '*.xml'):
254                file_path = os.path.join(root, filename)
255                for child in ET.parse(file_path).getroot().findall('project'):
256                    if 'path' in child.attrib and child.attrib['path'] == repo_path:
257                        if 'gitee_name' in child.attrib:
258                            return child.attrib['gitee_name']
259                        if 'name' in child.attrib:
260                            return child.attrib['name']
261        return None
262
263class GetInterfaceData(Ci_Manager):
264
265    def __init__(self, xts_root_dir, code_root_dir):
266        self._xts_root_dir = xts_root_dir
267        self._code_root_dir = code_root_dir
268        self._build_paths = []
269        self._build_targets = []
270        self.sum_change_list_path = []
271        self.bundle_name_list = []
272        self.match_path_list = []
273        self.no_match_path_list = []
274
275    # 截取路径
276    def get_first_levels_path(self, path, num):
277        normalized_path = os.path.normpath(path)
278        parts = normalized_path.split(os.sep)
279        return os.sep.join(parts[:num])
280
281    def path_error(self, path_list):
282        if len(path_list) > 0:
283            raise Exception('Error:  interface 路径无法匹配 bundle_name, 请前往 test/xts/tools/config/ci_api_part_name.json 配置对应 path 与 bundle_name')
284
285    def get_targets_from_change(self, change_list):
286
287        # 分开处理三个 interface 仓
288        self.get_c_bundle_name(change_list, MatchConfig.get_interface_json_c_data())
289        self.get_driver_interface_bundle_name(change_list, MatchConfig.get_interface_json_driver_interface_data())
290        self.get_js_bundle_name(change_list, MatchConfig.get_interface_json_js_data())
291        # 筛选出未匹配路径
292        for path in self.sum_change_list_path:
293            if path not in self.match_path_list:
294               self.no_match_path_list.append(path)
295
296        self.bundle_name_list = list(set(self.bundle_name_list))
297        self._build_targets = list(set(self._build_targets))
298        print('INTERFACE_BUNDLE_NAME = ', self.bundle_name_list)
299
300        # 抛出未匹配到 bundle_name 的路径
301        try:
302            self.path_error(self.no_match_path_list)
303        except Exception as e:
304            print(e)
305            for path in self.no_match_path_list:
306                print('Error: 无法匹配路径: ', path)
307            sys.exit(1)
308
309        # 根据bundle_name 查找对应 build_paths
310        self._build_paths = XTSTargetUtils.getPathsByBundle(self.bundle_name_list, self._xts_root_dir)
311
312    # 处理 interface/sdk-js
313    def get_js_bundle_name(self, change_list, js_json_data):
314        # 获取 change_list interface/sdk-js 仓数据
315        for store in change_list:
316            if store.path != MatchConfig.get_interface_path_list()[0]:
317                continue
318            paths = store.add + store.modified + store.delete
319            self.sum_change_list_path += paths
320            targete_data = [data for data in js_json_data if data['path'] in paths]
321            for _data in targete_data:
322                store.set_already_match_utils(True)
323                self.match_path_list.append(_data.get('path'))
324                # 找出 bundle_name 加入 self.bundle_name_list
325                self.bundle_name_list += _data.get('bundle_name')
326                # 针对 interface/sdk-js/kitsinterface/sdk-js/arkts 在配置文件中查找 build_target
327                self._build_targets += _data.get('build_targets')
328            # 根据路径匹配
329            for path in paths:
330                for source_path in js_json_data:
331                    if PathUtils.is_parent_path(source_path.get('path'), path):
332                        self.match_path_list.append(path)
333                        store.set_already_match_utils(True)
334                        self.bundle_name_list += source_path.get('bundle_name')
335
336    # 处理 driver_interface 仓
337    def get_driver_interface_bundle_name(self, change_list, driver_interface_json_data):
338        add_bundle_json_path = []
339        for store in change_list:
340            # 获取 change_list driver_interface 仓数据
341            if store.path != MatchConfig.get_interface_path_list()[2]:
342                continue
343            for path in store.add + store.modified + store.delete:
344                    self.sum_change_list_path.append(path)
345                    for source_path in driver_interface_json_data:
346                        # 从 drivers/interface 下第一级目录截取路径在配置文件中查找 bundle_name
347                        if self.get_first_levels_path(path, 3) == source_path.get('path'):
348                            store.set_already_match_utils(True)
349                            self.bundle_name_list += source_path.get('bundle_name')
350                            self.match_path_list.append(path)
351
352            # 查看新增目录下是否有 bundle.json
353            for path in store.add:
354                if os.path.basename(path) == 'bundle.json':
355                    try:
356                        # 发现 bundle.json 后进去文件寻找 bundle_name
357                        with open(os.path.abspath(__file__).split('/test/')[0] + '/' + path, 'r') as d:
358                            for k, v in json.load(d).items():
359                                if k == 'component':
360                                    add_bundle_json_path.append([path, v['name']])
361                    except FileNotFoundError:
362                        print('Error:  drivers/interface仓新增目录且在目录下未发现 bundle.json, 无法匹配 bundle_name, 请添加 bundle.json 文件')
363                        sys.exit(1)
364
365            # 处理新增目录下其他文件
366            for path in store.add:
367                for path_name in add_bundle_json_path:
368                    if self.get_first_levels_path(path, 3) == self.get_first_levels_path(path_name[0], 3):
369                        self.match_path_list.append(path)
370                        store.set_already_match_utils(True)
371                        self.bundle_name_list.append(path_name[1])
372
373    # 处理 interface/sdk_c
374    def get_c_bundle_name(self, change_list, c_json_data):
375        for store in change_list:
376            # 获取 change_list interface/sdk_c 仓数据
377            if store.path != MatchConfig.get_interface_path_list()[1]:
378                continue
379            for path in store.add + store.modified + store.delete:
380                self.sum_change_list_path.append(path)
381                for source_path in c_json_data:
382                    # 根据目录匹配
383                    if PathUtils.is_parent_path(source_path.get('path'), path):
384                        self.match_path_list.append(path)
385                        store.set_already_match_utils(True)
386                        self.bundle_name_list += source_path.get('bundle_name')
387                    # 根据文件匹配
388                    elif path == source_path.get('path'):
389                        self.match_path_list.append(path)
390                        store.set_already_match_utils(True)
391                        self.bundle_name_list += source_path.get('bundle_name')