1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3""" 4Copyright (c) 2020-2021 Huawei Device Co., Ltd. 5Licensed under the Apache License, Version 2.0 (the "License"); 6you may not use this file except in compliance with the License. 7You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11Unless required by applicable law or agreed to in writing, software 12distributed under the License is distributed on an "AS IS" BASIS, 13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14See the License for the specific language governing permissions and 15limitations under the License. 16""" 17 18 19import os 20import sys 21import argparse 22import re 23import subprocess 24import xml.dom.minidom 25from xml.parsers.expat import ExpatError 26from string import Template 27import utils 28import json 29import shutil 30import glob 31import stat 32 33 34XTS_RESOURCE_ROOT = 'ivbxfj`qspqsjfubsz0sftpvsdf' 35 36 37def _get_xts_rootpath(project_path): 38 if project_path is not None: 39 return project_path[0:project_path.find('/xts/') + len('/xts/')] 40 raise ValueError('Illegal xts project path ' + project_path) 41 42 43def _get_subsystem_name(project_path): 44 if '/hits/' in project_path: 45 index0 = project_path.find('/hits/') + len('/hits/') 46 elif '/acts/' in project_path: 47 index0 = project_path.find('/acts/') + len('/acts/') 48 else: 49 raise ValueError('Illegal xts project path ' + project_path) 50 index1 = project_path.find('/', index0) 51 return project_path[index0:index1] 52 53 54def _get_resource_rootpath(project_path): 55 xts_root = _get_xts_rootpath(project_path) 56 resource_reldir = ''.join(chr(ord(ch) - 1) for ch in XTS_RESOURCE_ROOT) 57 return os.path.join(xts_root, resource_reldir) 58 59 60class XDeviceBuilder: 61 """ 62 To build XTS as an egg package 63 @arguments(project_dir, output_dirs) 64 """ 65 66 def __init__(self, arguments): 67 parser = argparse.ArgumentParser() 68 parser.add_argument('--source_dir', help='', required=True) 69 parser.add_argument('--configs_dir', help='', required=False) 70 parser.add_argument('--resources_dir', help='', required=False) 71 parser.add_argument('--suite_out_dir', help='', required=True) 72 self.args = parser.parse_args(arguments) 73 74 def build_xdevice(self): 75 """ 76 build xdevice package 77 :return: 78 """ 79 extension_dir = os.path.join(self.args.source_dir, 'extension') 80 gen_dir0 = os.path.join(self.args.source_dir, 'dist') 81 gen_dir1 = os.path.join(extension_dir, 'dist') 82 shutil.rmtree(gen_dir0, ignore_errors=True) 83 shutil.rmtree(gen_dir1, ignore_errors=True) 84 command0 = ["python", "setup.py", "sdist"] 85 command1 = ["python", "setup.py", "sdist"] 86 try: 87 subprocess.check_call(command0, cwd=self.args.source_dir) 88 subprocess.check_call(command1, cwd=extension_dir) 89 except subprocess.CalledProcessError as exc: 90 print('returncode: {} cmd: {} output: {}'.format( 91 exc.returncode, exc.cmd, exc.output)) 92 93 run_scripts = ",".join( 94 [os.path.join(self.args.source_dir, "run.bat"), 95 os.path.join(self.args.source_dir, "run.sh")]) 96 97 dist_tools_dir = os.path.join(self.args.suite_out_dir, 'tools') 98 utils.copy_file(output=dist_tools_dir, source_dirs=gen_dir0, 99 to_dir=True) 100 utils.copy_file(output=dist_tools_dir, source_dirs=gen_dir1, 101 to_dir=True) 102 utils.copy_file(output=self.args.suite_out_dir, sources=run_scripts, 103 to_dir=True) 104 105 if self.args.configs_dir: 106 dist_configs_dir = os.path.join(self.args.suite_out_dir, 'config') 107 utils.copy_file(output=dist_configs_dir, 108 source_dirs=self.args.configs_dir, to_dir=True) 109 if self.args.resources_dir: 110 dist_resources_dir = os.path.join(self.args.suite_out_dir, 111 'resource') 112 utils.copy_file(output=dist_resources_dir, 113 source_dirs=self.args.resources_dir, to_dir=True) 114 115 116class SuiteArchiveBuilder: 117 def __init__(self, arguments): 118 self.module_info_dir = None 119 self.arguments = arguments 120 121 def archive_suite(self): 122 parser = argparse.ArgumentParser() 123 parser.add_argument('--suite_path', help='', required=True) 124 parser.add_argument('--prebuilts_resource', help='', required=True) 125 parser.add_argument('--suite_archive_dir', help='', required=True) 126 parser.add_argument('--make_archive', help='', required=True) 127 args = parser.parse_args(self.arguments) 128 129 if not args.make_archive.lower() == 'true': 130 print('make archive disabled') 131 return 0 132 133 suite_path = args.suite_path 134 if not os.path.isdir(suite_path): 135 raise Exception("[%s] does not exist" % suite_path) 136 137 copyfiles = args.prebuilts_resource.split(",") 138 for file_path in copyfiles: 139 subprocess.call(["cp", "-rf", file_path, suite_path]) 140 141 archive_name = os.path.basename(suite_path) 142 archive_root_path = args.suite_archive_dir 143 if not os.path.isdir(archive_root_path): 144 os.mkdir(archive_root_path) 145 # remove the extra output of target "java_prebuilt" 146 # such as ztest-tradefed-common.interface.jar 147 subprocess.call(["find", suite_path, "-name", "*.interface.jar", 148 "-exec", "rm", "{}", "+"]) 149 shutil.make_archive(os.path.join(archive_root_path, archive_name), 150 "zip", suite_path) 151 return 0 152 153 154class SuiteModuleWithTestbundleBuilder: 155 156 def __init__(self, arguments): 157 self.arguments = arguments 158 self.args = None 159 160 def build_module_with_testbundle(self): 161 parser = argparse.ArgumentParser() 162 parser.add_argument('--build_gen_dir', help='', required=True) 163 parser.add_argument('--build_target_name', help='', required=True) 164 parser.add_argument('--subsystem_name', help='', 165 required=False) 166 parser.add_argument('--buildgen_testfile', help='', required=True) 167 parser.add_argument('--project_path', help='', required=True) 168 parser.add_argument('--test_xml', help='', required=False) 169 parser.add_argument('--project_type', help='', required=True) 170 parser.add_argument('--suite_out_dir', help='', required=True) 171 parser.add_argument('--archive_testfile', help='', required=True) 172 parser.add_argument('--apilibrary_deps', help='', required=False) 173 parser.add_argument('--test_files', help='', required=False) 174 175 self.args = parser.parse_args(self.arguments) 176 177 self._create_testsuite(self.args) 178 return 0 179 180 def _create_testsuite(self, args): 181 _test_xml = args.test_xml 182 _testcases_dir = os.path.dirname(args.archive_testfile) 183 _testsuite_name = os.path.basename(args.archive_testfile)\ 184 .replace('.hap', '').replace('module_', '') 185 _testcase_xml = os.path.join(_testcases_dir, _testsuite_name + ".xml") 186 _config_file = os.path.join(_testcases_dir, 187 _testsuite_name + ".config") 188 _json_file = os.path.join(_testcases_dir, _testsuite_name + ".json") 189 190 if args.project_type == "js_test_hap": 191 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 192 _testsuite_name, _json_file) 193 dest_file = os.path.join( 194 _testcases_dir, "{}.hap".format(_testsuite_name)) 195 self._copy_file(args.buildgen_testfile, dest_file) 196 os.chmod(dest_file, stat.S_IRWXU|stat.S_IRWXG|stat.S_IRWXO) 197 return 198 if args.project_type == "pythontest": 199 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 200 _testsuite_name, os.path.join( 201 args.archive_testfile, _testsuite_name + ".json")) 202 utils.copy_file(output=self.args.archive_testfile, 203 source_dirs=self.args.test_files) 204 return 205 self._check_file_exist(args.buildgen_testfile) 206 self._copy_file(args.buildgen_testfile, args.archive_testfile) 207 if args.project_type == "app": 208 return 209 self._record_testmodule_info(args.build_target_name, 210 _testsuite_name, 211 _testcases_dir) 212 if _test_xml and os.path.exists(_test_xml): 213 self._copy_file(_test_xml, _config_file) 214 elif _test_xml.replace(".xml", ".json") and \ 215 os.path.exists(_test_xml.replace(".xml", ".json")): 216 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 217 _testsuite_name, _json_file) 218 else: 219 self._generate_xml_by_template(_test_xml, _testsuite_name, 220 _config_file) 221 #self._generate_testcase_xml(args) 222 _resource_srcroot = _get_resource_rootpath(args.project_path) 223 self._archive_test_file_to_testcase(_testcases_dir) 224 225 @staticmethod 226 def _record_testmodule_info(build_target_name, module_name, testcases_dir): 227 if not build_target_name or not module_name: 228 raise ValueError( 229 'Ethire build_target_name or module_name is invalid') 230 231 module_info_list_file = os.path.join(testcases_dir, 'module_info.list') 232 lines = [] 233 if os.path.isfile(module_info_list_file): 234 with open(module_info_list_file, 'r') as file_read: 235 for line in file_read: 236 lines.append(line.strip()) 237 238 new_lines = ['%s %s' % (build_target_name, module_name)] 239 for line in lines: 240 arr = line.strip().split(' ') 241 if len(arr) == 0 or arr[0] == build_target_name: 242 continue 243 new_lines.append(line) 244 245 # add module info 246 with open(module_info_list_file, 'w') as file_write: 247 file_write.write('\n'.join(new_lines) + '\n') 248 249 250 @staticmethod 251 def _generate_json_by_template(source_file, module_name, dest_file): 252 source_content = utils.read_file(source_file) 253 values = {"module": module_name} 254 xml_content = Template(source_content).substitute(values) 255 utils.write_file(dest_file, xml_content, False) 256 257 @staticmethod 258 def _generate_xml_by_template(test_xml, module_name, 259 config_file): 260 # find the template file 261 index = test_xml.rfind(".xml") 262 tmpl_file = test_xml[:index] + ".tmpl" 263 if not os.path.exists(tmpl_file): 264 raise Exception("Can't find the Test.xml or " 265 "Test.tmpl in the path %s " % 266 os.path.dirname(test_xml)) 267 tmpl_content = utils.read_file(tmpl_file) 268 values = {"module": module_name} 269 xml_content = Template(tmpl_content).substitute(values) 270 utils.write_file(config_file, xml_content, False) 271 272 def _copy_file(self, source, target): 273 if not os.path.exists(os.path.dirname(target)): 274 os.makedirs(os.path.dirname(target)) 275 if not os.path.exists(target) or ( 276 os.path.exists(target) and 277 (os.stat(target).st_mtime != os.stat(source).st_mtime)): 278 print('Trying to copy "%s" to "%s"' % (source, target)) 279 subprocess.call(["rm", "-rf", target]) 280 subprocess.call(["cp", "-rf", source, target]) 281 282 def _copy_file_to_target(self, source_file, dest_file): 283 if not os.path.exists(source_file): 284 print("[ERROR] source_file is not exist. %s" % source_file, 285 file=sys.stderr) 286 return 287 else: 288 self._copy_file(source_file, dest_file) 289 290 def _archive_test_resource(self, xml_file, root_dir, resource_dir): 291 _pattern = re.compile("[-=]>") 292 if os.path.exists(xml_file): 293 try: 294 dom = xml.dom.minidom.parse(xml_file) 295 childroot = dom.getElementsByTagName("target_preparer") 296 for child in childroot: 297 for child_atrr in child.getElementsByTagName("option"): 298 name = child_atrr.getAttribute("name") 299 attr_value = child_atrr.getAttribute("value") 300 value = _pattern.split(attr_value)[0].strip() 301 if name != "" or value != "": 302 if name == "push" and \ 303 value.startswith("resource/"): 304 self._copy_file_to_target( 305 os.path.join(root_dir, "../", value), 306 os.path.join(resource_dir, value)) 307 else: 308 raise Exception("Test.xml node name and " 309 "value not is null") 310 except IOError as error: 311 print("IO Error: %s" % error, file=sys.stderr) 312 except ExpatError as error: 313 print("ExpatError Error: %s" % error, file=sys.stderr) 314 finally: 315 pass 316 else: 317 with open(xml_file.replace(".config", ".json"), 318 "r", encoding="UTF-8") as json_file: 319 json_str = json.load(json_file) 320 kits = json_str["kits"] 321 for kit in kits: 322 types = kit["type"] 323 if types == "PushKit": 324 push_resources = kit["push"] 325 for resource in push_resources: 326 value = _pattern.split(resource)[0].strip() 327 if value.startswith("resource/"): 328 self._copy_file_to_target( 329 os.path.join(root_dir, "../", value), 330 os.path.join(resource_dir, value)) 331 332 def _archive_test_file_to_testcase(self, cases_dir): 333 if self.args.test_files is None: 334 return 335 arr_test_files = self.args.test_files.split(',') 336 for file in arr_test_files: 337 if file == "": 338 continue 339 self._copy_file_to_target(file, 340 os.path.join(cases_dir, 341 os.path.basename(file))) 342 343 def _check_file_exist(self, file_path): 344 if not os.path.exists(file_path): 345 raise Exception("File [%s] does not exist!" % file_path) 346 347 348ACTION_MAP = {"build_module": "SuiteModuleBuilder", 349 "build_xdevice": "XDeviceBuilder", 350 "archive_suite": "SuiteArchiveBuilder", 351 "build_module_with_testbundle": 352 "SuiteModuleWithTestbundleBuilder"} 353 354 355def _find_action(action, arguments): 356 class_name = ACTION_MAP[action] 357 if not class_name: 358 raise ValueError('Unsupported operation: %s' % action) 359 360 this_module = sys.modules[__name__] 361 class_def = getattr(this_module, class_name, None) 362 if not class_def: 363 raise ValueError( 364 'Unsupported operation(No Implementation Class): %s' % action) 365 class_obj = class_def(arguments) 366 func = getattr(class_obj, action, None) 367 if not func: 368 raise ValueError( 369 'Unsupported operation(No Implementation Method): %s' % action) 370 return func 371 372 373def main(arguments): 374 action = arguments[0] 375 args = arguments[1:] 376 func = _find_action(action, args) 377 func() 378 return 0 379 380 381if __name__ == '__main__': 382 sys.exit(main(sys.argv[1:])) 383