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