1#!/usr/bin/env python3 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""common_util 18 19This module has a collection of functions that provide helper functions to 20other modules. 21""" 22 23import fnmatch 24import inspect 25import json 26import logging 27import os 28import re 29import sys 30import time 31import xml.dom.minidom 32import zipfile 33 34from functools import partial 35from functools import wraps 36from xml.etree import ElementTree 37 38from aidegen import constant 39from aidegen.lib import errors 40from atest import atest_utils 41from atest import constants 42from atest import module_info 43 44 45COLORED_INFO = partial(atest_utils.colorize, color=constants.MAGENTA) 46COLORED_PASS = partial(atest_utils.colorize, color=constants.GREEN) 47COLORED_FAIL = partial(atest_utils.colorize, color=constants.RED) 48FAKE_MODULE_ERROR = '{} is a fake module.' 49OUTSIDE_ROOT_ERROR = '{} is outside android root.' 50PATH_NOT_EXISTS_ERROR = 'The path {} doesn\'t exist.' 51NO_MODULE_DEFINED_ERROR = 'No modules defined at {}.' 52_REBUILD_MODULE_INFO = '%s We should rebuild module-info.json file for it.' 53_ENVSETUP_NOT_RUN = ('Please run "source build/envsetup.sh" and "lunch" before ' 54 'running aidegen.') 55_LOG_FORMAT = '%(asctime)s %(filename)s:%(lineno)s:%(levelname)s: %(message)s' 56_DATE_FORMAT = '%Y-%m-%d %H:%M:%S' 57_ARG_IS_NULL_ERROR = "{0}.{1}: argument '{2}' is null." 58_ARG_TYPE_INCORRECT_ERROR = "{0}.{1}: argument '{2}': type is {3}, must be {4}." 59_IDE_UNDEFINED = constant.IDE_DICT[constant.IDE_UNDEFINED] 60_IDE_INTELLIJ = constant.IDE_DICT[constant.IDE_INTELLIJ] 61_IDE_CLION = constant.IDE_DICT[constant.IDE_CLION] 62_IDE_VSCODE = constant.IDE_DICT[constant.IDE_VSCODE] 63 64 65def time_logged(func=None, *, message='', maximum=1): 66 """Decorate a function to find out how much time it spends. 67 68 Args: 69 func: a function is to be calculated its spending time. 70 message: the message the decorated function wants to show. 71 maximum: an integer, minutes. If time exceeds the maximum time show 72 message, otherwise doesn't. 73 74 Returns: 75 The wrapper function. 76 """ 77 if func is None: 78 return partial(time_logged, message=message, maximum=maximum) 79 80 @wraps(func) 81 def wrapper(*args, **kwargs): 82 """A wrapper function.""" 83 84 start = time.time() 85 try: 86 return func(*args, **kwargs) 87 finally: 88 timestamp = time.time() - start 89 logging.debug('{}.{} takes: {:.2f}s'.format( 90 func.__module__, func.__name__, timestamp)) 91 if message and timestamp > maximum * 60: 92 print(message) 93 94 return wrapper 95 96 97def get_related_paths(atest_module_info, target=None): 98 """Get the relative and absolute paths of target from module-info. 99 100 Args: 101 atest_module_info: A ModuleInfo instance. 102 target: A string user input from command line. It could be several cases 103 such as: 104 1. Module name, e.g. Settings 105 2. Module path, e.g. packages/apps/Settings 106 3. Relative path, e.g. ../../packages/apps/Settings 107 4. Current directory, e.g. '.' or no argument 108 5. An empty string, which added by AIDEGen, used for generating 109 the iml files for the whole Android repo tree. 110 e.g. 111 1. ~/aosp$ aidegen 112 2. ~/aosp/frameworks/base$ aidegen -a 113 6. An absolute path, e.g. /usr/local/home/test/aosp 114 115 Return: 116 rel_path: The relative path of a module, return None if no matching 117 module found. 118 abs_path: The absolute path of a module, return None if no matching 119 module found. 120 """ 121 rel_path = None 122 abs_path = None 123 # take the command 'aidegen .' as 'aidegen'. 124 if target == '.': 125 target = None 126 if target: 127 # For the case of whole Android repo tree. 128 if target == constant.WHOLE_ANDROID_TREE_TARGET: 129 rel_path = '' 130 abs_path = get_android_root_dir() 131 # User inputs an absolute path. 132 elif os.path.isabs(target): 133 abs_path = target 134 rel_path = os.path.relpath(abs_path, get_android_root_dir()) 135 # User inputs a module name. 136 elif atest_module_info.is_module(target): 137 paths = atest_module_info.get_paths(target) 138 if paths: 139 rel_path = paths[0].strip(os.sep) 140 abs_path = os.path.join(get_android_root_dir(), rel_path) 141 # User inputs a module path or a relative path of android root folder. 142 elif (atest_module_info.get_module_names(target) 143 or os.path.isdir(os.path.join(get_android_root_dir(), target))): 144 rel_path = target.strip(os.sep) 145 abs_path = os.path.join(get_android_root_dir(), rel_path) 146 # User inputs a relative path of current directory. 147 else: 148 abs_path = os.path.abspath(os.path.join(os.getcwd(), target)) 149 rel_path = os.path.relpath(abs_path, get_android_root_dir()) 150 else: 151 # User doesn't input. 152 abs_path = os.getcwd() 153 if is_android_root(abs_path): 154 rel_path = '' 155 else: 156 rel_path = os.path.relpath(abs_path, get_android_root_dir()) 157 return rel_path, abs_path 158 159 160def is_target_android_root(atest_module_info, targets): 161 """Check if any target is the Android root path. 162 163 Args: 164 atest_module_info: A ModuleInfo instance contains data of 165 module-info.json. 166 targets: A list of target modules or project paths from user input. 167 168 Returns: 169 True if target is Android root, otherwise False. 170 """ 171 for target in targets: 172 _, abs_path = get_related_paths(atest_module_info, target) 173 if is_android_root(abs_path): 174 return True 175 return False 176 177 178def is_android_root(abs_path): 179 """Check if an absolute path is the Android root path. 180 181 Args: 182 abs_path: The absolute path of a module. 183 184 Returns: 185 True if abs_path is Android root, otherwise False. 186 """ 187 return abs_path == get_android_root_dir() 188 189 190def has_build_target(atest_module_info, rel_path): 191 """Determine if a relative path contains buildable module. 192 193 Args: 194 atest_module_info: A ModuleInfo instance contains data of 195 module-info.json. 196 rel_path: The module path relative to android root. 197 198 Returns: 199 True if the relative path contains a build target, otherwise false. 200 """ 201 return any( 202 is_source_under_relative_path(mod_path, rel_path) 203 for mod_path in atest_module_info.path_to_module_info) 204 205 206def _check_modules(atest_module_info, targets, raise_on_lost_module=True): 207 """Check if all targets are valid build targets. 208 209 Args: 210 atest_module_info: A ModuleInfo instance contains data of 211 module-info.json. 212 targets: A list of target modules or project paths from user input. 213 When locating the path of the target, given a matched module 214 name has priority over path. Below is the priority of locating a 215 target: 216 1. Module name, e.g. Settings 217 2. Module path, e.g. packages/apps/Settings 218 3. Relative path, e.g. ../../packages/apps/Settings 219 4. Current directory, e.g. '.' or no argument 220 raise_on_lost_module: A boolean, pass to _check_module to determine if 221 ProjectPathNotExistError or NoModuleDefinedInModuleInfoError 222 should be raised. 223 224 Returns: 225 True if any _check_module return flip the True/False. 226 """ 227 for target in targets: 228 if not check_module(atest_module_info, target, raise_on_lost_module): 229 return False 230 return True 231 232 233def check_module(atest_module_info, target, raise_on_lost_module=True): 234 """Check if a target is valid or it's a path containing build target. 235 236 Args: 237 atest_module_info: A ModuleInfo instance contains the data of 238 module-info.json. 239 target: A target module or project path from user input. 240 When locating the path of the target, given a matched module 241 name has priority over path. Below is the priority of locating a 242 target: 243 1. Module name, e.g. Settings 244 2. Module path, e.g. packages/apps/Settings 245 3. Relative path, e.g. ../../packages/apps/Settings 246 4. Current directory, e.g. . or no argument 247 5. An absolute path, e.g. /usr/local/home/test/aosp 248 raise_on_lost_module: A boolean, handles if ProjectPathNotExistError or 249 NoModuleDefinedInModuleInfoError should be raised. 250 251 Returns: 252 1. If there is no error _check_module always return True. 253 2. If there is an error, 254 a. When raise_on_lost_module is False, _check_module will raise the 255 error. 256 b. When raise_on_lost_module is True, _check_module will return 257 False if module's error is ProjectPathNotExistError or 258 NoModuleDefinedInModuleInfoError else raise the error. 259 260 Raises: 261 Raise ProjectPathNotExistError and NoModuleDefinedInModuleInfoError only 262 when raise_on_lost_module is True, others don't subject to the limit. 263 The rules for raising exceptions: 264 1. Absolute path of a module is None -> FakeModuleError 265 2. Module doesn't exist in repo root -> ProjectOutsideAndroidRootError 266 3. The given absolute path is not a dir -> ProjectPathNotExistError 267 4. If the given abs path doesn't contain any target and not repo root 268 -> NoModuleDefinedInModuleInfoError 269 """ 270 rel_path, abs_path = get_related_paths(atest_module_info, target) 271 if not abs_path: 272 err = FAKE_MODULE_ERROR.format(target) 273 logging.error(err) 274 raise errors.FakeModuleError(err) 275 if not is_source_under_relative_path(abs_path, get_android_root_dir()): 276 err = OUTSIDE_ROOT_ERROR.format(abs_path) 277 logging.error(err) 278 raise errors.ProjectOutsideAndroidRootError(err) 279 if not os.path.isdir(abs_path): 280 err = PATH_NOT_EXISTS_ERROR.format(rel_path) 281 if raise_on_lost_module: 282 logging.error(err) 283 raise errors.ProjectPathNotExistError(err) 284 logging.debug(_REBUILD_MODULE_INFO, err) 285 return False 286 if (not has_build_target(atest_module_info, rel_path) 287 and not is_android_root(abs_path)): 288 err = NO_MODULE_DEFINED_ERROR.format(rel_path) 289 if raise_on_lost_module: 290 logging.error(err) 291 raise errors.NoModuleDefinedInModuleInfoError(err) 292 logging.debug(_REBUILD_MODULE_INFO, err) 293 return False 294 return True 295 296 297def get_abs_path(rel_path): 298 """Get absolute path from a relative path. 299 300 Args: 301 rel_path: A string, a relative path to get_android_root_dir(). 302 303 Returns: 304 abs_path: A string, an absolute path starts with 305 get_android_root_dir(). 306 """ 307 if not rel_path: 308 return get_android_root_dir() 309 if is_source_under_relative_path(rel_path, get_android_root_dir()): 310 return rel_path 311 return os.path.join(get_android_root_dir(), rel_path) 312 313 314def is_target(src_file, src_file_extensions): 315 """Check if src_file is a type of src_file_extensions. 316 317 Args: 318 src_file: A string of the file path to be checked. 319 src_file_extensions: A list of file types to be checked 320 321 Returns: 322 True if src_file is one of the types of src_file_extensions, otherwise 323 False. 324 """ 325 return any(src_file.endswith(x) for x in src_file_extensions) 326 327 328def get_atest_module_info(targets=None): 329 """Get the right version of atest ModuleInfo instance. 330 331 The rules: 332 1. If targets is None: 333 We just want to get an atest ModuleInfo instance. 334 2. If targets isn't None: 335 Check if the targets don't exist in ModuleInfo, we'll regain a new 336 atest ModuleInfo instance by setting force_build=True and call 337 _check_modules again. If targets still don't exist, raise exceptions. 338 339 Args: 340 targets: A list of targets to be built. 341 342 Returns: 343 An atest ModuleInfo instance. 344 """ 345 amodule_info = module_info.ModuleInfo() 346 if targets and not _check_modules( 347 amodule_info, targets, raise_on_lost_module=False): 348 amodule_info = module_info.ModuleInfo(force_build=True) 349 _check_modules(amodule_info, targets) 350 return amodule_info 351 352 353def get_blueprint_json_path(file_name): 354 """Assemble the path of blueprint json file. 355 356 Args: 357 file_name: A string of blueprint json file name. 358 359 Returns: 360 The path of json file. 361 """ 362 return os.path.join(get_soong_out_path(), file_name) 363 364 365def back_to_cwd(func): 366 """Decorate a function change directory back to its original one. 367 368 Args: 369 func: a function is to be changed back to its original directory. 370 371 Returns: 372 The wrapper function. 373 """ 374 375 @wraps(func) 376 def wrapper(*args, **kwargs): 377 """A wrapper function.""" 378 cwd = os.getcwd() 379 try: 380 return func(*args, **kwargs) 381 finally: 382 os.chdir(cwd) 383 384 return wrapper 385 386 387def get_android_out_dir(): 388 """Get out directory in different conditions. 389 390 Returns: 391 Android out directory path. 392 """ 393 android_root_path = get_android_root_dir() 394 android_out_dir = os.getenv(constants.ANDROID_OUT_DIR) 395 out_dir_common_base = os.getenv(constant.OUT_DIR_COMMON_BASE_ENV_VAR) 396 android_out_dir_common_base = (os.path.join( 397 out_dir_common_base, os.path.basename(android_root_path)) 398 if out_dir_common_base else None) 399 return (android_out_dir or android_out_dir_common_base 400 or constant.ANDROID_DEFAULT_OUT) 401 402 403def get_android_root_dir(): 404 """Get Android root directory. 405 406 If the path doesn't exist show message to ask users to run envsetup.sh and 407 lunch first. 408 409 Returns: 410 Android root directory path. 411 """ 412 android_root_path = os.environ.get(constants.ANDROID_BUILD_TOP) 413 if not android_root_path: 414 _show_env_setup_msg_and_exit() 415 return android_root_path 416 417 418def get_aidegen_root_dir(): 419 """Get AIDEGen root directory. 420 421 Returns: 422 AIDEGen root directory path. 423 """ 424 return os.path.join(get_android_root_dir(), constant.AIDEGEN_ROOT_PATH) 425 426 427def _show_env_setup_msg_and_exit(): 428 """Show message if users didn't run envsetup.sh and lunch.""" 429 print(_ENVSETUP_NOT_RUN) 430 sys.exit(0) 431 432 433def get_soong_out_path(): 434 """Assemble out directory's soong path. 435 436 Returns: 437 Out directory's soong path. 438 """ 439 return os.path.join(get_android_root_dir(), get_android_out_dir(), 'soong') 440 441 442def configure_logging(verbose): 443 """Configure the logger. 444 445 Args: 446 verbose: A boolean. If true, display DEBUG level logs. 447 """ 448 log_format = _LOG_FORMAT 449 datefmt = _DATE_FORMAT 450 level = logging.DEBUG if verbose else logging.INFO 451 # Clear all handlers to prevent setting level not working. 452 logging.getLogger().handlers = [] 453 logging.basicConfig(level=level, format=log_format, datefmt=datefmt) 454 455 456def exist_android_bp(abs_path): 457 """Check if the Android.bp exists under specific folder. 458 459 Args: 460 abs_path: An absolute path string. 461 462 Returns: A boolean, true if the Android.bp exists under the folder, 463 otherwise false. 464 """ 465 return os.path.isfile(os.path.join(abs_path, constant.ANDROID_BP)) 466 467 468def exist_android_mk(abs_path): 469 """Check if the Android.mk exists under specific folder. 470 471 Args: 472 abs_path: An absolute path string. 473 474 Returns: A boolean, true if the Android.mk exists under the folder, 475 otherwise false. 476 """ 477 return os.path.isfile(os.path.join(abs_path, constant.ANDROID_MK)) 478 479 480def is_source_under_relative_path(source, relative_path): 481 """Check if a source file is a project relative path file. 482 483 Args: 484 source: Android source file path. 485 relative_path: Relative path of the module. 486 487 Returns: 488 True if source file is a project relative path file, otherwise False. 489 """ 490 return re.search( 491 constant.RE_INSIDE_PATH_CHECK.format(relative_path), source) 492 493 494def remove_user_home_path(data): 495 """Replace the user home path string with a constant string. 496 497 Args: 498 data: A string of xml content or an attributeError of error message. 499 500 Returns: 501 A string which replaced the user home path to $USER_HOME$. 502 """ 503 return str(data).replace(os.path.expanduser('~'), constant.USER_HOME) 504 505 506def io_error_handle(func): 507 """Decorates a function of handling io error and raising exception. 508 509 Args: 510 func: A function is to be raised exception if writing file failed. 511 512 Returns: 513 The wrapper function. 514 """ 515 516 @wraps(func) 517 def wrapper(*args, **kwargs): 518 """A wrapper function.""" 519 try: 520 return func(*args, **kwargs) 521 except (OSError, IOError) as err: 522 print('{0}.{1} I/O error: {2}'.format( 523 func.__module__, func.__name__, err)) 524 raise 525 return wrapper 526 527 528def check_args(**decls): 529 """Decorates a function to check its argument types. 530 531 Usage: 532 @check_args(name=str, text=str) 533 def parse_rule(name, text): 534 ... 535 536 Args: 537 decls: A dictionary with keys as arguments' names and values as 538 arguments' types. 539 540 Returns: 541 The wrapper function. 542 """ 543 544 def decorator(func): 545 """A wrapper function.""" 546 fmodule = func.__module__ 547 fname = func.__name__ 548 fparams = inspect.signature(func).parameters 549 550 @wraps(func) 551 def decorated(*args, **kwargs): 552 """A wrapper function.""" 553 params = dict(zip(fparams, args)) 554 for arg_name, arg_type in decls.items(): 555 try: 556 arg_val = params[arg_name] 557 except KeyError: 558 # If arg_name can't be found in function's signature, it 559 # might be a case of a partial function or default 560 # parameters, we'll neglect it. 561 if arg_name not in kwargs: 562 continue 563 arg_val = kwargs.get(arg_name) 564 if arg_val is None: 565 raise TypeError(_ARG_IS_NULL_ERROR.format( 566 fmodule, fname, arg_name)) 567 if not isinstance(arg_val, arg_type): 568 raise TypeError(_ARG_TYPE_INCORRECT_ERROR.format( 569 fmodule, fname, arg_name, type(arg_val), arg_type)) 570 return func(*args, **kwargs) 571 return decorated 572 573 return decorator 574 575 576@io_error_handle 577def dump_json_dict(json_path, data): 578 """Dumps a dictionary of data into a json file. 579 580 Args: 581 json_path: An absolute json file path string. 582 data: A dictionary of data to be written into a json file. 583 """ 584 with open(json_path, 'w', encoding='utf-8') as json_file: 585 json.dump(data, json_file, indent=4) 586 587 588@io_error_handle 589def get_json_dict(json_path): 590 """Loads a json file from path and convert it into a json dictionary. 591 592 Args: 593 json_path: An absolute json file path string. 594 595 Returns: 596 A dictionary loaded from the json_path. 597 """ 598 with open(json_path, 'r', encoding='utf-8') as jfile: 599 return json.load(jfile) 600 601 602@io_error_handle 603def read_file_line_to_list(file_path): 604 """Read a file line by line and write them into a list. 605 606 Args: 607 file_path: A string of a file's path. 608 609 Returns: 610 A list of the file's content by line. 611 """ 612 files = [] 613 with open(file_path, 'r', encoding='utf8') as infile: 614 for line in infile: 615 files.append(line.strip()) 616 return files 617 618 619@io_error_handle 620def read_file_content(path, encode_type='utf8'): 621 """Read file's content. 622 623 Args: 624 path: Path of input file. 625 encode_type: A string of encoding name, default to UTF-8. 626 627 Returns: 628 String: Content of the file. 629 """ 630 with open(path, 'r', encoding=encode_type) as template: 631 return template.read() 632 633 634@io_error_handle 635def file_generate(path, content): 636 """Generate file from content. 637 638 Args: 639 path: Path of target file. 640 content: String content of file. 641 """ 642 if not os.path.exists(os.path.dirname(path)): 643 os.makedirs(os.path.dirname(path)) 644 with open(path, 'w', encoding='utf-8') as target: 645 target.write(content) 646 647 648def get_lunch_target(): 649 """Gets the Android lunch target in current console. 650 651 Returns: 652 A json format string of lunch target in current console. 653 """ 654 product = os.environ.get(constant.TARGET_PRODUCT) 655 build_variant = os.environ.get(constant.TARGET_BUILD_VARIANT) 656 if product and build_variant: 657 return json.dumps( 658 {constant.LUNCH_TARGET: "-".join([product, build_variant])}) 659 return None 660 661 662def get_blueprint_json_files_relative_dict(): 663 """Gets a dictionary with key: environment variable, value: absolute path. 664 665 Returns: 666 A dictionary with key: environment variable and value: absolute path of 667 the file generated by the environment variable. 668 """ 669 data = {} 670 root_dir = get_android_root_dir() 671 bp_java_path = os.path.join( 672 root_dir, get_blueprint_json_path( 673 constant.BLUEPRINT_JAVA_JSONFILE_NAME)) 674 data[constant.GEN_JAVA_DEPS] = bp_java_path 675 bp_cc_path = os.path.join( 676 root_dir, get_blueprint_json_path(constant.BLUEPRINT_CC_JSONFILE_NAME)) 677 data[constant.GEN_CC_DEPS] = bp_cc_path 678 data[constant.GEN_COMPDB] = os.path.join(get_soong_out_path(), 679 constant.RELATIVE_COMPDB_PATH, 680 constant.COMPDB_JSONFILE_NAME) 681 data[constant.GEN_RUST] = os.path.join( 682 root_dir, get_blueprint_json_path(constant.RUST_PROJECT_JSON)) 683 return data 684 685 686def to_pretty_xml(root, indent=" "): 687 """Gets pretty xml from an xml.etree.ElementTree root. 688 689 Args: 690 root: An element tree root. 691 indent: The indent of XML. 692 Returns: 693 A string of pretty xml. 694 """ 695 xml_string = xml.dom.minidom.parseString( 696 ElementTree.tostring(root)).toprettyxml(indent) 697 # Remove the xml declaration since IntelliJ doesn't use it. 698 xml_string = xml_string.split("\n", 1)[1] 699 # Remove the weird newline issue from toprettyxml. 700 return os.linesep.join([s for s in xml_string.splitlines() if s.strip()]) 701 702 703def to_boolean(str_bool): 704 """Converts a string to a boolean. 705 706 Args: 707 str_bool: A string in the expression of boolean type. 708 709 Returns: 710 A boolean True if the string is one of ('True', 'true', 'T', 't', '1') 711 else False. 712 """ 713 return str_bool and str_bool.lower() in ('true', 't', '1') 714 715 716def find_git_root(relpath): 717 """Finds the parent directory which has a .git folder from the relpath. 718 719 Args: 720 relpath: A string of relative path. 721 722 Returns: 723 A string of the absolute path which contains a .git, otherwise, none. 724 """ 725 dir_list = relpath.split(os.sep) 726 for i in range(len(dir_list), 0, -1): 727 real_path = os.path.join(get_android_root_dir(), 728 os.sep.join(dir_list[:i]), 729 constant.GIT_FOLDER_NAME) 730 if os.path.exists(real_path): 731 return os.path.dirname(real_path) 732 logging.warning('%s can\'t find its .git folder.', relpath) 733 return None 734 735 736def determine_language_ide(lang, ide, jlist=None, clist=None, rlist=None): 737 """Determines the language and IDE by the input language and IDE arguments. 738 739 If IDE and language are undefined, the priority of the language is: 740 1. Java 741 2. C/C++ 742 3. Rust 743 744 Args: 745 lang: A character represents the input language. 746 ide: A character represents the input IDE. 747 jlist: A list of Android Java projects, the default value is None. 748 clist: A list of Android C/C++ projects, the default value is None. 749 rlist: A list of Android Rust projects, the default value is None. 750 751 Returns: 752 A tuple of the determined language and IDE name strings. 753 """ 754 if ide == _IDE_UNDEFINED and lang == constant.LANG_UNDEFINED: 755 if jlist: 756 lang = constant.LANG_JAVA 757 elif clist: 758 lang = constant.LANG_CC 759 elif rlist: 760 lang = constant.LANG_RUST 761 if lang in (constant.LANG_UNDEFINED, constant.LANG_JAVA): 762 if ide == _IDE_UNDEFINED: 763 ide = _IDE_INTELLIJ 764 lang = constant.LANG_JAVA 765 if constant.IDE_NAME_DICT[ide] == constant.IDE_CLION: 766 lang = constant.LANG_CC 767 elif lang == constant.LANG_CC: 768 if ide == _IDE_UNDEFINED: 769 ide = _IDE_CLION 770 if constant.IDE_NAME_DICT[ide] == constant.IDE_INTELLIJ: 771 lang = constant.LANG_JAVA 772 elif lang == constant.LANG_RUST: 773 ide = _IDE_VSCODE 774 return constant.LANGUAGE_NAME_DICT[lang], constant.IDE_NAME_DICT[ide] 775 776 777def check_java_or_kotlin_file_exists(abs_path): 778 """Checks if any Java or Kotlin files exist in an abs_path directory. 779 780 Args: 781 abs_path: A string of absolute path of a directory to be checked. 782 783 Returns: 784 True if any Java or Kotlin files exist otherwise False. 785 """ 786 for _, _, filenames in os.walk(abs_path): 787 for extension in (constant.JAVA_FILES, constant.KOTLIN_FILES): 788 if fnmatch.filter(filenames, extension): 789 return True 790 return False 791 792 793@io_error_handle 794def unzip_file(src, dest): 795 """Unzips the source zip file and extract it to the destination directory. 796 797 Args: 798 src: A string of the file to be unzipped. 799 dest: A string of the destination directory to be extracted to. 800 """ 801 with zipfile.ZipFile(src, 'r') as zip_ref: 802 zip_ref.extractall(dest) 803