1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# 5# Copyright (c) 2022 Huawei Device Co., Ltd. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19import subprocess 20import os 21import re 22from hb_internal.common.utils import OHOSException, read_json_file 23from hb.__main__ import find_top 24 25 26def add_options(parser): 27 group = parser.add_mutually_exclusive_group(required=True) 28 group.add_argument("--ls", 29 action='store_true', 30 default=False, 31 help='lists all targets matching the given pattern for the given build directory, ' 32 'refer to gn ls. ' 33 'eg1: hb tool --ls ' 34 'eg2: hb tool --ls --all-toolchains') 35 group.add_argument('--desc', 36 action='store_true', 37 default=False, 38 help='displays information about a given target or config, refer to gn desc, ' 39 'The --tree and --blame options are enabled by default. ' 40 'eg1: hb tool --desc init:innergroup ' 41 'eg2: hb tool --desc ai_engine:ai --all') 42 group.add_argument('--path', 43 action='store_true', 44 default=False, 45 help='finds paths of dependencies between two targets, refer to gn path, ' 46 'The --all options are enabled by default. ' 47 'eg1: hb tool --path init:innergroup screenlock_mgr:screenlock_utils ' 48 'eg2: hb tool --path init:innergroup screenlock_mgr:screenlock_utils --public') 49 parser.add_argument('--out-path', 50 nargs=1, 51 help='specify the build directory, ' 52 'eg: hb tool --ls --out-path out/rk3568') 53 54 55class Tool(): 56 def __init__(self, out_path=None): 57 self.cmd_list = [] 58 self.gn_default = {'ls': [], 'desc': ['--tree', '--blame'], 'path': ['--all']} 59 if out_path: 60 self.out_path = out_path[0] 61 else: 62 self.out_path = read_json_file(os.path.join(find_top(), 'ohos_config.json'))['out_path'] 63 if not os.path.isdir(self.out_path): 64 raise OHOSException(f"{self.out_path} doesn't exist.") 65 66 def register_target(self, component, module): 67 target_name = self.get_target_name(component, module) 68 if target_name: 69 self.cmd_list.append(target_name) 70 else: 71 raise OHOSException(f'The input {component}:{module} matches no targets, configs or files.') 72 73 def get_target_name(self, component, module): 74 root_path = os.path.join(self.out_path, "build_configs") 75 target_info = "" 76 module_list = [] 77 for file in os.listdir(root_path): 78 if len(target_info): 79 break 80 file_path = os.path.join(root_path, file) 81 if not os.path.isdir(file_path): 82 continue 83 for component_name in os.listdir(file_path): 84 if os.path.isdir(os.path.join(file_path, component_name)) and component_name == component: 85 target_info = self.read_gn_file(os.path.join(file_path, component_name, "BUILD.gn")) 86 break 87 pattern = re.compile(r'(?<=module_list = )\[([^\[\]]*)\]') 88 results = pattern.findall(target_info) 89 for each_tuple in results: 90 module_list = each_tuple.replace('\n', '').replace(' ', '').replace('\"', '').split(',') 91 for target_path in module_list: 92 if target_path != '': 93 path, target = target_path.split(":") 94 if target == module: 95 return target_path 96 return 0 97 98 def read_gn_file(self, input_file): 99 if not os.path.exists(input_file): 100 raise OHOSException("file '{}' doesn't exist.".format(input_file)) 101 data = None 102 with open(input_file, 'r') as input_f: 103 data = input_f.read() 104 return data 105 106 def get_gn_path(self): 107 gn_path = "" 108 config_data = read_json_file(os.path.join(find_top(), 'build/prebuilts_download_config.json')) 109 copy_config_list = config_data[os.uname().sysname.lower()][os.uname().machine.lower()]['copy_config'] 110 for config in copy_config_list: 111 if config['unzip_filename'] == 'gn': 112 gn_path = os.path.join(find_top(), config['unzip_dir'], 'gn') 113 return gn_path 114 115 116 def exec_gn_cmd(self, gn_cmd): 117 gn_path = self.get_gn_path() 118 for arg in self.gn_default.get(gn_cmd): 119 if arg not in self.cmd_list: 120 self.cmd_list.append(arg) 121 cmds = [gn_path, gn_cmd, self.out_path] 122 cmds.extend(self.cmd_list) 123 pipe = subprocess.Popen( 124 cmds, 125 stdout=subprocess.PIPE, 126 shell=False, 127 encoding='utf8' 128 ) 129 while pipe.poll() is None: 130 out = pipe.stdout.readline() 131 if out != "": 132 print(out, end="") 133 134 135def exec_command(args): 136 tool = Tool(args[0].out_path) 137 gn_args = args[1] 138 if args[0].ls: 139 tool.cmd_list.extend(gn_args) 140 tool.exec_gn_cmd('ls') 141 142 if args[0].desc: 143 try: 144 component, module = gn_args[0].split(':') 145 tool.register_target(component, module) 146 gn_args.remove(gn_args[0]) 147 except Exception: 148 raise OHOSException(f'Invalid desc args: {gn_args[0]} ,need <component:module>') 149 tool.cmd_list.extend(gn_args) 150 tool.exec_gn_cmd('desc') 151 152 if args[0].path: 153 try: 154 for i in range(2): 155 component, module = gn_args[0].split(':') 156 tool.register_target(component, module) 157 gn_args.remove(gn_args[0]) 158 except Exception: 159 raise OHOSException('Invalid path args: need <component_one:module_one> <component_two:module_two>') 160 tool.cmd_list.extend(gn_args) 161 tool.exec_gn_cmd('path') 162