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