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 current_parentdir_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 80 xtstools_othersign_dir = os.path.join(current_parentdir_path, 'others', 'sign') 81 run_scripts1 = ",".join( 82 [os.path.join(xtstools_othersign_dir, "add_root.bat"), 83 os.path.join(xtstools_othersign_dir, "add_root.sh"), 84 os.path.join(xtstools_othersign_dir, "add_trust_root.py")]) 85 ohos_dir = os.path.join(self.args.source_dir, 'plugins', 'ohos') 86 gen_dir0 = os.path.join(self.args.source_dir, 'dist') 87 gen_dir1 = os.path.join(ohos_dir, 'dist') 88 shutil.rmtree(gen_dir0, ignore_errors=True) 89 shutil.rmtree(gen_dir1, ignore_errors=True) 90 command0 = ["python", "setup.py", "sdist"] 91 command1 = ["python", "setup.py", "sdist"] 92 try: 93 subprocess.check_call(command0, cwd=self.args.source_dir) 94 subprocess.check_call(command1, cwd=ohos_dir) 95 except subprocess.CalledProcessError as exc: 96 print('returncode: {} cmd: {} output: {}'.format( 97 exc.returncode, exc.cmd, exc.output)) 98 99 run_scripts = ",".join( 100 [os.path.join(self.args.source_dir, "run.bat"), 101 os.path.join(self.args.source_dir, "run.sh")]) 102 103 dist_tools_dir = os.path.join(self.args.suite_out_dir, 'tools') 104 utils.copy_file(output=dist_tools_dir, source_dirs=gen_dir0, 105 to_dir=True) 106 utils.copy_file(output=dist_tools_dir, source_dirs=gen_dir1, 107 to_dir=True) 108 utils.copy_file(output=self.args.suite_out_dir, sources=run_scripts, 109 to_dir=True) 110 dist_sign_dir = os.path.join(self.args.suite_out_dir, 'sign') 111 utils.copy_file(output=dist_sign_dir, sources=run_scripts1, 112 to_dir=True) 113 xtssuite_out_dir = self.args.suite_out_dir 114 if (xtssuite_out_dir.find('/acts/') != -1): 115 acts_validator_dir = os.path.join(self.args.suite_out_dir, '../acts-validator') 116 acts_validator_tools_dir = os.path.join(self.args.suite_out_dir, '../acts-validator/tools') 117 acts_validator_sign_dir = os.path.join(self.args.suite_out_dir, '../acts-validator/sign') 118 utils.copy_file(output=acts_validator_tools_dir, source_dirs=gen_dir0, 119 to_dir=True) 120 utils.copy_file(output=acts_validator_tools_dir, source_dirs=gen_dir1, 121 to_dir=True) 122 utils.copy_file(output=acts_validator_dir, sources=run_scripts, 123 to_dir=True) 124 utils.copy_file(output=acts_validator_sign_dir, sources=run_scripts1, 125 to_dir=True) 126 127 128 if self.args.configs_dir: 129 dist_configs_dir = os.path.join(self.args.suite_out_dir, 'config') 130 utils.copy_file(output=dist_configs_dir, 131 source_dirs=self.args.configs_dir, to_dir=True) 132 if (xtssuite_out_dir.find('/acts/') != -1): 133 acts_validator_config_dir = os.path.join(self.args.suite_out_dir, '../acts-validator/config') 134 utils.copy_file(output=acts_validator_config_dir, 135 source_dirs=self.args.configs_dir, to_dir=True) 136 if self.args.resources_dir: 137 dist_resources_dir = os.path.join(self.args.suite_out_dir, 138 'resource') 139 utils.copy_file(output=dist_resources_dir, 140 source_dirs=self.args.resources_dir, to_dir=True) 141 142 143class SuiteArchiveBuilder: 144 def __init__(self, arguments): 145 self.module_info_dir = None 146 self.arguments = arguments 147 148 def archive_suite(self): 149 parser = argparse.ArgumentParser() 150 parser.add_argument('--suite_path', help='', required=True) 151 parser.add_argument('--prebuilts_resource', help='', required=True) 152 parser.add_argument('--suite_archive_dir', help='', required=True) 153 parser.add_argument('--make_archive', help='', required=True) 154 args = parser.parse_args(self.arguments) 155 156 if not args.make_archive.lower() == 'true': 157 print('make archive disabled') 158 return 0 159 160 suite_path = args.suite_path 161 if not os.path.isdir(suite_path): 162 raise Exception("[%s] does not exist" % suite_path) 163 164 copyfiles = args.prebuilts_resource.split(",") 165 for file_path in copyfiles: 166 subprocess.call(["cp", "-rf", file_path, suite_path]) 167 168 archive_name = os.path.basename(suite_path) 169 archive_root_path = args.suite_archive_dir 170 if not os.path.isdir(archive_root_path): 171 os.mkdir(archive_root_path) 172 # remove the extra output of target "java_prebuilt" 173 # such as ztest-tradefed-common.interface.jar 174 subprocess.call(["find", suite_path, "-name", "*.interface.jar", 175 "-exec", "rm", "{}", "+"]) 176 shutil.make_archive(os.path.join(archive_root_path, archive_name), 177 "zip", suite_path) 178 return 0 179 180 181class SuiteModuleWithTestbundleBuilder: 182 183 def __init__(self, arguments): 184 self.arguments = arguments 185 self.args = None 186 187 def build_module_with_testbundle(self): 188 parser = argparse.ArgumentParser() 189 parser.add_argument('--build_gen_dir', help='', required=True) 190 parser.add_argument('--build_target_name', help='', required=True) 191 parser.add_argument('--subsystem_name', help='', 192 required=False) 193 parser.add_argument('--part_name', help='', 194 required=False) 195 parser.add_argument('--buildgen_testfile', help='', required=True) 196 parser.add_argument('--project_path', help='', required=True) 197 parser.add_argument('--test_xml', help='', required=False) 198 parser.add_argument('--project_type', help='', required=True) 199 parser.add_argument('--suite_out_dir', help='', required=True) 200 parser.add_argument('--archive_testfile', help='', required=True) 201 parser.add_argument('--apilibrary_deps', help='', required=False) 202 parser.add_argument('--test_files', help='', required=False) 203 204 self.args = parser.parse_args(self.arguments) 205 206 self._create_testsuite(self.args) 207 return 0 208 209 def _create_testsuite(self, args): 210 _test_xml = args.test_xml 211 _testcases_dir = os.path.dirname(args.archive_testfile) 212 _testsuite_name = os.path.basename(args.archive_testfile)\ 213 .replace('.hap', '').replace('module_', '') 214 _testcase_xml = os.path.join(_testcases_dir, _testsuite_name + ".xml") 215 _config_file = os.path.join(_testcases_dir, 216 _testsuite_name + ".config") 217 _json_file = os.path.join(_testcases_dir, _testsuite_name + ".json") 218 219 if args.project_type == "js_test_hap": 220 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 221 _testsuite_name, _json_file) 222 dest_file = os.path.join( 223 _testcases_dir, "{}.hap".format(_testsuite_name)) 224 self._copy_file(args.buildgen_testfile, dest_file) 225 os.chmod(dest_file, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) 226 return 227 if args.project_type == "pythontest": 228 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 229 _testsuite_name, os.path.join( 230 args.archive_testfile, _testsuite_name + ".json")) 231 utils.copy_file(output=self.args.archive_testfile, 232 source_dirs=self.args.test_files) 233 return 234 self._check_file_exist(args.buildgen_testfile) 235 self._copy_file(args.buildgen_testfile, args.archive_testfile) 236 if args.project_type == "app": 237 return 238 self._record_testmodule_info(args.build_target_name, 239 _testsuite_name, 240 _testcases_dir) 241 self._record_testpart_info(args.build_target_name, 242 _testsuite_name, 243 _testcases_dir, 244 args.subsystem_name,args.part_name) 245 if _test_xml and os.path.exists(_test_xml): 246 self._copy_file(_test_xml, _config_file) 247 elif _test_xml.replace(".xml", ".json") and \ 248 os.path.exists(_test_xml.replace(".xml", ".json")): 249 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 250 _testsuite_name, _json_file) 251 else: 252 self._generate_xml_by_template(_test_xml, _testsuite_name, 253 _config_file) 254 _resource_srcroot = _get_resource_rootpath(args.project_path) 255 self._archive_test_file_to_testcase(_testcases_dir) 256 257 @staticmethod 258 def _record_testmodule_info(build_target_name, module_name, testcases_dir): 259 if not build_target_name or not module_name: 260 raise ValueError( 261 'Ethire build_target_name or module_name is invalid') 262 263 module_info_list_file = os.path.join(testcases_dir, 'module_info.list') 264 lines = [] 265 if os.path.isfile(module_info_list_file): 266 with open(module_info_list_file, 'r') as file_read: 267 for line in file_read: 268 lines.append(line.strip()) 269 270 new_lines = ['%s %s' % (build_target_name, module_name)] 271 for line in lines: 272 arr = line.strip().split(' ') 273 if len(arr) == 0 or arr[0] == build_target_name: 274 continue 275 new_lines.append(line) 276 277 # add module info 278 new_lines.sort() 279 with open(module_info_list_file, 'w') as file_write: 280 file_write.write('\n'.join(new_lines) + '\n') 281 282 @staticmethod 283 def _record_testpart_info(build_target_name, module_name, testcases_dir, subsystem_name, part_name): 284 if not build_target_name or not module_name: 285 raise ValueError( 286 'Ethire build_target_name or module_name is invalid') 287 288 module_info_file = os.path.join(testcases_dir, module_name+'.moduleInfo') 289 290 if os.path.exists(module_info_file): 291 return 292 module_info_data = {'subsystem': subsystem_name, 'part': part_name, 293 'module': module_name} 294 with open(module_info_file, 'w') as out_file: 295 json.dump(module_info_data, out_file) 296 297 @staticmethod 298 def _generate_json_by_template(source_file, module_name, dest_file): 299 source_content = utils.read_file(source_file) 300 values = {"module": module_name} 301 xml_content = Template(source_content).substitute(values) 302 utils.write_file(dest_file, xml_content, False) 303 304 @staticmethod 305 def _generate_xml_by_template(test_xml, module_name, 306 config_file): 307 # find the template file 308 index = test_xml.rfind(".xml") 309 tmpl_file = test_xml[:index] + ".tmpl" 310 if not os.path.exists(tmpl_file): 311 raise Exception("Can't find the Test.xml or " 312 "Test.tmpl in the path %s " % 313 os.path.dirname(test_xml)) 314 tmpl_content = utils.read_file(tmpl_file) 315 values = {"module": module_name} 316 xml_content = Template(tmpl_content).substitute(values) 317 utils.write_file(config_file, xml_content, False) 318 319 def _copy_file(self, source, target): 320 if not os.path.exists(os.path.dirname(target)): 321 os.makedirs(os.path.dirname(target)) 322 if not os.path.exists(target) or ( 323 os.path.exists(target) and 324 (os.stat(target).st_mtime != os.stat(source).st_mtime)): 325 print('Trying to copy "%s" to "%s"' % (source, target)) 326 subprocess.call(["rm", "-rf", target]) 327 subprocess.call(["cp", "-rf", source, target]) 328 329 def _copy_file_to_target(self, source_file, dest_file): 330 if not os.path.exists(source_file): 331 print("[ERROR] source_file is not exist. %s" % source_file, 332 file=sys.stderr) 333 return 334 else: 335 self._copy_file(source_file, dest_file) 336 337 def _archive_test_resource(self, xml_file, root_dir, resource_dir): 338 _pattern = re.compile("[-=]>") 339 if os.path.exists(xml_file): 340 try: 341 dom = xml.dom.minidom.parse(xml_file) 342 childroot = dom.getElementsByTagName("target_preparer") 343 for child in childroot: 344 for child_atrr in child.getElementsByTagName("option"): 345 name = child_atrr.getAttribute("name") 346 attr_value = child_atrr.getAttribute("value") 347 value = _pattern.split(attr_value)[0].strip() 348 if name != "" or value != "": 349 if name == "push" and \ 350 value.startswith("resource/"): 351 self._copy_file_to_target( 352 os.path.join(root_dir, "../", value), 353 os.path.join(resource_dir, value)) 354 else: 355 raise Exception("Test.xml node name and " 356 "value not is null") 357 except IOError as error: 358 print("IO Error: %s" % error, file=sys.stderr) 359 except ExpatError as error: 360 print("ExpatError Error: %s" % error, file=sys.stderr) 361 finally: 362 pass 363 else: 364 with open(xml_file.replace(".config", ".json"), 365 "r", encoding="UTF-8") as json_file: 366 json_str = json.load(json_file) 367 kits = json_str["kits"] 368 for kit in kits: 369 types = kit["type"] 370 if types == "PushKit": 371 push_resources = kit["push"] 372 for resource in push_resources: 373 value = _pattern.split(resource)[0].strip() 374 if value.startswith("resource/"): 375 self._copy_file_to_target( 376 os.path.join(root_dir, "../", value), 377 os.path.join(resource_dir, value)) 378 379 def _archive_test_file_to_testcase(self, cases_dir): 380 if self.args.test_files is None: 381 return 382 arr_test_files = self.args.test_files.split(',') 383 for file in arr_test_files: 384 if file == "": 385 continue 386 self._copy_file_to_target(file, 387 os.path.join(cases_dir, 388 os.path.basename(file))) 389 390 def _check_file_exist(self, file_path): 391 if not os.path.exists(file_path): 392 raise Exception("File [%s] does not exist!" % file_path) 393 394 395ACTION_MAP = {"build_module": "SuiteModuleBuilder", 396 "build_xdevice": "XDeviceBuilder", 397 "archive_suite": "SuiteArchiveBuilder", 398 "build_module_with_testbundle": 399 "SuiteModuleWithTestbundleBuilder"} 400 401 402def _find_action(action, arguments): 403 class_name = ACTION_MAP[action] 404 if not class_name: 405 raise ValueError('Unsupported operation: %s' % action) 406 407 this_module = sys.modules[__name__] 408 class_def = getattr(this_module, class_name, None) 409 if not class_def: 410 raise ValueError( 411 'Unsupported operation(No Implementation Class): %s' % action) 412 class_obj = class_def(arguments) 413 func = getattr(class_obj, action, None) 414 if not func: 415 raise ValueError( 416 'Unsupported operation(No Implementation Method): %s' % action) 417 return func 418 419 420def main(arguments): 421 action = arguments[0] 422 args = arguments[1:] 423 func = _find_action(action, args) 424 func() 425 return 0 426 427 428if __name__ == '__main__': 429 sys.exit(main(sys.argv[1:])) 430