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 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('--part_name', help='', 167 required=False) 168 parser.add_argument('--buildgen_testfile', help='', required=True) 169 parser.add_argument('--project_path', help='', required=True) 170 parser.add_argument('--test_xml', help='', required=False) 171 parser.add_argument('--project_type', help='', required=True) 172 parser.add_argument('--suite_out_dir', help='', required=True) 173 parser.add_argument('--archive_testfile', help='', required=True) 174 parser.add_argument('--apilibrary_deps', help='', required=False) 175 parser.add_argument('--test_files', help='', required=False) 176 177 self.args = parser.parse_args(self.arguments) 178 179 self._create_testsuite(self.args) 180 return 0 181 182 def _create_testsuite(self, args): 183 _test_xml = args.test_xml 184 _testcases_dir = os.path.dirname(args.archive_testfile) 185 _testsuite_name = os.path.basename(args.archive_testfile)\ 186 .replace('.hap', '').replace('module_', '') 187 _testcase_xml = os.path.join(_testcases_dir, _testsuite_name + ".xml") 188 _config_file = os.path.join(_testcases_dir, 189 _testsuite_name + ".config") 190 _json_file = os.path.join(_testcases_dir, _testsuite_name + ".json") 191 192 if args.project_type == "js_test_hap": 193 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 194 _testsuite_name, _json_file) 195 dest_file = os.path.join( 196 _testcases_dir, "{}.hap".format(_testsuite_name)) 197 self._copy_file(args.buildgen_testfile, dest_file) 198 os.chmod(dest_file, stat.S_IRWXU|stat.S_IRWXG|stat.S_IRWXO) 199 return 200 if args.project_type == "pythontest": 201 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 202 _testsuite_name, os.path.join( 203 args.archive_testfile, _testsuite_name + ".json")) 204 utils.copy_file(output=self.args.archive_testfile, 205 source_dirs=self.args.test_files) 206 return 207 self._check_file_exist(args.buildgen_testfile) 208 self._copy_file(args.buildgen_testfile, args.archive_testfile) 209 if args.project_type == "app": 210 return 211 self._record_testmodule_info(args.build_target_name, 212 _testsuite_name, 213 _testcases_dir) 214 self._record_testpart_info(args.build_target_name, 215 _testsuite_name, 216 _testcases_dir,args.subsystem_name,args.part_name) 217 if _test_xml and os.path.exists(_test_xml): 218 self._copy_file(_test_xml, _config_file) 219 elif _test_xml.replace(".xml", ".json") and \ 220 os.path.exists(_test_xml.replace(".xml", ".json")): 221 self._generate_json_by_template(_test_xml.replace(".xml", ".json"), 222 _testsuite_name, _json_file) 223 else: 224 self._generate_xml_by_template(_test_xml, _testsuite_name, 225 _config_file) 226 _resource_srcroot = _get_resource_rootpath(args.project_path) 227 self._archive_test_file_to_testcase(_testcases_dir) 228 229 @staticmethod 230 def _record_testmodule_info(build_target_name, module_name, testcases_dir): 231 if not build_target_name or not module_name: 232 raise ValueError( 233 'Ethire build_target_name or module_name is invalid') 234 235 module_info_list_file = os.path.join(testcases_dir, 'module_info.list') 236 lines = [] 237 if os.path.isfile(module_info_list_file): 238 with open(module_info_list_file, 'r') as file_read: 239 for line in file_read: 240 lines.append(line.strip()) 241 242 new_lines = ['%s %s' % (build_target_name, module_name)] 243 for line in lines: 244 arr = line.strip().split(' ') 245 if len(arr) == 0 or arr[0] == build_target_name: 246 continue 247 new_lines.append(line) 248 249 # add module info 250 new_lines.sort() 251 with open(module_info_list_file, 'w') as file_write: 252 file_write.write('\n'.join(new_lines) + '\n') 253 254 @staticmethod 255 def _record_testpart_info(build_target_name, module_name, testcases_dir, subsystem_name, part_name): 256 if not build_target_name or not module_name: 257 raise ValueError( 258 'Ethire build_target_name or module_name is invalid') 259 260 module_info_file = os.path.join(testcases_dir, module_name+'.moduleInfo') 261 262 if os.path.exists(module_info_file): 263 return 264 module_info_data = {'subsystem': subsystem_name, 'part': part_name, 265 'module': module_name} 266 with open(module_info_file, 'w') as out_file: 267 json.dump(module_info_data, out_file) 268 269 @staticmethod 270 def _generate_json_by_template(source_file, module_name, dest_file): 271 source_content = utils.read_file(source_file) 272 values = {"module": module_name} 273 xml_content = Template(source_content).substitute(values) 274 utils.write_file(dest_file, xml_content, False) 275 276 @staticmethod 277 def _generate_xml_by_template(test_xml, module_name, 278 config_file): 279 # find the template file 280 index = test_xml.rfind(".xml") 281 tmpl_file = test_xml[:index] + ".tmpl" 282 if not os.path.exists(tmpl_file): 283 raise Exception("Can't find the Test.xml or " 284 "Test.tmpl in the path %s " % 285 os.path.dirname(test_xml)) 286 tmpl_content = utils.read_file(tmpl_file) 287 values = {"module": module_name} 288 xml_content = Template(tmpl_content).substitute(values) 289 utils.write_file(config_file, xml_content, False) 290 291 def _copy_file(self, source, target): 292 if not os.path.exists(os.path.dirname(target)): 293 os.makedirs(os.path.dirname(target)) 294 if not os.path.exists(target) or ( 295 os.path.exists(target) and 296 (os.stat(target).st_mtime != os.stat(source).st_mtime)): 297 print('Trying to copy "%s" to "%s"' % (source, target)) 298 subprocess.call(["rm", "-rf", target]) 299 subprocess.call(["cp", "-rf", source, target]) 300 301 def _copy_file_to_target(self, source_file, dest_file): 302 if not os.path.exists(source_file): 303 print("[ERROR] source_file is not exist. %s" % source_file, 304 file=sys.stderr) 305 return 306 else: 307 self._copy_file(source_file, dest_file) 308 309 def _archive_test_resource(self, xml_file, root_dir, resource_dir): 310 _pattern = re.compile("[-=]>") 311 if os.path.exists(xml_file): 312 try: 313 dom = xml.dom.minidom.parse(xml_file) 314 childroot = dom.getElementsByTagName("target_preparer") 315 for child in childroot: 316 for child_atrr in child.getElementsByTagName("option"): 317 name = child_atrr.getAttribute("name") 318 attr_value = child_atrr.getAttribute("value") 319 value = _pattern.split(attr_value)[0].strip() 320 if name != "" or value != "": 321 if name == "push" and \ 322 value.startswith("resource/"): 323 self._copy_file_to_target( 324 os.path.join(root_dir, "../", value), 325 os.path.join(resource_dir, value)) 326 else: 327 raise Exception("Test.xml node name and " 328 "value not is null") 329 except IOError as error: 330 print("IO Error: %s" % error, file=sys.stderr) 331 except ExpatError as error: 332 print("ExpatError Error: %s" % error, file=sys.stderr) 333 finally: 334 pass 335 else: 336 with open(xml_file.replace(".config", ".json"), 337 "r", encoding="UTF-8") as json_file: 338 json_str = json.load(json_file) 339 kits = json_str["kits"] 340 for kit in kits: 341 types = kit["type"] 342 if types == "PushKit": 343 push_resources = kit["push"] 344 for resource in push_resources: 345 value = _pattern.split(resource)[0].strip() 346 if value.startswith("resource/"): 347 self._copy_file_to_target( 348 os.path.join(root_dir, "../", value), 349 os.path.join(resource_dir, value)) 350 351 def _archive_test_file_to_testcase(self, cases_dir): 352 if self.args.test_files is None: 353 return 354 arr_test_files = self.args.test_files.split(',') 355 for file in arr_test_files: 356 if file == "": 357 continue 358 self._copy_file_to_target(file, 359 os.path.join(cases_dir, 360 os.path.basename(file))) 361 362 def _check_file_exist(self, file_path): 363 if not os.path.exists(file_path): 364 raise Exception("File [%s] does not exist!" % file_path) 365 366 367ACTION_MAP = {"build_module": "SuiteModuleBuilder", 368 "build_xdevice": "XDeviceBuilder", 369 "archive_suite": "SuiteArchiveBuilder", 370 "build_module_with_testbundle": 371 "SuiteModuleWithTestbundleBuilder"} 372 373 374def _find_action(action, arguments): 375 class_name = ACTION_MAP[action] 376 if not class_name: 377 raise ValueError('Unsupported operation: %s' % action) 378 379 this_module = sys.modules[__name__] 380 class_def = getattr(this_module, class_name, None) 381 if not class_def: 382 raise ValueError( 383 'Unsupported operation(No Implementation Class): %s' % action) 384 class_obj = class_def(arguments) 385 func = getattr(class_obj, action, None) 386 if not func: 387 raise ValueError( 388 'Unsupported operation(No Implementation Method): %s' % action) 389 return func 390 391 392def main(arguments): 393 action = arguments[0] 394 args = arguments[1:] 395 func = _find_action(action, args) 396 func() 397 return 0 398 399 400if __name__ == '__main__': 401 sys.exit(main(sys.argv[1:])) 402