1*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2017, The Android Open Source Project 2*c2e18aaaSAndroid Build Coastguard Worker# 3*c2e18aaaSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*c2e18aaaSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*c2e18aaaSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*c2e18aaaSAndroid Build Coastguard Worker# 7*c2e18aaaSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*c2e18aaaSAndroid Build Coastguard Worker# 9*c2e18aaaSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*c2e18aaaSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*c2e18aaaSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*c2e18aaaSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*c2e18aaaSAndroid Build Coastguard Worker# limitations under the License. 14*c2e18aaaSAndroid Build Coastguard Worker 15*c2e18aaaSAndroid Build Coastguard Worker"""Utility functions for atest.""" 16*c2e18aaaSAndroid Build Coastguard Worker 17*c2e18aaaSAndroid Build Coastguard Worker 18*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=import-outside-toplevel 19*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=too-many-lines 20*c2e18aaaSAndroid Build Coastguard Worker 21*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import print_function 22*c2e18aaaSAndroid Build Coastguard Worker 23*c2e18aaaSAndroid Build Coastguard Workerfrom collections import deque 24*c2e18aaaSAndroid Build Coastguard Workerfrom dataclasses import dataclass 25*c2e18aaaSAndroid Build Coastguard Workerimport datetime 26*c2e18aaaSAndroid Build Coastguard Workerimport enum 27*c2e18aaaSAndroid Build Coastguard Workerimport fnmatch 28*c2e18aaaSAndroid Build Coastguard Workerimport hashlib 29*c2e18aaaSAndroid Build Coastguard Workerimport html 30*c2e18aaaSAndroid Build Coastguard Workerimport importlib.resources 31*c2e18aaaSAndroid Build Coastguard Workerimport importlib.util 32*c2e18aaaSAndroid Build Coastguard Workerimport io 33*c2e18aaaSAndroid Build Coastguard Workerimport itertools 34*c2e18aaaSAndroid Build Coastguard Workerimport json 35*c2e18aaaSAndroid Build Coastguard Workerimport logging 36*c2e18aaaSAndroid Build Coastguard Workerfrom multiprocessing import Process 37*c2e18aaaSAndroid Build Coastguard Workerimport os 38*c2e18aaaSAndroid Build Coastguard Workerfrom pathlib import Path 39*c2e18aaaSAndroid Build Coastguard Workerimport pickle 40*c2e18aaaSAndroid Build Coastguard Workerimport platform 41*c2e18aaaSAndroid Build Coastguard Workerimport re 42*c2e18aaaSAndroid Build Coastguard Workerimport shutil 43*c2e18aaaSAndroid Build Coastguard Workerimport subprocess 44*c2e18aaaSAndroid Build Coastguard Workerimport sys 45*c2e18aaaSAndroid Build Coastguard Workerimport threading 46*c2e18aaaSAndroid Build Coastguard Workerfrom threading import Thread 47*c2e18aaaSAndroid Build Coastguard Workerimport traceback 48*c2e18aaaSAndroid Build Coastguard Workerfrom typing import Any, Dict, IO, List, Set, Tuple 49*c2e18aaaSAndroid Build Coastguard Workerimport urllib 50*c2e18aaaSAndroid Build Coastguard Workerimport xml.etree.ElementTree as ET 51*c2e18aaaSAndroid Build Coastguard Workerimport zipfile 52*c2e18aaaSAndroid Build Coastguard Worker 53*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_decorator 54*c2e18aaaSAndroid Build Coastguard Workerfrom atest import constants 55*c2e18aaaSAndroid Build Coastguard Workerfrom atest.atest_enum import DetectType, ExitCode, FilterType 56*c2e18aaaSAndroid Build Coastguard Workerfrom atest.metrics import metrics 57*c2e18aaaSAndroid Build Coastguard Workerfrom atest.metrics import metrics_utils 58*c2e18aaaSAndroid Build Coastguard Workerfrom atest.tf_proto import test_record_pb2 59*c2e18aaaSAndroid Build Coastguard Worker 60*c2e18aaaSAndroid Build Coastguard WorkerDEFAULT_OUTPUT_ROLLING_LINES = 6 61*c2e18aaaSAndroid Build Coastguard Worker_BASH_CLEAR_PREVIOUS_LINE_CODE = '\033[F\033[K' 62*c2e18aaaSAndroid Build Coastguard Worker_BASH_RESET_CODE = '\033[0m' 63*c2e18aaaSAndroid Build Coastguard WorkerDIST_OUT_DIR = Path( 64*c2e18aaaSAndroid Build Coastguard Worker os.environ.get(constants.ANDROID_BUILD_TOP, os.getcwd()) + '/out/dist/' 65*c2e18aaaSAndroid Build Coastguard Worker) 66*c2e18aaaSAndroid Build Coastguard WorkerMAINLINE_MODULES_EXT_RE = re.compile(r'\.(apex|apks|apk)$') 67*c2e18aaaSAndroid Build Coastguard WorkerTEST_WITH_MAINLINE_MODULES_RE = re.compile( 68*c2e18aaaSAndroid Build Coastguard Worker r'(?P<test>.*)\[(?P<mainline_modules>.*' r'[.](apk|apks|apex))\]$' 69*c2e18aaaSAndroid Build Coastguard Worker) 70*c2e18aaaSAndroid Build Coastguard Worker 71*c2e18aaaSAndroid Build Coastguard Worker# Arbitrary number to limit stdout for failed runs in run_limited_output. 72*c2e18aaaSAndroid Build Coastguard Worker# Reason for its use is that the make command itself has its own carriage 73*c2e18aaaSAndroid Build Coastguard Worker# return output mechanism that when collected line by line causes the streaming 74*c2e18aaaSAndroid Build Coastguard Worker# full_output list to be extremely large. 75*c2e18aaaSAndroid Build Coastguard Worker_FAILED_OUTPUT_LINE_LIMIT = 100 76*c2e18aaaSAndroid Build Coastguard Worker# Regular expression to match the start of a ninja compile: 77*c2e18aaaSAndroid Build Coastguard Worker# ex: [ 99% 39710/39711] 78*c2e18aaaSAndroid Build Coastguard Worker_BUILD_COMPILE_STATUS = re.compile(r'\[\s*(\d{1,3}%\s+)?\d+/\d+\]') 79*c2e18aaaSAndroid Build Coastguard Worker_BUILD_FAILURE = 'FAILED: ' 80*c2e18aaaSAndroid Build Coastguard WorkerBUILD_TOP_HASH = hashlib.md5( 81*c2e18aaaSAndroid Build Coastguard Worker os.environ.get(constants.ANDROID_BUILD_TOP, '').encode() 82*c2e18aaaSAndroid Build Coastguard Worker).hexdigest() 83*c2e18aaaSAndroid Build Coastguard Worker_DEFAULT_TERMINAL_WIDTH = 80 84*c2e18aaaSAndroid Build Coastguard Worker_DEFAULT_TERMINAL_HEIGHT = 25 85*c2e18aaaSAndroid Build Coastguard Worker_BUILD_CMD = 'build/soong/soong_ui.bash' 86*c2e18aaaSAndroid Build Coastguard Worker_FIND_MODIFIED_FILES_CMDS = ( 87*c2e18aaaSAndroid Build Coastguard Worker 'cd {};' 88*c2e18aaaSAndroid Build Coastguard Worker 'local_branch=$(git rev-parse --abbrev-ref HEAD);' 89*c2e18aaaSAndroid Build Coastguard Worker "remote_branch=$(git branch -r | grep '\\->' | awk '{{print $1}}');" 90*c2e18aaaSAndroid Build Coastguard Worker # Get the number of commits from local branch to remote branch. 91*c2e18aaaSAndroid Build Coastguard Worker 'ahead=$(git rev-list --left-right --count $local_branch...$remote_branch ' 92*c2e18aaaSAndroid Build Coastguard Worker "| awk '{{print $1}}');" 93*c2e18aaaSAndroid Build Coastguard Worker # Get the list of modified files from HEAD to previous $ahead generation. 94*c2e18aaaSAndroid Build Coastguard Worker 'git diff HEAD~$ahead --name-only' 95*c2e18aaaSAndroid Build Coastguard Worker) 96*c2e18aaaSAndroid Build Coastguard Worker_ANDROID_BUILD_EXT = ('.bp', '.mk') 97*c2e18aaaSAndroid Build Coastguard Worker 98*c2e18aaaSAndroid Build Coastguard Worker# Set of special chars for various purposes. 99*c2e18aaaSAndroid Build Coastguard Worker_REGEX_CHARS = {'[', '(', '{', '|', '\\', '*', '?', '+', '^'} 100*c2e18aaaSAndroid Build Coastguard Worker_WILDCARD_CHARS = {'?', '*'} 101*c2e18aaaSAndroid Build Coastguard Worker 102*c2e18aaaSAndroid Build Coastguard Worker_WILDCARD_FILTER_RE = re.compile(r'.*[?|*]$') 103*c2e18aaaSAndroid Build Coastguard Worker_REGULAR_FILTER_RE = re.compile(r'.*\w$') 104*c2e18aaaSAndroid Build Coastguard Worker 105*c2e18aaaSAndroid Build Coastguard WorkerSUGGESTIONS = { 106*c2e18aaaSAndroid Build Coastguard Worker # (b/177626045) If Atest does not install target application properly. 107*c2e18aaaSAndroid Build Coastguard Worker 'Runner reported an invalid method': 'Please reflash the device(s).', 108*c2e18aaaSAndroid Build Coastguard Worker} 109*c2e18aaaSAndroid Build Coastguard Worker 110*c2e18aaaSAndroid Build Coastguard Worker_BUILD_ENV = {} 111*c2e18aaaSAndroid Build Coastguard Worker 112*c2e18aaaSAndroid Build Coastguard WorkerCACHE_VERSION = 1 113*c2e18aaaSAndroid Build Coastguard Worker 114*c2e18aaaSAndroid Build Coastguard Worker_original_sys_stdout = sys.stdout 115*c2e18aaaSAndroid Build Coastguard Worker 116*c2e18aaaSAndroid Build Coastguard Worker 117*c2e18aaaSAndroid Build Coastguard Worker@dataclass 118*c2e18aaaSAndroid Build Coastguard Workerclass BuildEnvProfiler: 119*c2e18aaaSAndroid Build Coastguard Worker """Represents the condition before and after trigging build.""" 120*c2e18aaaSAndroid Build Coastguard Worker 121*c2e18aaaSAndroid Build Coastguard Worker ninja_file: Path 122*c2e18aaaSAndroid Build Coastguard Worker ninja_file_mtime: float 123*c2e18aaaSAndroid Build Coastguard Worker variable_file: Path 124*c2e18aaaSAndroid Build Coastguard Worker variable_file_md5: str 125*c2e18aaaSAndroid Build Coastguard Worker clean_out: bool 126*c2e18aaaSAndroid Build Coastguard Worker build_files_integrity: bool 127*c2e18aaaSAndroid Build Coastguard Worker 128*c2e18aaaSAndroid Build Coastguard Worker 129*c2e18aaaSAndroid Build Coastguard Worker@enum.unique 130*c2e18aaaSAndroid Build Coastguard Workerclass BuildOutputMode(enum.Enum): 131*c2e18aaaSAndroid Build Coastguard Worker 'Represents the different ways to display build output.' 132*c2e18aaaSAndroid Build Coastguard Worker 133*c2e18aaaSAndroid Build Coastguard Worker STREAMED = 'streamed' 134*c2e18aaaSAndroid Build Coastguard Worker LOGGED = 'logged' 135*c2e18aaaSAndroid Build Coastguard Worker 136*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, arg_name: str): 137*c2e18aaaSAndroid Build Coastguard Worker self._description = arg_name 138*c2e18aaaSAndroid Build Coastguard Worker 139*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=missing-function-docstring 140*c2e18aaaSAndroid Build Coastguard Worker def description(self): 141*c2e18aaaSAndroid Build Coastguard Worker return self._description 142*c2e18aaaSAndroid Build Coastguard Worker 143*c2e18aaaSAndroid Build Coastguard Worker 144*c2e18aaaSAndroid Build Coastguard Worker@dataclass 145*c2e18aaaSAndroid Build Coastguard Workerclass AndroidVariables: 146*c2e18aaaSAndroid Build Coastguard Worker """Class that stores the value of environment variables.""" 147*c2e18aaaSAndroid Build Coastguard Worker 148*c2e18aaaSAndroid Build Coastguard Worker build_top: str 149*c2e18aaaSAndroid Build Coastguard Worker product_out: str 150*c2e18aaaSAndroid Build Coastguard Worker target_out_cases: str 151*c2e18aaaSAndroid Build Coastguard Worker host_out: str 152*c2e18aaaSAndroid Build Coastguard Worker host_out_cases: str 153*c2e18aaaSAndroid Build Coastguard Worker target_product: str 154*c2e18aaaSAndroid Build Coastguard Worker build_variant: str 155*c2e18aaaSAndroid Build Coastguard Worker 156*c2e18aaaSAndroid Build Coastguard Worker def __init__(self): 157*c2e18aaaSAndroid Build Coastguard Worker self.build_top = os.getenv('ANDROID_BUILD_TOP') 158*c2e18aaaSAndroid Build Coastguard Worker self.product_out = os.getenv('ANDROID_PRODUCT_OUT') 159*c2e18aaaSAndroid Build Coastguard Worker self.target_out_cases = os.getenv('ANDROID_TARGET_OUT_TESTCASES') 160*c2e18aaaSAndroid Build Coastguard Worker self.host_out = os.getenv('ANDROID_HOST_OUT') 161*c2e18aaaSAndroid Build Coastguard Worker self.host_out_cases = os.getenv('ANDROID_HOST_OUT_TESTCASES') 162*c2e18aaaSAndroid Build Coastguard Worker self.target_product = os.getenv('TARGET_PRODUCT') 163*c2e18aaaSAndroid Build Coastguard Worker self.build_variant = os.getenv('TARGET_BUILD_VARIANT') 164*c2e18aaaSAndroid Build Coastguard Worker 165*c2e18aaaSAndroid Build Coastguard Worker 166*c2e18aaaSAndroid Build Coastguard Workerdef get_build_top(*joinpaths: Any) -> Path: 167*c2e18aaaSAndroid Build Coastguard Worker """Get the absolute path from the given repo path.""" 168*c2e18aaaSAndroid Build Coastguard Worker return Path(AndroidVariables().build_top, *joinpaths) 169*c2e18aaaSAndroid Build Coastguard Worker 170*c2e18aaaSAndroid Build Coastguard Worker 171*c2e18aaaSAndroid Build Coastguard Workerdef get_host_out(*joinpaths: Any) -> Path: 172*c2e18aaaSAndroid Build Coastguard Worker """Get the absolute host out path from the given path.""" 173*c2e18aaaSAndroid Build Coastguard Worker return Path(AndroidVariables().host_out, *joinpaths) 174*c2e18aaaSAndroid Build Coastguard Worker 175*c2e18aaaSAndroid Build Coastguard Worker 176*c2e18aaaSAndroid Build Coastguard Workerdef get_product_out(*joinpaths: Any) -> Path: 177*c2e18aaaSAndroid Build Coastguard Worker """Get the absolute product out path from the given path.""" 178*c2e18aaaSAndroid Build Coastguard Worker return Path(AndroidVariables().product_out, *joinpaths) 179*c2e18aaaSAndroid Build Coastguard Worker 180*c2e18aaaSAndroid Build Coastguard Worker 181*c2e18aaaSAndroid Build Coastguard Workerdef get_index_path(*filename: Any) -> Path: 182*c2e18aaaSAndroid Build Coastguard Worker """Get absolute path of the desired index file.""" 183*c2e18aaaSAndroid Build Coastguard Worker return get_host_out('indices', *filename) 184*c2e18aaaSAndroid Build Coastguard Worker 185*c2e18aaaSAndroid Build Coastguard Worker 186*c2e18aaaSAndroid Build Coastguard Workerdef getenv_abs_path(env: str, suffix: str = None) -> Path: 187*c2e18aaaSAndroid Build Coastguard Worker """Translate the environment variable to an absolute path. 188*c2e18aaaSAndroid Build Coastguard Worker 189*c2e18aaaSAndroid Build Coastguard Worker Args: 190*c2e18aaaSAndroid Build Coastguard Worker env: string of the given environment variable. 191*c2e18aaaSAndroid Build Coastguard Worker suffix: string that will be appended to. 192*c2e18aaaSAndroid Build Coastguard Worker 193*c2e18aaaSAndroid Build Coastguard Worker Returns: 194*c2e18aaaSAndroid Build Coastguard Worker Absolute Path of the given environment variable. 195*c2e18aaaSAndroid Build Coastguard Worker """ 196*c2e18aaaSAndroid Build Coastguard Worker env_value = os.getenv(env) 197*c2e18aaaSAndroid Build Coastguard Worker if not env_value: 198*c2e18aaaSAndroid Build Coastguard Worker return None 199*c2e18aaaSAndroid Build Coastguard Worker 200*c2e18aaaSAndroid Build Coastguard Worker env_path = Path(env_value) 201*c2e18aaaSAndroid Build Coastguard Worker if env_path.is_absolute(): 202*c2e18aaaSAndroid Build Coastguard Worker return env_path.joinpath(suffix) if suffix else env_path 203*c2e18aaaSAndroid Build Coastguard Worker 204*c2e18aaaSAndroid Build Coastguard Worker return get_build_top(env_path, suffix) if suffix else get_build_top(env_path) 205*c2e18aaaSAndroid Build Coastguard Worker 206*c2e18aaaSAndroid Build Coastguard Worker 207*c2e18aaaSAndroid Build Coastguard Workerdef get_build_cmd(dump=False): 208*c2e18aaaSAndroid Build Coastguard Worker """Compose build command with no-absolute path and flag "--make-mode". 209*c2e18aaaSAndroid Build Coastguard Worker 210*c2e18aaaSAndroid Build Coastguard Worker Args: 211*c2e18aaaSAndroid Build Coastguard Worker dump: boolean that determines the option of build/soong/soong_iu.bash. 212*c2e18aaaSAndroid Build Coastguard Worker True: used to dump build variables, equivalent to printconfig. e.g. 213*c2e18aaaSAndroid Build Coastguard Worker build/soong/soong_iu.bash --dumpvar-mode <VAR_NAME> 214*c2e18aaaSAndroid Build Coastguard Worker False: (default) used to build targets in make mode. e.g. 215*c2e18aaaSAndroid Build Coastguard Worker build/soong/soong_iu.bash --make-mode <MOD_NAME> 216*c2e18aaaSAndroid Build Coastguard Worker 217*c2e18aaaSAndroid Build Coastguard Worker Returns: 218*c2e18aaaSAndroid Build Coastguard Worker A list of soong build command. 219*c2e18aaaSAndroid Build Coastguard Worker """ 220*c2e18aaaSAndroid Build Coastguard Worker make_cmd = '%s/%s' % ( 221*c2e18aaaSAndroid Build Coastguard Worker os.path.relpath( 222*c2e18aaaSAndroid Build Coastguard Worker os.environ.get(constants.ANDROID_BUILD_TOP, os.getcwd()), os.getcwd() 223*c2e18aaaSAndroid Build Coastguard Worker ), 224*c2e18aaaSAndroid Build Coastguard Worker _BUILD_CMD, 225*c2e18aaaSAndroid Build Coastguard Worker ) 226*c2e18aaaSAndroid Build Coastguard Worker if dump: 227*c2e18aaaSAndroid Build Coastguard Worker return [make_cmd, '--dumpvar-mode', 'report_config'] 228*c2e18aaaSAndroid Build Coastguard Worker return [ 229*c2e18aaaSAndroid Build Coastguard Worker make_cmd, 230*c2e18aaaSAndroid Build Coastguard Worker '--make-mode', 231*c2e18aaaSAndroid Build Coastguard Worker 'WRAPPER_TOOL=atest', 232*c2e18aaaSAndroid Build Coastguard Worker f'ATEST_RUN_ID={metrics.get_run_id()}', 233*c2e18aaaSAndroid Build Coastguard Worker ] 234*c2e18aaaSAndroid Build Coastguard Worker 235*c2e18aaaSAndroid Build Coastguard Worker 236*c2e18aaaSAndroid Build Coastguard Workerdef _capture_fail_section(full_log): 237*c2e18aaaSAndroid Build Coastguard Worker """Return the error message from the build output. 238*c2e18aaaSAndroid Build Coastguard Worker 239*c2e18aaaSAndroid Build Coastguard Worker Args: 240*c2e18aaaSAndroid Build Coastguard Worker full_log: List of strings representing full output of build. 241*c2e18aaaSAndroid Build Coastguard Worker 242*c2e18aaaSAndroid Build Coastguard Worker Returns: 243*c2e18aaaSAndroid Build Coastguard Worker capture_output: List of strings that are build errors. 244*c2e18aaaSAndroid Build Coastguard Worker """ 245*c2e18aaaSAndroid Build Coastguard Worker am_capturing = False 246*c2e18aaaSAndroid Build Coastguard Worker capture_output = [] 247*c2e18aaaSAndroid Build Coastguard Worker for line in full_log: 248*c2e18aaaSAndroid Build Coastguard Worker if am_capturing and _BUILD_COMPILE_STATUS.match(line): 249*c2e18aaaSAndroid Build Coastguard Worker break 250*c2e18aaaSAndroid Build Coastguard Worker if am_capturing or line.startswith(_BUILD_FAILURE): 251*c2e18aaaSAndroid Build Coastguard Worker capture_output.append(line) 252*c2e18aaaSAndroid Build Coastguard Worker am_capturing = True 253*c2e18aaaSAndroid Build Coastguard Worker continue 254*c2e18aaaSAndroid Build Coastguard Worker return capture_output 255*c2e18aaaSAndroid Build Coastguard Worker 256*c2e18aaaSAndroid Build Coastguard Worker 257*c2e18aaaSAndroid Build Coastguard Workerdef _capture_limited_output(full_log): 258*c2e18aaaSAndroid Build Coastguard Worker """Return the limited error message from capture_failed_section. 259*c2e18aaaSAndroid Build Coastguard Worker 260*c2e18aaaSAndroid Build Coastguard Worker Args: 261*c2e18aaaSAndroid Build Coastguard Worker full_log: List of strings representing full output of build. 262*c2e18aaaSAndroid Build Coastguard Worker 263*c2e18aaaSAndroid Build Coastguard Worker Returns: 264*c2e18aaaSAndroid Build Coastguard Worker output: List of strings that are build errors. 265*c2e18aaaSAndroid Build Coastguard Worker """ 266*c2e18aaaSAndroid Build Coastguard Worker # Parse out the build error to output. 267*c2e18aaaSAndroid Build Coastguard Worker output = _capture_fail_section(full_log) 268*c2e18aaaSAndroid Build Coastguard Worker if not output: 269*c2e18aaaSAndroid Build Coastguard Worker output = full_log 270*c2e18aaaSAndroid Build Coastguard Worker if len(output) >= _FAILED_OUTPUT_LINE_LIMIT: 271*c2e18aaaSAndroid Build Coastguard Worker output = output[-_FAILED_OUTPUT_LINE_LIMIT:] 272*c2e18aaaSAndroid Build Coastguard Worker output = 'Output (may be trimmed):\n%s' % ''.join(output) 273*c2e18aaaSAndroid Build Coastguard Worker return output 274*c2e18aaaSAndroid Build Coastguard Worker 275*c2e18aaaSAndroid Build Coastguard Worker 276*c2e18aaaSAndroid Build Coastguard Workerdef stream_io_output( 277*c2e18aaaSAndroid Build Coastguard Worker io_input: IO, 278*c2e18aaaSAndroid Build Coastguard Worker max_lines=None, 279*c2e18aaaSAndroid Build Coastguard Worker full_output_receiver: IO = None, 280*c2e18aaaSAndroid Build Coastguard Worker io_output: IO = None, 281*c2e18aaaSAndroid Build Coastguard Worker is_io_output_atty=None, 282*c2e18aaaSAndroid Build Coastguard Worker): 283*c2e18aaaSAndroid Build Coastguard Worker """Stream an IO output with max number of rolling lines to display if set. 284*c2e18aaaSAndroid Build Coastguard Worker 285*c2e18aaaSAndroid Build Coastguard Worker Args: 286*c2e18aaaSAndroid Build Coastguard Worker input: The file-like object to read the output from. 287*c2e18aaaSAndroid Build Coastguard Worker max_lines: The maximum number of rolling lines to display. If None, all 288*c2e18aaaSAndroid Build Coastguard Worker lines will be displayed. 289*c2e18aaaSAndroid Build Coastguard Worker full_output_receiver: Optional io to receive the full output. 290*c2e18aaaSAndroid Build Coastguard Worker io_output: The file-like object to write the output to. 291*c2e18aaaSAndroid Build Coastguard Worker is_io_output_atty: Whether the io_output is a TTY. 292*c2e18aaaSAndroid Build Coastguard Worker """ 293*c2e18aaaSAndroid Build Coastguard Worker if io_output is None: 294*c2e18aaaSAndroid Build Coastguard Worker io_output = _original_sys_stdout 295*c2e18aaaSAndroid Build Coastguard Worker if is_io_output_atty is None: 296*c2e18aaaSAndroid Build Coastguard Worker is_io_output_atty = _has_colors(io_output) 297*c2e18aaaSAndroid Build Coastguard Worker if not max_lines or not is_io_output_atty: 298*c2e18aaaSAndroid Build Coastguard Worker for line in iter(io_input.readline, ''): 299*c2e18aaaSAndroid Build Coastguard Worker if not line: 300*c2e18aaaSAndroid Build Coastguard Worker break 301*c2e18aaaSAndroid Build Coastguard Worker if full_output_receiver is not None: 302*c2e18aaaSAndroid Build Coastguard Worker full_output_receiver.write( 303*c2e18aaaSAndroid Build Coastguard Worker line if isinstance(line, str) else line.decode('utf-8') 304*c2e18aaaSAndroid Build Coastguard Worker ) 305*c2e18aaaSAndroid Build Coastguard Worker io_output.write(line) 306*c2e18aaaSAndroid Build Coastguard Worker io_output.flush() 307*c2e18aaaSAndroid Build Coastguard Worker return 308*c2e18aaaSAndroid Build Coastguard Worker 309*c2e18aaaSAndroid Build Coastguard Worker term_width, _ = get_terminal_size() 310*c2e18aaaSAndroid Build Coastguard Worker last_lines = deque(maxlen=max_lines) 311*c2e18aaaSAndroid Build Coastguard Worker is_rolling = True 312*c2e18aaaSAndroid Build Coastguard Worker 313*c2e18aaaSAndroid Build Coastguard Worker def reset_output(): 314*c2e18aaaSAndroid Build Coastguard Worker if is_rolling and last_lines: 315*c2e18aaaSAndroid Build Coastguard Worker io_output.write(_BASH_CLEAR_PREVIOUS_LINE_CODE * (len(last_lines) + 2)) 316*c2e18aaaSAndroid Build Coastguard Worker 317*c2e18aaaSAndroid Build Coastguard Worker def write_output(new_lines: list[str]): 318*c2e18aaaSAndroid Build Coastguard Worker if not is_rolling: 319*c2e18aaaSAndroid Build Coastguard Worker return 320*c2e18aaaSAndroid Build Coastguard Worker last_lines.extend(new_lines) 321*c2e18aaaSAndroid Build Coastguard Worker lines = ['========== Rolling subprocess output =========='] 322*c2e18aaaSAndroid Build Coastguard Worker lines.extend(last_lines) 323*c2e18aaaSAndroid Build Coastguard Worker lines.append('-----------------------------------------------') 324*c2e18aaaSAndroid Build Coastguard Worker io_output.write('\n'.join(lines)) 325*c2e18aaaSAndroid Build Coastguard Worker io_output.write('\n') 326*c2e18aaaSAndroid Build Coastguard Worker io_output.flush() 327*c2e18aaaSAndroid Build Coastguard Worker 328*c2e18aaaSAndroid Build Coastguard Worker original_stdout = sys.stdout 329*c2e18aaaSAndroid Build Coastguard Worker original_stderr = sys.stderr 330*c2e18aaaSAndroid Build Coastguard Worker 331*c2e18aaaSAndroid Build Coastguard Worker lock = threading.Lock() 332*c2e18aaaSAndroid Build Coastguard Worker 333*c2e18aaaSAndroid Build Coastguard Worker class SafeStdout: 334*c2e18aaaSAndroid Build Coastguard Worker 335*c2e18aaaSAndroid Build Coastguard Worker def __init__(self): 336*c2e18aaaSAndroid Build Coastguard Worker self._buffers = [] 337*c2e18aaaSAndroid Build Coastguard Worker 338*c2e18aaaSAndroid Build Coastguard Worker def write(self, buf: str) -> None: 339*c2e18aaaSAndroid Build Coastguard Worker if len(buf) == 1 and buf[0] == '\n' and self._buffers: 340*c2e18aaaSAndroid Build Coastguard Worker with lock: 341*c2e18aaaSAndroid Build Coastguard Worker reset_output() 342*c2e18aaaSAndroid Build Coastguard Worker original_stdout.write(''.join(self._buffers)) 343*c2e18aaaSAndroid Build Coastguard Worker original_stdout.write('\n') 344*c2e18aaaSAndroid Build Coastguard Worker original_stdout.flush() 345*c2e18aaaSAndroid Build Coastguard Worker write_output([]) 346*c2e18aaaSAndroid Build Coastguard Worker self._buffers.clear() 347*c2e18aaaSAndroid Build Coastguard Worker else: 348*c2e18aaaSAndroid Build Coastguard Worker self._buffers.append(buf) 349*c2e18aaaSAndroid Build Coastguard Worker 350*c2e18aaaSAndroid Build Coastguard Worker def flush(self) -> None: 351*c2e18aaaSAndroid Build Coastguard Worker original_stdout.flush() 352*c2e18aaaSAndroid Build Coastguard Worker 353*c2e18aaaSAndroid Build Coastguard Worker sys.stdout = SafeStdout() 354*c2e18aaaSAndroid Build Coastguard Worker sys.stderr = sys.stdout 355*c2e18aaaSAndroid Build Coastguard Worker 356*c2e18aaaSAndroid Build Coastguard Worker for line in iter(io_input.readline, ''): 357*c2e18aaaSAndroid Build Coastguard Worker if not line: 358*c2e18aaaSAndroid Build Coastguard Worker break 359*c2e18aaaSAndroid Build Coastguard Worker line = line.decode('utf-8') if isinstance(line, bytes) else line 360*c2e18aaaSAndroid Build Coastguard Worker if full_output_receiver is not None: 361*c2e18aaaSAndroid Build Coastguard Worker full_output_receiver.write(line) 362*c2e18aaaSAndroid Build Coastguard Worker line = line.rstrip().replace('\t', ' ') 363*c2e18aaaSAndroid Build Coastguard Worker # Split the line if it's longer than the terminal width 364*c2e18aaaSAndroid Build Coastguard Worker wrapped_lines = ( 365*c2e18aaaSAndroid Build Coastguard Worker [line] 366*c2e18aaaSAndroid Build Coastguard Worker if len(line) <= term_width 367*c2e18aaaSAndroid Build Coastguard Worker else [line[i : i + term_width] for i in range(0, len(line), term_width)] 368*c2e18aaaSAndroid Build Coastguard Worker ) 369*c2e18aaaSAndroid Build Coastguard Worker with lock: 370*c2e18aaaSAndroid Build Coastguard Worker reset_output() 371*c2e18aaaSAndroid Build Coastguard Worker write_output(wrapped_lines) 372*c2e18aaaSAndroid Build Coastguard Worker 373*c2e18aaaSAndroid Build Coastguard Worker with lock: 374*c2e18aaaSAndroid Build Coastguard Worker reset_output() 375*c2e18aaaSAndroid Build Coastguard Worker is_rolling = False 376*c2e18aaaSAndroid Build Coastguard Worker io_output.write(_BASH_RESET_CODE) 377*c2e18aaaSAndroid Build Coastguard Worker io_output.flush() 378*c2e18aaaSAndroid Build Coastguard Worker 379*c2e18aaaSAndroid Build Coastguard Worker sys.stdout = original_stdout 380*c2e18aaaSAndroid Build Coastguard Worker sys.stderr = original_stderr 381*c2e18aaaSAndroid Build Coastguard Worker 382*c2e18aaaSAndroid Build Coastguard Worker io_input.close() 383*c2e18aaaSAndroid Build Coastguard Worker 384*c2e18aaaSAndroid Build Coastguard Worker 385*c2e18aaaSAndroid Build Coastguard Workerdef run_limited_output( 386*c2e18aaaSAndroid Build Coastguard Worker cmd, env_vars=None, shell=False, start_new_session=False 387*c2e18aaaSAndroid Build Coastguard Worker): 388*c2e18aaaSAndroid Build Coastguard Worker """Runs a given command and streams the output on a single line in stdout. 389*c2e18aaaSAndroid Build Coastguard Worker 390*c2e18aaaSAndroid Build Coastguard Worker Args: 391*c2e18aaaSAndroid Build Coastguard Worker cmd: A list of strings representing the command to run. 392*c2e18aaaSAndroid Build Coastguard Worker env_vars: Optional arg. Dict of env vars to set during build. 393*c2e18aaaSAndroid Build Coastguard Worker shell: Optional arg. Whether to use shell to run the command. 394*c2e18aaaSAndroid Build Coastguard Worker start_new_session: Optional arg. Whether to start a new session for the 395*c2e18aaaSAndroid Build Coastguard Worker command. 396*c2e18aaaSAndroid Build Coastguard Worker 397*c2e18aaaSAndroid Build Coastguard Worker Raises: 398*c2e18aaaSAndroid Build Coastguard Worker subprocess.CalledProcessError: When the command exits with a non-0 399*c2e18aaaSAndroid Build Coastguard Worker exitcode. 400*c2e18aaaSAndroid Build Coastguard Worker """ 401*c2e18aaaSAndroid Build Coastguard Worker # Send stderr to stdout so we only have to deal with a single pipe. 402*c2e18aaaSAndroid Build Coastguard Worker with subprocess.Popen( 403*c2e18aaaSAndroid Build Coastguard Worker cmd, 404*c2e18aaaSAndroid Build Coastguard Worker stdout=subprocess.PIPE, 405*c2e18aaaSAndroid Build Coastguard Worker stderr=subprocess.STDOUT, 406*c2e18aaaSAndroid Build Coastguard Worker env=env_vars, 407*c2e18aaaSAndroid Build Coastguard Worker shell=shell, 408*c2e18aaaSAndroid Build Coastguard Worker start_new_session=start_new_session, 409*c2e18aaaSAndroid Build Coastguard Worker text=True, 410*c2e18aaaSAndroid Build Coastguard Worker ) as proc: 411*c2e18aaaSAndroid Build Coastguard Worker full_output_receiver = io.StringIO() 412*c2e18aaaSAndroid Build Coastguard Worker stream_io_output( 413*c2e18aaaSAndroid Build Coastguard Worker proc.stdout, 414*c2e18aaaSAndroid Build Coastguard Worker DEFAULT_OUTPUT_ROLLING_LINES, 415*c2e18aaaSAndroid Build Coastguard Worker full_output_receiver, 416*c2e18aaaSAndroid Build Coastguard Worker _original_sys_stdout, 417*c2e18aaaSAndroid Build Coastguard Worker ) 418*c2e18aaaSAndroid Build Coastguard Worker returncode = proc.wait() 419*c2e18aaaSAndroid Build Coastguard Worker if returncode: 420*c2e18aaaSAndroid Build Coastguard Worker raise subprocess.CalledProcessError( 421*c2e18aaaSAndroid Build Coastguard Worker returncode, cmd, full_output_receiver.getvalue() 422*c2e18aaaSAndroid Build Coastguard Worker ) 423*c2e18aaaSAndroid Build Coastguard Worker 424*c2e18aaaSAndroid Build Coastguard Worker 425*c2e18aaaSAndroid Build Coastguard Workerdef get_build_out_dir(*joinpaths) -> Path: 426*c2e18aaaSAndroid Build Coastguard Worker """Get android build out directory. 427*c2e18aaaSAndroid Build Coastguard Worker 428*c2e18aaaSAndroid Build Coastguard Worker The order of the rules are: 429*c2e18aaaSAndroid Build Coastguard Worker 1. OUT_DIR 430*c2e18aaaSAndroid Build Coastguard Worker 2. OUT_DIR_COMMON_BASE 431*c2e18aaaSAndroid Build Coastguard Worker 3. ANDROID_BUILD_TOP/out 432*c2e18aaaSAndroid Build Coastguard Worker 433*c2e18aaaSAndroid Build Coastguard Worker e.g. OUT_DIR='/disk1/out' -> '/disk1/out' 434*c2e18aaaSAndroid Build Coastguard Worker OUT_DIR='out_dir' -> '<build_top>/out_dir' 435*c2e18aaaSAndroid Build Coastguard Worker 436*c2e18aaaSAndroid Build Coastguard Worker Assume the branch name is 'aosp-main': 437*c2e18aaaSAndroid Build Coastguard Worker OUT_DIR_COMMON_BASE='/disk2/out' -> '/disk1/out/aosp-main' 438*c2e18aaaSAndroid Build Coastguard Worker OUT_DIR_COMMON_BASE='out_dir' -> '<build_top>/out_dir/aosp-main' 439*c2e18aaaSAndroid Build Coastguard Worker 440*c2e18aaaSAndroid Build Coastguard Worker Returns: 441*c2e18aaaSAndroid Build Coastguard Worker Absolute Path of the out directory. 442*c2e18aaaSAndroid Build Coastguard Worker """ 443*c2e18aaaSAndroid Build Coastguard Worker out_dir = getenv_abs_path('OUT_DIR') 444*c2e18aaaSAndroid Build Coastguard Worker if out_dir: 445*c2e18aaaSAndroid Build Coastguard Worker return out_dir.joinpath(*joinpaths) 446*c2e18aaaSAndroid Build Coastguard Worker 447*c2e18aaaSAndroid Build Coastguard Worker # https://source.android.com/setup/build/initializing#using-a-separate-output-directory 448*c2e18aaaSAndroid Build Coastguard Worker basename = get_build_top().name 449*c2e18aaaSAndroid Build Coastguard Worker out_dir_common_base = getenv_abs_path('OUT_DIR_COMMON_BASE', basename) 450*c2e18aaaSAndroid Build Coastguard Worker if out_dir_common_base: 451*c2e18aaaSAndroid Build Coastguard Worker return out_dir_common_base.joinpath(*joinpaths) 452*c2e18aaaSAndroid Build Coastguard Worker 453*c2e18aaaSAndroid Build Coastguard Worker return get_build_top('out').joinpath(*joinpaths) 454*c2e18aaaSAndroid Build Coastguard Worker 455*c2e18aaaSAndroid Build Coastguard Worker 456*c2e18aaaSAndroid Build Coastguard Workerdef update_build_env(env: Dict[str, str]): 457*c2e18aaaSAndroid Build Coastguard Worker """Method that updates build environment variables.""" 458*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=global-statement, global-variable-not-assigned 459*c2e18aaaSAndroid Build Coastguard Worker global _BUILD_ENV 460*c2e18aaaSAndroid Build Coastguard Worker _BUILD_ENV.update(env) 461*c2e18aaaSAndroid Build Coastguard Worker 462*c2e18aaaSAndroid Build Coastguard Worker 463*c2e18aaaSAndroid Build Coastguard Workerdef build(build_targets: Set[str]): 464*c2e18aaaSAndroid Build Coastguard Worker """Shell out and invoke run_build_cmd to make build_targets. 465*c2e18aaaSAndroid Build Coastguard Worker 466*c2e18aaaSAndroid Build Coastguard Worker Args: 467*c2e18aaaSAndroid Build Coastguard Worker build_targets: A set of strings of build targets to make. 468*c2e18aaaSAndroid Build Coastguard Worker 469*c2e18aaaSAndroid Build Coastguard Worker Returns: 470*c2e18aaaSAndroid Build Coastguard Worker Boolean of whether build command was successful, True if nothing to 471*c2e18aaaSAndroid Build Coastguard Worker build. 472*c2e18aaaSAndroid Build Coastguard Worker """ 473*c2e18aaaSAndroid Build Coastguard Worker if not build_targets: 474*c2e18aaaSAndroid Build Coastguard Worker logging.debug('No build targets, skipping build.') 475*c2e18aaaSAndroid Build Coastguard Worker return True 476*c2e18aaaSAndroid Build Coastguard Worker 477*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=global-statement, global-variable-not-assigned 478*c2e18aaaSAndroid Build Coastguard Worker global _BUILD_ENV 479*c2e18aaaSAndroid Build Coastguard Worker full_env_vars = os.environ.copy() 480*c2e18aaaSAndroid Build Coastguard Worker update_build_env(full_env_vars) 481*c2e18aaaSAndroid Build Coastguard Worker print( 482*c2e18aaaSAndroid Build Coastguard Worker '\n%s\n%s' 483*c2e18aaaSAndroid Build Coastguard Worker % (mark_cyan('Building Dependencies...'), ', '.join(build_targets)) 484*c2e18aaaSAndroid Build Coastguard Worker ) 485*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Building Dependencies: %s', ' '.join(build_targets)) 486*c2e18aaaSAndroid Build Coastguard Worker cmd = get_build_cmd() + list(build_targets) 487*c2e18aaaSAndroid Build Coastguard Worker return _run_build_cmd(cmd, _BUILD_ENV) 488*c2e18aaaSAndroid Build Coastguard Worker 489*c2e18aaaSAndroid Build Coastguard Worker 490*c2e18aaaSAndroid Build Coastguard Workerdef _run_build_cmd_with_limited_output( 491*c2e18aaaSAndroid Build Coastguard Worker cmd: List[str], env_vars: Dict[str, str] = None 492*c2e18aaaSAndroid Build Coastguard Worker) -> None: 493*c2e18aaaSAndroid Build Coastguard Worker """Runs the build command and streams the output on a single line in stdout. 494*c2e18aaaSAndroid Build Coastguard Worker 495*c2e18aaaSAndroid Build Coastguard Worker Args: 496*c2e18aaaSAndroid Build Coastguard Worker cmd: A list of strings representing the command to run. 497*c2e18aaaSAndroid Build Coastguard Worker env_vars: Optional arg. Dict of env vars to set during build. 498*c2e18aaaSAndroid Build Coastguard Worker 499*c2e18aaaSAndroid Build Coastguard Worker Raises: 500*c2e18aaaSAndroid Build Coastguard Worker subprocess.CalledProcessError: When the command exits with a non-0 501*c2e18aaaSAndroid Build Coastguard Worker exitcode. 502*c2e18aaaSAndroid Build Coastguard Worker """ 503*c2e18aaaSAndroid Build Coastguard Worker try: 504*c2e18aaaSAndroid Build Coastguard Worker run_limited_output(cmd, env_vars=env_vars) 505*c2e18aaaSAndroid Build Coastguard Worker except subprocess.CalledProcessError as e: 506*c2e18aaaSAndroid Build Coastguard Worker # get error log from "OUT_DIR/error.log" 507*c2e18aaaSAndroid Build Coastguard Worker error_log_file = get_build_out_dir('error.log') 508*c2e18aaaSAndroid Build Coastguard Worker output = [] 509*c2e18aaaSAndroid Build Coastguard Worker if error_log_file.is_file(): 510*c2e18aaaSAndroid Build Coastguard Worker if error_log_file.stat().st_size > 0: 511*c2e18aaaSAndroid Build Coastguard Worker with open(error_log_file, encoding='utf-8') as f: 512*c2e18aaaSAndroid Build Coastguard Worker output = f.read() 513*c2e18aaaSAndroid Build Coastguard Worker if not output: 514*c2e18aaaSAndroid Build Coastguard Worker output = _capture_limited_output(e.output) 515*c2e18aaaSAndroid Build Coastguard Worker raise subprocess.CalledProcessError(e.returncode, e.cmd, output) 516*c2e18aaaSAndroid Build Coastguard Worker 517*c2e18aaaSAndroid Build Coastguard Worker 518*c2e18aaaSAndroid Build Coastguard Workerdef _run_build_cmd(cmd: List[str], env_vars: Dict[str, str]): 519*c2e18aaaSAndroid Build Coastguard Worker """The main process of building targets. 520*c2e18aaaSAndroid Build Coastguard Worker 521*c2e18aaaSAndroid Build Coastguard Worker Args: 522*c2e18aaaSAndroid Build Coastguard Worker cmd: A list of soong command. 523*c2e18aaaSAndroid Build Coastguard Worker env_vars: Dict of environment variables used for build. 524*c2e18aaaSAndroid Build Coastguard Worker 525*c2e18aaaSAndroid Build Coastguard Worker Returns: 526*c2e18aaaSAndroid Build Coastguard Worker Boolean of whether build command was successful, True if nothing to 527*c2e18aaaSAndroid Build Coastguard Worker build. 528*c2e18aaaSAndroid Build Coastguard Worker """ 529*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Executing command: %s', cmd) 530*c2e18aaaSAndroid Build Coastguard Worker build_profiler = _build_env_profiling() 531*c2e18aaaSAndroid Build Coastguard Worker try: 532*c2e18aaaSAndroid Build Coastguard Worker if env_vars.get('BUILD_OUTPUT_MODE') == BuildOutputMode.STREAMED.value: 533*c2e18aaaSAndroid Build Coastguard Worker print() 534*c2e18aaaSAndroid Build Coastguard Worker subprocess.check_call(cmd, stderr=subprocess.STDOUT, env=env_vars) 535*c2e18aaaSAndroid Build Coastguard Worker else: 536*c2e18aaaSAndroid Build Coastguard Worker # Note that piping stdout forces Soong to switch to 'dumb terminal 537*c2e18aaaSAndroid Build Coastguard Worker # mode' which only prints completed actions. This gives users the 538*c2e18aaaSAndroid Build Coastguard Worker # impression that actions are taking longer than they really are. 539*c2e18aaaSAndroid Build Coastguard Worker # See b/233044822 for more details. 540*c2e18aaaSAndroid Build Coastguard Worker log_path = get_build_out_dir('verbose.log.gz') 541*c2e18aaaSAndroid Build Coastguard Worker print( 542*c2e18aaaSAndroid Build Coastguard Worker '\n(Build log may not reflect actual status in simple output' 543*c2e18aaaSAndroid Build Coastguard Worker 'mode; check {} for detail after build finishes.)'.format( 544*c2e18aaaSAndroid Build Coastguard Worker mark_cyan(f'{log_path}') 545*c2e18aaaSAndroid Build Coastguard Worker ), 546*c2e18aaaSAndroid Build Coastguard Worker end='', 547*c2e18aaaSAndroid Build Coastguard Worker ) 548*c2e18aaaSAndroid Build Coastguard Worker _run_build_cmd_with_limited_output(cmd, env_vars=env_vars) 549*c2e18aaaSAndroid Build Coastguard Worker _send_build_condition_metrics(build_profiler, cmd) 550*c2e18aaaSAndroid Build Coastguard Worker print_and_log_info('Build successful') 551*c2e18aaaSAndroid Build Coastguard Worker return True 552*c2e18aaaSAndroid Build Coastguard Worker except subprocess.CalledProcessError as err: 553*c2e18aaaSAndroid Build Coastguard Worker print_and_log_error('Build failure when running: %s', ' '.join(cmd)) 554*c2e18aaaSAndroid Build Coastguard Worker if err.output: 555*c2e18aaaSAndroid Build Coastguard Worker print_and_log_error(err.output) 556*c2e18aaaSAndroid Build Coastguard Worker return False 557*c2e18aaaSAndroid Build Coastguard Worker 558*c2e18aaaSAndroid Build Coastguard Worker 559*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=unused-argument 560*c2e18aaaSAndroid Build Coastguard Workerdef get_result_server_args(for_test_mapping=False): 561*c2e18aaaSAndroid Build Coastguard Worker """Return list of args for communication with result server. 562*c2e18aaaSAndroid Build Coastguard Worker 563*c2e18aaaSAndroid Build Coastguard Worker Args: 564*c2e18aaaSAndroid Build Coastguard Worker for_test_mapping: True if the test run is for Test Mapping to include 565*c2e18aaaSAndroid Build Coastguard Worker additional reporting args. Default is False. 566*c2e18aaaSAndroid Build Coastguard Worker """ 567*c2e18aaaSAndroid Build Coastguard Worker # Customize test mapping argument here if needed. 568*c2e18aaaSAndroid Build Coastguard Worker return constants.RESULT_SERVER_ARGS 569*c2e18aaaSAndroid Build Coastguard Worker 570*c2e18aaaSAndroid Build Coastguard Worker 571*c2e18aaaSAndroid Build Coastguard Workerdef sort_and_group(iterable, key): 572*c2e18aaaSAndroid Build Coastguard Worker """Sort and group helper function.""" 573*c2e18aaaSAndroid Build Coastguard Worker return itertools.groupby(sorted(iterable, key=key), key=key) 574*c2e18aaaSAndroid Build Coastguard Worker 575*c2e18aaaSAndroid Build Coastguard Worker 576*c2e18aaaSAndroid Build Coastguard Workerdef is_supported_mainline_module(installed_path: str) -> re.Match: 577*c2e18aaaSAndroid Build Coastguard Worker """Determine whether the given path is supported.""" 578*c2e18aaaSAndroid Build Coastguard Worker return re.search(MAINLINE_MODULES_EXT_RE, installed_path) 579*c2e18aaaSAndroid Build Coastguard Worker 580*c2e18aaaSAndroid Build Coastguard Worker 581*c2e18aaaSAndroid Build Coastguard Workerdef get_test_and_mainline_modules(test_name: str) -> re.Match: 582*c2e18aaaSAndroid Build Coastguard Worker """Return test name and mainline modules from the given test.""" 583*c2e18aaaSAndroid Build Coastguard Worker return TEST_WITH_MAINLINE_MODULES_RE.match(test_name) 584*c2e18aaaSAndroid Build Coastguard Worker 585*c2e18aaaSAndroid Build Coastguard Worker 586*c2e18aaaSAndroid Build Coastguard Workerdef is_test_mapping(args): 587*c2e18aaaSAndroid Build Coastguard Worker """Check if the atest command intends to run tests in test mapping. 588*c2e18aaaSAndroid Build Coastguard Worker 589*c2e18aaaSAndroid Build Coastguard Worker When atest runs tests in test mapping, it must have at most one test 590*c2e18aaaSAndroid Build Coastguard Worker specified. If a test is specified, it must be started with `:`, 591*c2e18aaaSAndroid Build Coastguard Worker which means the test value is a test group name in TEST_MAPPING file, e.g., 592*c2e18aaaSAndroid Build Coastguard Worker `:postsubmit`. 593*c2e18aaaSAndroid Build Coastguard Worker 594*c2e18aaaSAndroid Build Coastguard Worker If --host-unit-test-only or --smart-testing-local was applied, it doesn't 595*c2e18aaaSAndroid Build Coastguard Worker intend to be a test_mapping test. 596*c2e18aaaSAndroid Build Coastguard Worker If any test mapping options is specified, the atest command must also be 597*c2e18aaaSAndroid Build Coastguard Worker set to run tests in test mapping files. 598*c2e18aaaSAndroid Build Coastguard Worker 599*c2e18aaaSAndroid Build Coastguard Worker Args: 600*c2e18aaaSAndroid Build Coastguard Worker args: arg parsed object. 601*c2e18aaaSAndroid Build Coastguard Worker 602*c2e18aaaSAndroid Build Coastguard Worker Returns: 603*c2e18aaaSAndroid Build Coastguard Worker True if the args indicates atest shall run tests in test mapping. False 604*c2e18aaaSAndroid Build Coastguard Worker otherwise. 605*c2e18aaaSAndroid Build Coastguard Worker """ 606*c2e18aaaSAndroid Build Coastguard Worker if args.host_unit_test_only: 607*c2e18aaaSAndroid Build Coastguard Worker return False 608*c2e18aaaSAndroid Build Coastguard Worker if any((args.test_mapping, args.include_subdirs, not args.tests)): 609*c2e18aaaSAndroid Build Coastguard Worker return True 610*c2e18aaaSAndroid Build Coastguard Worker # ':postsubmit' implicitly indicates running in test-mapping mode. 611*c2e18aaaSAndroid Build Coastguard Worker return all((len(args.tests) == 1, args.tests[0][0] == ':')) 612*c2e18aaaSAndroid Build Coastguard Worker 613*c2e18aaaSAndroid Build Coastguard Worker 614*c2e18aaaSAndroid Build Coastguard Workerdef is_atty_terminal() -> bool: 615*c2e18aaaSAndroid Build Coastguard Worker """Check if the current process is running in a TTY.""" 616*c2e18aaaSAndroid Build Coastguard Worker return getattr(_original_sys_stdout, 'isatty', lambda: False)() 617*c2e18aaaSAndroid Build Coastguard Worker 618*c2e18aaaSAndroid Build Coastguard Worker 619*c2e18aaaSAndroid Build Coastguard Workerdef _has_colors(stream): 620*c2e18aaaSAndroid Build Coastguard Worker """Check the output stream is colorful. 621*c2e18aaaSAndroid Build Coastguard Worker 622*c2e18aaaSAndroid Build Coastguard Worker Args: 623*c2e18aaaSAndroid Build Coastguard Worker stream: The standard file stream. 624*c2e18aaaSAndroid Build Coastguard Worker 625*c2e18aaaSAndroid Build Coastguard Worker Returns: 626*c2e18aaaSAndroid Build Coastguard Worker True if the file stream can interpreter the ANSI color code. 627*c2e18aaaSAndroid Build Coastguard Worker """ 628*c2e18aaaSAndroid Build Coastguard Worker # Following from Python cookbook, #475186 629*c2e18aaaSAndroid Build Coastguard Worker # Auto color only on TTYs 630*c2e18aaaSAndroid Build Coastguard Worker # curses.tigetnum() cannot be used for telling supported color numbers 631*c2e18aaaSAndroid Build Coastguard Worker # because it does not come with the prebuilt py3-cmd. 632*c2e18aaaSAndroid Build Coastguard Worker return getattr(stream, 'isatty', lambda: False)() 633*c2e18aaaSAndroid Build Coastguard Worker 634*c2e18aaaSAndroid Build Coastguard Worker 635*c2e18aaaSAndroid Build Coastguard Workerdef colorize(text, color, bp_color=None): 636*c2e18aaaSAndroid Build Coastguard Worker """Convert to colorful string with ANSI escape code. 637*c2e18aaaSAndroid Build Coastguard Worker 638*c2e18aaaSAndroid Build Coastguard Worker Args: 639*c2e18aaaSAndroid Build Coastguard Worker text: A string to print. 640*c2e18aaaSAndroid Build Coastguard Worker color: Forground(Text) color which is an ANSI code shift for colorful 641*c2e18aaaSAndroid Build Coastguard Worker print. They are defined in constants_default.py. 642*c2e18aaaSAndroid Build Coastguard Worker bp_color: Backgroud color which is an ANSI code shift for colorful print. 643*c2e18aaaSAndroid Build Coastguard Worker 644*c2e18aaaSAndroid Build Coastguard Worker Returns: 645*c2e18aaaSAndroid Build Coastguard Worker Colorful string with ANSI escape code. 646*c2e18aaaSAndroid Build Coastguard Worker """ 647*c2e18aaaSAndroid Build Coastguard Worker clr_pref = '\033[1;' 648*c2e18aaaSAndroid Build Coastguard Worker clr_suff = '\033[0m' 649*c2e18aaaSAndroid Build Coastguard Worker has_colors = _has_colors(_original_sys_stdout) 650*c2e18aaaSAndroid Build Coastguard Worker if has_colors: 651*c2e18aaaSAndroid Build Coastguard Worker background_color = '' 652*c2e18aaaSAndroid Build Coastguard Worker if bp_color: 653*c2e18aaaSAndroid Build Coastguard Worker # Foreground(Text) ranges from 30-37 654*c2e18aaaSAndroid Build Coastguard Worker text_color = 30 + color 655*c2e18aaaSAndroid Build Coastguard Worker # Background ranges from 40-47 656*c2e18aaaSAndroid Build Coastguard Worker background_color = ';%d' % (40 + bp_color) 657*c2e18aaaSAndroid Build Coastguard Worker else: 658*c2e18aaaSAndroid Build Coastguard Worker text_color = 30 + color 659*c2e18aaaSAndroid Build Coastguard Worker clr_str = '%s%d%sm%s%s' % ( 660*c2e18aaaSAndroid Build Coastguard Worker clr_pref, 661*c2e18aaaSAndroid Build Coastguard Worker text_color, 662*c2e18aaaSAndroid Build Coastguard Worker background_color, 663*c2e18aaaSAndroid Build Coastguard Worker text, 664*c2e18aaaSAndroid Build Coastguard Worker clr_suff, 665*c2e18aaaSAndroid Build Coastguard Worker ) 666*c2e18aaaSAndroid Build Coastguard Worker else: 667*c2e18aaaSAndroid Build Coastguard Worker clr_str = text 668*c2e18aaaSAndroid Build Coastguard Worker return clr_str 669*c2e18aaaSAndroid Build Coastguard Worker 670*c2e18aaaSAndroid Build Coastguard Worker 671*c2e18aaaSAndroid Build Coastguard Workerdef mark_red(text): 672*c2e18aaaSAndroid Build Coastguard Worker """Wrap colorized function and print in red.""" 673*c2e18aaaSAndroid Build Coastguard Worker return colorize(text, constants.RED) 674*c2e18aaaSAndroid Build Coastguard Worker 675*c2e18aaaSAndroid Build Coastguard Worker 676*c2e18aaaSAndroid Build Coastguard Workerdef mark_yellow(text): 677*c2e18aaaSAndroid Build Coastguard Worker """Wrap colorized function and print in yellow.""" 678*c2e18aaaSAndroid Build Coastguard Worker return colorize(text, constants.YELLOW) 679*c2e18aaaSAndroid Build Coastguard Worker 680*c2e18aaaSAndroid Build Coastguard Worker 681*c2e18aaaSAndroid Build Coastguard Workerdef mark_green(text): 682*c2e18aaaSAndroid Build Coastguard Worker """Wrap colorized function and print in green.""" 683*c2e18aaaSAndroid Build Coastguard Worker return colorize(text, constants.GREEN) 684*c2e18aaaSAndroid Build Coastguard Worker 685*c2e18aaaSAndroid Build Coastguard Worker 686*c2e18aaaSAndroid Build Coastguard Workerdef mark_magenta(text): 687*c2e18aaaSAndroid Build Coastguard Worker """Wrap colorized function and print in magenta.""" 688*c2e18aaaSAndroid Build Coastguard Worker return colorize(text, constants.MAGENTA) 689*c2e18aaaSAndroid Build Coastguard Worker 690*c2e18aaaSAndroid Build Coastguard Worker 691*c2e18aaaSAndroid Build Coastguard Workerdef mark_cyan(text): 692*c2e18aaaSAndroid Build Coastguard Worker """Wrap colorized function and print in cyan.""" 693*c2e18aaaSAndroid Build Coastguard Worker return colorize(text, constants.CYAN) 694*c2e18aaaSAndroid Build Coastguard Worker 695*c2e18aaaSAndroid Build Coastguard Worker 696*c2e18aaaSAndroid Build Coastguard Workerdef mark_blue(text): 697*c2e18aaaSAndroid Build Coastguard Worker """Wrap colorized function and print in blue.""" 698*c2e18aaaSAndroid Build Coastguard Worker return colorize(text, constants.BLUE) 699*c2e18aaaSAndroid Build Coastguard Worker 700*c2e18aaaSAndroid Build Coastguard Worker 701*c2e18aaaSAndroid Build Coastguard Workerdef colorful_print(text, color=None, bp_color=None, auto_wrap=True): 702*c2e18aaaSAndroid Build Coastguard Worker """Print out the text with color. 703*c2e18aaaSAndroid Build Coastguard Worker 704*c2e18aaaSAndroid Build Coastguard Worker Args: 705*c2e18aaaSAndroid Build Coastguard Worker text: A string to print. 706*c2e18aaaSAndroid Build Coastguard Worker color: Forground(Text) color which is an ANSI code shift for colorful 707*c2e18aaaSAndroid Build Coastguard Worker print. They are defined in constants_default.py. 708*c2e18aaaSAndroid Build Coastguard Worker bp_color: Backgroud color which is an ANSI code shift for colorful print. 709*c2e18aaaSAndroid Build Coastguard Worker auto_wrap: If True, Text wraps while print. 710*c2e18aaaSAndroid Build Coastguard Worker """ 711*c2e18aaaSAndroid Build Coastguard Worker output = colorize(text, color, bp_color) if color else text 712*c2e18aaaSAndroid Build Coastguard Worker if auto_wrap: 713*c2e18aaaSAndroid Build Coastguard Worker print(output) 714*c2e18aaaSAndroid Build Coastguard Worker else: 715*c2e18aaaSAndroid Build Coastguard Worker print(output, end='') 716*c2e18aaaSAndroid Build Coastguard Worker 717*c2e18aaaSAndroid Build Coastguard Worker 718*c2e18aaaSAndroid Build Coastguard Workerdef _print_to_console( 719*c2e18aaaSAndroid Build Coastguard Worker prefix: str, msg: Any, *fmt_args: list[Any], color: int = None 720*c2e18aaaSAndroid Build Coastguard Worker) -> None: 721*c2e18aaaSAndroid Build Coastguard Worker """Print a message to the console. 722*c2e18aaaSAndroid Build Coastguard Worker 723*c2e18aaaSAndroid Build Coastguard Worker Args: 724*c2e18aaaSAndroid Build Coastguard Worker msg: The message to format. 725*c2e18aaaSAndroid Build Coastguard Worker *fmt_args: Format arguments for the message. 726*c2e18aaaSAndroid Build Coastguard Worker """ 727*c2e18aaaSAndroid Build Coastguard Worker if not fmt_args: 728*c2e18aaaSAndroid Build Coastguard Worker evaluated_msg = str(msg) 729*c2e18aaaSAndroid Build Coastguard Worker else: 730*c2e18aaaSAndroid Build Coastguard Worker try: 731*c2e18aaaSAndroid Build Coastguard Worker evaluated_msg = msg % fmt_args 732*c2e18aaaSAndroid Build Coastguard Worker except (TypeError, ValueError): 733*c2e18aaaSAndroid Build Coastguard Worker traceback.print_exc() 734*c2e18aaaSAndroid Build Coastguard Worker return 735*c2e18aaaSAndroid Build Coastguard Worker colorful_print(f'{prefix}{evaluated_msg}', color) 736*c2e18aaaSAndroid Build Coastguard Worker 737*c2e18aaaSAndroid Build Coastguard Worker 738*c2e18aaaSAndroid Build Coastguard Workerdef print_and_log_error(msg, *fmt_args): 739*c2e18aaaSAndroid Build Coastguard Worker """Print error message to the console and log it. 740*c2e18aaaSAndroid Build Coastguard Worker 741*c2e18aaaSAndroid Build Coastguard Worker Args: 742*c2e18aaaSAndroid Build Coastguard Worker msg: The message to print. 743*c2e18aaaSAndroid Build Coastguard Worker *fmt_args: Format arguments for the message. 744*c2e18aaaSAndroid Build Coastguard Worker """ 745*c2e18aaaSAndroid Build Coastguard Worker logging.error(msg, *fmt_args) 746*c2e18aaaSAndroid Build Coastguard Worker _print_to_console('Error: ', msg, *fmt_args, color=constants.RED) 747*c2e18aaaSAndroid Build Coastguard Worker 748*c2e18aaaSAndroid Build Coastguard Worker 749*c2e18aaaSAndroid Build Coastguard Workerdef print_and_log_warning(msg, *fmt_args): 750*c2e18aaaSAndroid Build Coastguard Worker """Print warning message to the console and log it. 751*c2e18aaaSAndroid Build Coastguard Worker 752*c2e18aaaSAndroid Build Coastguard Worker Args: 753*c2e18aaaSAndroid Build Coastguard Worker msg: The message to print. 754*c2e18aaaSAndroid Build Coastguard Worker *fmt_args: Format arguments for the message. 755*c2e18aaaSAndroid Build Coastguard Worker """ 756*c2e18aaaSAndroid Build Coastguard Worker logging.warning(msg, *fmt_args) 757*c2e18aaaSAndroid Build Coastguard Worker _print_to_console('Warning: ', msg, *fmt_args, color=constants.MAGENTA) 758*c2e18aaaSAndroid Build Coastguard Worker 759*c2e18aaaSAndroid Build Coastguard Worker 760*c2e18aaaSAndroid Build Coastguard Workerdef print_and_log_info(msg, *fmt_args): 761*c2e18aaaSAndroid Build Coastguard Worker """Print info message to the console and log it. 762*c2e18aaaSAndroid Build Coastguard Worker 763*c2e18aaaSAndroid Build Coastguard Worker Args: 764*c2e18aaaSAndroid Build Coastguard Worker msg: The message to print. 765*c2e18aaaSAndroid Build Coastguard Worker *fmt_args: Format arguments for the message. 766*c2e18aaaSAndroid Build Coastguard Worker """ 767*c2e18aaaSAndroid Build Coastguard Worker logging.info(msg, *fmt_args) 768*c2e18aaaSAndroid Build Coastguard Worker _print_to_console(mark_cyan('Info: '), msg, *fmt_args) 769*c2e18aaaSAndroid Build Coastguard Worker 770*c2e18aaaSAndroid Build Coastguard Worker 771*c2e18aaaSAndroid Build Coastguard Workerdef get_terminal_size(): 772*c2e18aaaSAndroid Build Coastguard Worker """Get terminal size and return a tuple. 773*c2e18aaaSAndroid Build Coastguard Worker 774*c2e18aaaSAndroid Build Coastguard Worker Returns: 775*c2e18aaaSAndroid Build Coastguard Worker 2 integers: the size of X(columns) and Y(lines/rows). 776*c2e18aaaSAndroid Build Coastguard Worker """ 777*c2e18aaaSAndroid Build Coastguard Worker # Determine the width of the terminal. We'll need to clear this many 778*c2e18aaaSAndroid Build Coastguard Worker # characters when carriage returning. Set default value as 80. 779*c2e18aaaSAndroid Build Coastguard Worker columns, rows = shutil.get_terminal_size( 780*c2e18aaaSAndroid Build Coastguard Worker fallback=(_DEFAULT_TERMINAL_WIDTH, _DEFAULT_TERMINAL_HEIGHT) 781*c2e18aaaSAndroid Build Coastguard Worker ) 782*c2e18aaaSAndroid Build Coastguard Worker return columns, rows 783*c2e18aaaSAndroid Build Coastguard Worker 784*c2e18aaaSAndroid Build Coastguard Worker 785*c2e18aaaSAndroid Build Coastguard Workerdef _get_hashed_file_name(main_file_name): 786*c2e18aaaSAndroid Build Coastguard Worker """Convert the input string to a md5-hashed string. 787*c2e18aaaSAndroid Build Coastguard Worker 788*c2e18aaaSAndroid Build Coastguard Worker If file_extension is 789*c2e18aaaSAndroid Build Coastguard Worker 790*c2e18aaaSAndroid Build Coastguard Worker given, returns $(hashed_string).$(file_extension), otherwise 791*c2e18aaaSAndroid Build Coastguard Worker $(hashed_string).cache. 792*c2e18aaaSAndroid Build Coastguard Worker 793*c2e18aaaSAndroid Build Coastguard Worker Args: 794*c2e18aaaSAndroid Build Coastguard Worker main_file_name: The input string need to be hashed. 795*c2e18aaaSAndroid Build Coastguard Worker 796*c2e18aaaSAndroid Build Coastguard Worker Returns: 797*c2e18aaaSAndroid Build Coastguard Worker A string as hashed file name with .cache file extension. 798*c2e18aaaSAndroid Build Coastguard Worker """ 799*c2e18aaaSAndroid Build Coastguard Worker hashed_fn = hashlib.md5(str(main_file_name).encode()) 800*c2e18aaaSAndroid Build Coastguard Worker hashed_name = hashed_fn.hexdigest() 801*c2e18aaaSAndroid Build Coastguard Worker return hashed_name + '.cache' 802*c2e18aaaSAndroid Build Coastguard Worker 803*c2e18aaaSAndroid Build Coastguard Worker 804*c2e18aaaSAndroid Build Coastguard Workerdef md5sum(filename): 805*c2e18aaaSAndroid Build Coastguard Worker """Generate MD5 checksum of a file. 806*c2e18aaaSAndroid Build Coastguard Worker 807*c2e18aaaSAndroid Build Coastguard Worker Args: 808*c2e18aaaSAndroid Build Coastguard Worker name: A string of a filename. 809*c2e18aaaSAndroid Build Coastguard Worker 810*c2e18aaaSAndroid Build Coastguard Worker Returns: 811*c2e18aaaSAndroid Build Coastguard Worker A string of hashed MD5 checksum. 812*c2e18aaaSAndroid Build Coastguard Worker """ 813*c2e18aaaSAndroid Build Coastguard Worker filename = Path(filename) 814*c2e18aaaSAndroid Build Coastguard Worker if not filename.is_file(): 815*c2e18aaaSAndroid Build Coastguard Worker return '' 816*c2e18aaaSAndroid Build Coastguard Worker with open(filename, 'rb') as target: 817*c2e18aaaSAndroid Build Coastguard Worker content = target.read() 818*c2e18aaaSAndroid Build Coastguard Worker if not isinstance(content, bytes): 819*c2e18aaaSAndroid Build Coastguard Worker content = content.encode('utf-8') 820*c2e18aaaSAndroid Build Coastguard Worker return hashlib.md5(content).hexdigest() 821*c2e18aaaSAndroid Build Coastguard Worker 822*c2e18aaaSAndroid Build Coastguard Worker 823*c2e18aaaSAndroid Build Coastguard Workerdef check_md5(check_file, missing_ok=False): 824*c2e18aaaSAndroid Build Coastguard Worker """Method equivalent to 'md5sum --check /file/to/check'. 825*c2e18aaaSAndroid Build Coastguard Worker 826*c2e18aaaSAndroid Build Coastguard Worker Args: 827*c2e18aaaSAndroid Build Coastguard Worker check_file: A string of filename that stores filename and its md5 828*c2e18aaaSAndroid Build Coastguard Worker checksum. 829*c2e18aaaSAndroid Build Coastguard Worker missing_ok: A boolean that considers OK even when the check_file does not 830*c2e18aaaSAndroid Build Coastguard Worker exist. Using missing_ok=True allows ignoring md5 check especially for 831*c2e18aaaSAndroid Build Coastguard Worker initial run that the check_file has not yet generated. Using 832*c2e18aaaSAndroid Build Coastguard Worker missing_ok=False ensures the consistency of files, and guarantees the 833*c2e18aaaSAndroid Build Coastguard Worker process is successfully completed. 834*c2e18aaaSAndroid Build Coastguard Worker 835*c2e18aaaSAndroid Build Coastguard Worker Returns: 836*c2e18aaaSAndroid Build Coastguard Worker When missing_ok is True (soft check): 837*c2e18aaaSAndroid Build Coastguard Worker - True if the checksum is consistent with the actual MD5, even the 838*c2e18aaaSAndroid Build Coastguard Worker check_file is missing or not a valid JSON. 839*c2e18aaaSAndroid Build Coastguard Worker - False when the checksum is inconsistent with the actual MD5. 840*c2e18aaaSAndroid Build Coastguard Worker When missing_ok is False (ensure the process completed properly): 841*c2e18aaaSAndroid Build Coastguard Worker - True if the checksum is consistent with the actual MD5. 842*c2e18aaaSAndroid Build Coastguard Worker - False otherwise. 843*c2e18aaaSAndroid Build Coastguard Worker """ 844*c2e18aaaSAndroid Build Coastguard Worker if not Path(check_file).is_file(): 845*c2e18aaaSAndroid Build Coastguard Worker if not missing_ok: 846*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Unable to verify: %s not found.', check_file) 847*c2e18aaaSAndroid Build Coastguard Worker return missing_ok 848*c2e18aaaSAndroid Build Coastguard Worker content = load_json_safely(check_file) 849*c2e18aaaSAndroid Build Coastguard Worker if content: 850*c2e18aaaSAndroid Build Coastguard Worker for filename, md5 in content.items(): 851*c2e18aaaSAndroid Build Coastguard Worker if md5sum(filename) != md5: 852*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s has altered.', filename) 853*c2e18aaaSAndroid Build Coastguard Worker return False 854*c2e18aaaSAndroid Build Coastguard Worker return True 855*c2e18aaaSAndroid Build Coastguard Worker return False 856*c2e18aaaSAndroid Build Coastguard Worker 857*c2e18aaaSAndroid Build Coastguard Worker 858*c2e18aaaSAndroid Build Coastguard Workerdef save_md5(filenames, save_file): 859*c2e18aaaSAndroid Build Coastguard Worker """Method equivalent to 'md5sum file1 file2 > /file/to/check' 860*c2e18aaaSAndroid Build Coastguard Worker 861*c2e18aaaSAndroid Build Coastguard Worker Args: 862*c2e18aaaSAndroid Build Coastguard Worker filenames: A list of filenames. 863*c2e18aaaSAndroid Build Coastguard Worker save_file: Filename for storing files and their md5 checksums. 864*c2e18aaaSAndroid Build Coastguard Worker """ 865*c2e18aaaSAndroid Build Coastguard Worker data = {} 866*c2e18aaaSAndroid Build Coastguard Worker for f in filenames: 867*c2e18aaaSAndroid Build Coastguard Worker name = Path(f) 868*c2e18aaaSAndroid Build Coastguard Worker if not name.is_file(): 869*c2e18aaaSAndroid Build Coastguard Worker print_and_log_warning(' ignore %s: not a file.', name) 870*c2e18aaaSAndroid Build Coastguard Worker data.update({str(name): md5sum(name)}) 871*c2e18aaaSAndroid Build Coastguard Worker with open(save_file, 'w+', encoding='utf-8') as _file: 872*c2e18aaaSAndroid Build Coastguard Worker json.dump(data, _file) 873*c2e18aaaSAndroid Build Coastguard Worker 874*c2e18aaaSAndroid Build Coastguard Worker 875*c2e18aaaSAndroid Build Coastguard Workerdef get_cache_root(): 876*c2e18aaaSAndroid Build Coastguard Worker """Get the root path dir for cache. 877*c2e18aaaSAndroid Build Coastguard Worker 878*c2e18aaaSAndroid Build Coastguard Worker Use branch and target information as cache_root. 879*c2e18aaaSAndroid Build Coastguard Worker The path will look like: 880*c2e18aaaSAndroid Build Coastguard Worker $(ANDROID_PRODUCT_OUT)/atest_cache/$CACHE_VERSION 881*c2e18aaaSAndroid Build Coastguard Worker 882*c2e18aaaSAndroid Build Coastguard Worker Returns: 883*c2e18aaaSAndroid Build Coastguard Worker A string of the path of the root dir of cache. 884*c2e18aaaSAndroid Build Coastguard Worker """ 885*c2e18aaaSAndroid Build Coastguard Worker # Note that the cache directory is stored in the build output directory. We 886*c2e18aaaSAndroid Build Coastguard Worker # do this because this directory is periodically cleaned and don't have to 887*c2e18aaaSAndroid Build Coastguard Worker # worry about the files growing without bound. The files are also much 888*c2e18aaaSAndroid Build Coastguard Worker # smaller than typical build output and less of an issue. Use build out to 889*c2e18aaaSAndroid Build Coastguard Worker # save caches which is next to atest_bazel_workspace which is easy for user 890*c2e18aaaSAndroid Build Coastguard Worker # to manually clean up if need. Use product out folder's base name as part 891*c2e18aaaSAndroid Build Coastguard Worker # of directory because of there may be different module-info in the same 892*c2e18aaaSAndroid Build Coastguard Worker # branch but different lunch target. 893*c2e18aaaSAndroid Build Coastguard Worker return os.path.join( 894*c2e18aaaSAndroid Build Coastguard Worker get_build_out_dir(), 895*c2e18aaaSAndroid Build Coastguard Worker 'atest_cache', 896*c2e18aaaSAndroid Build Coastguard Worker f'ver_{CACHE_VERSION}', 897*c2e18aaaSAndroid Build Coastguard Worker os.path.basename( 898*c2e18aaaSAndroid Build Coastguard Worker os.environ.get( 899*c2e18aaaSAndroid Build Coastguard Worker constants.ANDROID_PRODUCT_OUT, constants.ANDROID_PRODUCT_OUT 900*c2e18aaaSAndroid Build Coastguard Worker ) 901*c2e18aaaSAndroid Build Coastguard Worker ), 902*c2e18aaaSAndroid Build Coastguard Worker ) 903*c2e18aaaSAndroid Build Coastguard Worker 904*c2e18aaaSAndroid Build Coastguard Worker 905*c2e18aaaSAndroid Build Coastguard Workerdef get_test_info_cache_path(test_reference, cache_root=None): 906*c2e18aaaSAndroid Build Coastguard Worker """Get the cache path of the desired test_infos. 907*c2e18aaaSAndroid Build Coastguard Worker 908*c2e18aaaSAndroid Build Coastguard Worker Args: 909*c2e18aaaSAndroid Build Coastguard Worker test_reference: A string of the test. 910*c2e18aaaSAndroid Build Coastguard Worker cache_root: Folder path where stores caches. 911*c2e18aaaSAndroid Build Coastguard Worker 912*c2e18aaaSAndroid Build Coastguard Worker Returns: 913*c2e18aaaSAndroid Build Coastguard Worker A string of the path of test_info cache. 914*c2e18aaaSAndroid Build Coastguard Worker """ 915*c2e18aaaSAndroid Build Coastguard Worker if not cache_root: 916*c2e18aaaSAndroid Build Coastguard Worker cache_root = get_cache_root() 917*c2e18aaaSAndroid Build Coastguard Worker return os.path.join(cache_root, _get_hashed_file_name(test_reference)) 918*c2e18aaaSAndroid Build Coastguard Worker 919*c2e18aaaSAndroid Build Coastguard Worker 920*c2e18aaaSAndroid Build Coastguard Workerdef update_test_info_cache(test_reference, test_infos, cache_root=None): 921*c2e18aaaSAndroid Build Coastguard Worker """Update cache content which stores a set of test_info objects through 922*c2e18aaaSAndroid Build Coastguard Worker 923*c2e18aaaSAndroid Build Coastguard Worker pickle module, each test_reference will be saved as a cache file. 924*c2e18aaaSAndroid Build Coastguard Worker 925*c2e18aaaSAndroid Build Coastguard Worker Args: 926*c2e18aaaSAndroid Build Coastguard Worker test_reference: A string referencing a test. 927*c2e18aaaSAndroid Build Coastguard Worker test_infos: A set of TestInfos. 928*c2e18aaaSAndroid Build Coastguard Worker cache_root: Folder path for saving caches. 929*c2e18aaaSAndroid Build Coastguard Worker """ 930*c2e18aaaSAndroid Build Coastguard Worker if not cache_root: 931*c2e18aaaSAndroid Build Coastguard Worker cache_root = get_cache_root() 932*c2e18aaaSAndroid Build Coastguard Worker if not os.path.isdir(cache_root): 933*c2e18aaaSAndroid Build Coastguard Worker os.makedirs(cache_root) 934*c2e18aaaSAndroid Build Coastguard Worker cache_path = get_test_info_cache_path(test_reference, cache_root) 935*c2e18aaaSAndroid Build Coastguard Worker # Save test_info to files. 936*c2e18aaaSAndroid Build Coastguard Worker try: 937*c2e18aaaSAndroid Build Coastguard Worker with open(cache_path, 'wb') as test_info_cache_file: 938*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Saving cache for %s as %s.', test_reference, cache_path) 939*c2e18aaaSAndroid Build Coastguard Worker pickle.dump(test_infos, test_info_cache_file, protocol=2) 940*c2e18aaaSAndroid Build Coastguard Worker except (pickle.PicklingError, TypeError, IOError) as err: 941*c2e18aaaSAndroid Build Coastguard Worker # Won't break anything, just log this error, and collect the exception 942*c2e18aaaSAndroid Build Coastguard Worker # by metrics. 943*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Exception raised: %s', err) 944*c2e18aaaSAndroid Build Coastguard Worker metrics_utils.handle_exc_and_send_exit_event(constants.ACCESS_CACHE_FAILURE) 945*c2e18aaaSAndroid Build Coastguard Worker 946*c2e18aaaSAndroid Build Coastguard Worker 947*c2e18aaaSAndroid Build Coastguard Workerdef load_test_info_cache(test_reference, cache_root=None): 948*c2e18aaaSAndroid Build Coastguard Worker """Load cache by test_reference to a set of test_infos object. 949*c2e18aaaSAndroid Build Coastguard Worker 950*c2e18aaaSAndroid Build Coastguard Worker Args: 951*c2e18aaaSAndroid Build Coastguard Worker test_reference: A string referencing a test. 952*c2e18aaaSAndroid Build Coastguard Worker cache_root: Folder path for finding caches. 953*c2e18aaaSAndroid Build Coastguard Worker 954*c2e18aaaSAndroid Build Coastguard Worker Returns: 955*c2e18aaaSAndroid Build Coastguard Worker A list of TestInfo namedtuple if cache found, else None. 956*c2e18aaaSAndroid Build Coastguard Worker """ 957*c2e18aaaSAndroid Build Coastguard Worker if not cache_root: 958*c2e18aaaSAndroid Build Coastguard Worker cache_root = get_cache_root() 959*c2e18aaaSAndroid Build Coastguard Worker 960*c2e18aaaSAndroid Build Coastguard Worker cache_file = get_test_info_cache_path(test_reference, cache_root) 961*c2e18aaaSAndroid Build Coastguard Worker if os.path.isfile(cache_file): 962*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Loading cache %s from %s.', test_reference, cache_file) 963*c2e18aaaSAndroid Build Coastguard Worker try: 964*c2e18aaaSAndroid Build Coastguard Worker with open(cache_file, 'rb') as config_dictionary_file: 965*c2e18aaaSAndroid Build Coastguard Worker return pickle.load(config_dictionary_file, encoding='utf-8') 966*c2e18aaaSAndroid Build Coastguard Worker except ( 967*c2e18aaaSAndroid Build Coastguard Worker pickle.UnpicklingError, 968*c2e18aaaSAndroid Build Coastguard Worker ValueError, 969*c2e18aaaSAndroid Build Coastguard Worker TypeError, 970*c2e18aaaSAndroid Build Coastguard Worker EOFError, 971*c2e18aaaSAndroid Build Coastguard Worker IOError, 972*c2e18aaaSAndroid Build Coastguard Worker ImportError, 973*c2e18aaaSAndroid Build Coastguard Worker ) as err: 974*c2e18aaaSAndroid Build Coastguard Worker # Won't break anything, just remove the old cache, log this error, 975*c2e18aaaSAndroid Build Coastguard Worker # and collect the exception by metrics. 976*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Exception raised: %s', err) 977*c2e18aaaSAndroid Build Coastguard Worker os.remove(cache_file) 978*c2e18aaaSAndroid Build Coastguard Worker metrics_utils.handle_exc_and_send_exit_event( 979*c2e18aaaSAndroid Build Coastguard Worker constants.ACCESS_CACHE_FAILURE 980*c2e18aaaSAndroid Build Coastguard Worker ) 981*c2e18aaaSAndroid Build Coastguard Worker return None 982*c2e18aaaSAndroid Build Coastguard Worker 983*c2e18aaaSAndroid Build Coastguard Worker 984*c2e18aaaSAndroid Build Coastguard Workerdef clean_test_info_caches(tests, cache_root=None): 985*c2e18aaaSAndroid Build Coastguard Worker """Clean caches of input tests. 986*c2e18aaaSAndroid Build Coastguard Worker 987*c2e18aaaSAndroid Build Coastguard Worker Args: 988*c2e18aaaSAndroid Build Coastguard Worker tests: A list of test references. 989*c2e18aaaSAndroid Build Coastguard Worker cache_root: Folder path for finding caches. 990*c2e18aaaSAndroid Build Coastguard Worker """ 991*c2e18aaaSAndroid Build Coastguard Worker if not cache_root: 992*c2e18aaaSAndroid Build Coastguard Worker cache_root = get_cache_root() 993*c2e18aaaSAndroid Build Coastguard Worker for test in tests: 994*c2e18aaaSAndroid Build Coastguard Worker cache_file = get_test_info_cache_path(test, cache_root) 995*c2e18aaaSAndroid Build Coastguard Worker if os.path.isfile(cache_file): 996*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Removing cache: %s', cache_file) 997*c2e18aaaSAndroid Build Coastguard Worker try: 998*c2e18aaaSAndroid Build Coastguard Worker os.remove(cache_file) 999*c2e18aaaSAndroid Build Coastguard Worker except IOError as err: 1000*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Exception raised: %s', err) 1001*c2e18aaaSAndroid Build Coastguard Worker metrics_utils.handle_exc_and_send_exit_event( 1002*c2e18aaaSAndroid Build Coastguard Worker constants.ACCESS_CACHE_FAILURE 1003*c2e18aaaSAndroid Build Coastguard Worker ) 1004*c2e18aaaSAndroid Build Coastguard Worker 1005*c2e18aaaSAndroid Build Coastguard Worker 1006*c2e18aaaSAndroid Build Coastguard Workerdef get_modified_files(root_dir): 1007*c2e18aaaSAndroid Build Coastguard Worker """Get the git modified files. 1008*c2e18aaaSAndroid Build Coastguard Worker 1009*c2e18aaaSAndroid Build Coastguard Worker The git path here is git top level of the root_dir. It's inevitable to utilise 1010*c2e18aaaSAndroid Build Coastguard Worker different commands to fulfill 2 scenario: 1011*c2e18aaaSAndroid Build Coastguard Worker 1012*c2e18aaaSAndroid Build Coastguard Worker 1. locate unstaged/staged files 1013*c2e18aaaSAndroid Build Coastguard Worker 2. locate committed files but not yet merged. 1014*c2e18aaaSAndroid Build Coastguard Worker the 'git_status_cmd' fulfils the former while the 'find_modified_files' 1015*c2e18aaaSAndroid Build Coastguard Worker fulfils the latter. 1016*c2e18aaaSAndroid Build Coastguard Worker 1017*c2e18aaaSAndroid Build Coastguard Worker Args: 1018*c2e18aaaSAndroid Build Coastguard Worker root_dir: the root where it starts finding. 1019*c2e18aaaSAndroid Build Coastguard Worker 1020*c2e18aaaSAndroid Build Coastguard Worker Returns: 1021*c2e18aaaSAndroid Build Coastguard Worker A set of modified files altered since last commit. 1022*c2e18aaaSAndroid Build Coastguard Worker """ 1023*c2e18aaaSAndroid Build Coastguard Worker modified_files = set() 1024*c2e18aaaSAndroid Build Coastguard Worker try: 1025*c2e18aaaSAndroid Build Coastguard Worker # TODO: (@jimtang) abandon using git command within Atest. 1026*c2e18aaaSAndroid Build Coastguard Worker find_git_cmd = f'cd {root_dir}; git rev-parse --show-toplevel 2>/dev/null' 1027*c2e18aaaSAndroid Build Coastguard Worker git_paths = ( 1028*c2e18aaaSAndroid Build Coastguard Worker subprocess.check_output(find_git_cmd, shell=True).decode().splitlines() 1029*c2e18aaaSAndroid Build Coastguard Worker ) 1030*c2e18aaaSAndroid Build Coastguard Worker for git_path in git_paths: 1031*c2e18aaaSAndroid Build Coastguard Worker # Find modified files from git working tree status. 1032*c2e18aaaSAndroid Build Coastguard Worker git_status_cmd = ( 1033*c2e18aaaSAndroid Build Coastguard Worker "repo forall {} -c git status --short | awk '{{print $NF}}'" 1034*c2e18aaaSAndroid Build Coastguard Worker ).format(git_path) 1035*c2e18aaaSAndroid Build Coastguard Worker modified_wo_commit = ( 1036*c2e18aaaSAndroid Build Coastguard Worker subprocess.check_output(git_status_cmd, shell=True) 1037*c2e18aaaSAndroid Build Coastguard Worker .decode() 1038*c2e18aaaSAndroid Build Coastguard Worker .rstrip() 1039*c2e18aaaSAndroid Build Coastguard Worker .splitlines() 1040*c2e18aaaSAndroid Build Coastguard Worker ) 1041*c2e18aaaSAndroid Build Coastguard Worker for change in modified_wo_commit: 1042*c2e18aaaSAndroid Build Coastguard Worker modified_files.add(os.path.normpath('{}/{}'.format(git_path, change))) 1043*c2e18aaaSAndroid Build Coastguard Worker # Find modified files that are committed but not yet merged. 1044*c2e18aaaSAndroid Build Coastguard Worker find_modified_files = _FIND_MODIFIED_FILES_CMDS.format(git_path) 1045*c2e18aaaSAndroid Build Coastguard Worker commit_modified_files = ( 1046*c2e18aaaSAndroid Build Coastguard Worker subprocess.check_output(find_modified_files, shell=True) 1047*c2e18aaaSAndroid Build Coastguard Worker .decode() 1048*c2e18aaaSAndroid Build Coastguard Worker .splitlines() 1049*c2e18aaaSAndroid Build Coastguard Worker ) 1050*c2e18aaaSAndroid Build Coastguard Worker for line in commit_modified_files: 1051*c2e18aaaSAndroid Build Coastguard Worker modified_files.add(os.path.normpath('{}/{}'.format(git_path, line))) 1052*c2e18aaaSAndroid Build Coastguard Worker except (OSError, subprocess.CalledProcessError) as err: 1053*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Exception raised: %s', err) 1054*c2e18aaaSAndroid Build Coastguard Worker return modified_files 1055*c2e18aaaSAndroid Build Coastguard Worker 1056*c2e18aaaSAndroid Build Coastguard Worker 1057*c2e18aaaSAndroid Build Coastguard Workerdef delimiter(char, length=_DEFAULT_TERMINAL_WIDTH, prenl=0, postnl=0): 1058*c2e18aaaSAndroid Build Coastguard Worker """A handy delimiter printer. 1059*c2e18aaaSAndroid Build Coastguard Worker 1060*c2e18aaaSAndroid Build Coastguard Worker Args: 1061*c2e18aaaSAndroid Build Coastguard Worker char: A string used for delimiter. 1062*c2e18aaaSAndroid Build Coastguard Worker length: An integer for the replication. 1063*c2e18aaaSAndroid Build Coastguard Worker prenl: An integer that insert '\n' before delimiter. 1064*c2e18aaaSAndroid Build Coastguard Worker postnl: An integer that insert '\n' after delimiter. 1065*c2e18aaaSAndroid Build Coastguard Worker 1066*c2e18aaaSAndroid Build Coastguard Worker Returns: 1067*c2e18aaaSAndroid Build Coastguard Worker A string of delimiter. 1068*c2e18aaaSAndroid Build Coastguard Worker """ 1069*c2e18aaaSAndroid Build Coastguard Worker return prenl * '\n' + char * length + postnl * '\n' 1070*c2e18aaaSAndroid Build Coastguard Worker 1071*c2e18aaaSAndroid Build Coastguard Worker 1072*c2e18aaaSAndroid Build Coastguard Workerdef find_files(path, file_name=constants.TEST_MAPPING, followlinks=False): 1073*c2e18aaaSAndroid Build Coastguard Worker """Find all files with given name under the given path. 1074*c2e18aaaSAndroid Build Coastguard Worker 1075*c2e18aaaSAndroid Build Coastguard Worker Args: 1076*c2e18aaaSAndroid Build Coastguard Worker path: A string of path in source. 1077*c2e18aaaSAndroid Build Coastguard Worker file_name: The file name pattern for finding matched files. 1078*c2e18aaaSAndroid Build Coastguard Worker followlinks: A boolean to indicate whether to follow symbolic links. 1079*c2e18aaaSAndroid Build Coastguard Worker 1080*c2e18aaaSAndroid Build Coastguard Worker Returns: 1081*c2e18aaaSAndroid Build Coastguard Worker A list of paths of the files with the matching name under the given 1082*c2e18aaaSAndroid Build Coastguard Worker path. 1083*c2e18aaaSAndroid Build Coastguard Worker """ 1084*c2e18aaaSAndroid Build Coastguard Worker match_files = [] 1085*c2e18aaaSAndroid Build Coastguard Worker for root, _, filenames in os.walk(path, followlinks=followlinks): 1086*c2e18aaaSAndroid Build Coastguard Worker try: 1087*c2e18aaaSAndroid Build Coastguard Worker for filename in fnmatch.filter(filenames, file_name): 1088*c2e18aaaSAndroid Build Coastguard Worker match_files.append(os.path.join(root, filename)) 1089*c2e18aaaSAndroid Build Coastguard Worker except re.error as e: 1090*c2e18aaaSAndroid Build Coastguard Worker msg = 'Unable to locate %s among %s' % (file_name, filenames) 1091*c2e18aaaSAndroid Build Coastguard Worker logging.debug(msg) 1092*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Exception: %s', e) 1093*c2e18aaaSAndroid Build Coastguard Worker metrics.AtestExitEvent( 1094*c2e18aaaSAndroid Build Coastguard Worker duration=metrics_utils.convert_duration(0), 1095*c2e18aaaSAndroid Build Coastguard Worker exit_code=ExitCode.COLLECT_ONLY_FILE_NOT_FOUND, 1096*c2e18aaaSAndroid Build Coastguard Worker stacktrace=msg, 1097*c2e18aaaSAndroid Build Coastguard Worker logs=str(e), 1098*c2e18aaaSAndroid Build Coastguard Worker ) 1099*c2e18aaaSAndroid Build Coastguard Worker return match_files 1100*c2e18aaaSAndroid Build Coastguard Worker 1101*c2e18aaaSAndroid Build Coastguard Worker 1102*c2e18aaaSAndroid Build Coastguard Workerdef extract_zip_text(zip_path): 1103*c2e18aaaSAndroid Build Coastguard Worker """Extract the text files content for input zip file. 1104*c2e18aaaSAndroid Build Coastguard Worker 1105*c2e18aaaSAndroid Build Coastguard Worker Args: 1106*c2e18aaaSAndroid Build Coastguard Worker zip_path: The file path of zip. 1107*c2e18aaaSAndroid Build Coastguard Worker 1108*c2e18aaaSAndroid Build Coastguard Worker Returns: 1109*c2e18aaaSAndroid Build Coastguard Worker The string in input zip file. 1110*c2e18aaaSAndroid Build Coastguard Worker """ 1111*c2e18aaaSAndroid Build Coastguard Worker content = '' 1112*c2e18aaaSAndroid Build Coastguard Worker try: 1113*c2e18aaaSAndroid Build Coastguard Worker with zipfile.ZipFile(zip_path) as zip_file: 1114*c2e18aaaSAndroid Build Coastguard Worker for filename in zip_file.namelist(): 1115*c2e18aaaSAndroid Build Coastguard Worker if os.path.isdir(filename): 1116*c2e18aaaSAndroid Build Coastguard Worker continue 1117*c2e18aaaSAndroid Build Coastguard Worker # Force change line if multiple text files in zip 1118*c2e18aaaSAndroid Build Coastguard Worker content = content + '\n' 1119*c2e18aaaSAndroid Build Coastguard Worker # read the file 1120*c2e18aaaSAndroid Build Coastguard Worker with zip_file.open(filename) as extract_file: 1121*c2e18aaaSAndroid Build Coastguard Worker for line in extract_file: 1122*c2e18aaaSAndroid Build Coastguard Worker if matched_tf_error_log(line.decode()): 1123*c2e18aaaSAndroid Build Coastguard Worker content = content + line.decode() 1124*c2e18aaaSAndroid Build Coastguard Worker except zipfile.BadZipfile as err: 1125*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Exception raised: %s', err) 1126*c2e18aaaSAndroid Build Coastguard Worker return content 1127*c2e18aaaSAndroid Build Coastguard Worker 1128*c2e18aaaSAndroid Build Coastguard Worker 1129*c2e18aaaSAndroid Build Coastguard Workerdef matched_tf_error_log(content): 1130*c2e18aaaSAndroid Build Coastguard Worker """Check if the input content matched tradefed log pattern. 1131*c2e18aaaSAndroid Build Coastguard Worker 1132*c2e18aaaSAndroid Build Coastguard Worker The format will look like this. 05-25 17:37:04 W/XXXXXX 05-25 17:37:04 1133*c2e18aaaSAndroid Build Coastguard Worker E/XXXXXX 1134*c2e18aaaSAndroid Build Coastguard Worker 1135*c2e18aaaSAndroid Build Coastguard Worker Args: 1136*c2e18aaaSAndroid Build Coastguard Worker content: Log string. 1137*c2e18aaaSAndroid Build Coastguard Worker 1138*c2e18aaaSAndroid Build Coastguard Worker Returns: 1139*c2e18aaaSAndroid Build Coastguard Worker True if the content matches the regular expression for tradefed error or 1140*c2e18aaaSAndroid Build Coastguard Worker warning log. 1141*c2e18aaaSAndroid Build Coastguard Worker """ 1142*c2e18aaaSAndroid Build Coastguard Worker reg = ( 1143*c2e18aaaSAndroid Build Coastguard Worker '^((0[1-9])|(1[0-2]))-((0[1-9])|([12][0-9])|(3[0-1])) ' 1144*c2e18aaaSAndroid Build Coastguard Worker '(([0-1][0-9])|([2][0-3])):([0-5][0-9]):([0-5][0-9]) (E|W/)' 1145*c2e18aaaSAndroid Build Coastguard Worker ) 1146*c2e18aaaSAndroid Build Coastguard Worker if re.search(reg, content): 1147*c2e18aaaSAndroid Build Coastguard Worker return True 1148*c2e18aaaSAndroid Build Coastguard Worker return False 1149*c2e18aaaSAndroid Build Coastguard Worker 1150*c2e18aaaSAndroid Build Coastguard Worker 1151*c2e18aaaSAndroid Build Coastguard Workerdef read_test_record(path): 1152*c2e18aaaSAndroid Build Coastguard Worker """A Helper to read test record proto. 1153*c2e18aaaSAndroid Build Coastguard Worker 1154*c2e18aaaSAndroid Build Coastguard Worker Args: 1155*c2e18aaaSAndroid Build Coastguard Worker path: The proto file path. 1156*c2e18aaaSAndroid Build Coastguard Worker 1157*c2e18aaaSAndroid Build Coastguard Worker Returns: 1158*c2e18aaaSAndroid Build Coastguard Worker The test_record proto instance. 1159*c2e18aaaSAndroid Build Coastguard Worker """ 1160*c2e18aaaSAndroid Build Coastguard Worker with open(path, 'rb') as proto_file: 1161*c2e18aaaSAndroid Build Coastguard Worker msg = test_record_pb2.TestRecord() 1162*c2e18aaaSAndroid Build Coastguard Worker msg.ParseFromString(proto_file.read()) 1163*c2e18aaaSAndroid Build Coastguard Worker return msg 1164*c2e18aaaSAndroid Build Coastguard Worker 1165*c2e18aaaSAndroid Build Coastguard Worker 1166*c2e18aaaSAndroid Build Coastguard Workerdef has_python_module(module_name): 1167*c2e18aaaSAndroid Build Coastguard Worker """Detect if the module can be loaded without importing it in real. 1168*c2e18aaaSAndroid Build Coastguard Worker 1169*c2e18aaaSAndroid Build Coastguard Worker Args: 1170*c2e18aaaSAndroid Build Coastguard Worker cmd: A string of the tested module name. 1171*c2e18aaaSAndroid Build Coastguard Worker 1172*c2e18aaaSAndroid Build Coastguard Worker Returns: 1173*c2e18aaaSAndroid Build Coastguard Worker True if found, False otherwise. 1174*c2e18aaaSAndroid Build Coastguard Worker """ 1175*c2e18aaaSAndroid Build Coastguard Worker return bool(importlib.util.find_spec(module_name)) 1176*c2e18aaaSAndroid Build Coastguard Worker 1177*c2e18aaaSAndroid Build Coastguard Worker 1178*c2e18aaaSAndroid Build Coastguard Workerdef load_json_safely(jsonfile): 1179*c2e18aaaSAndroid Build Coastguard Worker """Load the given json file as an object. 1180*c2e18aaaSAndroid Build Coastguard Worker 1181*c2e18aaaSAndroid Build Coastguard Worker Args: 1182*c2e18aaaSAndroid Build Coastguard Worker jsonfile: The json file path. 1183*c2e18aaaSAndroid Build Coastguard Worker 1184*c2e18aaaSAndroid Build Coastguard Worker Returns: 1185*c2e18aaaSAndroid Build Coastguard Worker The content of the give json file. Null dict when: 1186*c2e18aaaSAndroid Build Coastguard Worker 1. the given path doesn't exist. 1187*c2e18aaaSAndroid Build Coastguard Worker 2. the given path is not a json or invalid format. 1188*c2e18aaaSAndroid Build Coastguard Worker """ 1189*c2e18aaaSAndroid Build Coastguard Worker if isinstance(jsonfile, bytes): 1190*c2e18aaaSAndroid Build Coastguard Worker jsonfile = jsonfile.decode('utf-8') 1191*c2e18aaaSAndroid Build Coastguard Worker if Path(jsonfile).is_file(): 1192*c2e18aaaSAndroid Build Coastguard Worker try: 1193*c2e18aaaSAndroid Build Coastguard Worker with open(jsonfile, 'r', encoding='utf-8') as cache: 1194*c2e18aaaSAndroid Build Coastguard Worker return json.load(cache) 1195*c2e18aaaSAndroid Build Coastguard Worker except json.JSONDecodeError: 1196*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Exception happened while loading %s.', jsonfile) 1197*c2e18aaaSAndroid Build Coastguard Worker else: 1198*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s: File not found.', jsonfile) 1199*c2e18aaaSAndroid Build Coastguard Worker return {} 1200*c2e18aaaSAndroid Build Coastguard Worker 1201*c2e18aaaSAndroid Build Coastguard Worker 1202*c2e18aaaSAndroid Build Coastguard Workerdef get_atest_version(): 1203*c2e18aaaSAndroid Build Coastguard Worker """Get atest version. 1204*c2e18aaaSAndroid Build Coastguard Worker 1205*c2e18aaaSAndroid Build Coastguard Worker Returns: 1206*c2e18aaaSAndroid Build Coastguard Worker Version string from the VERSION file, e.g. prebuilt 1207*c2e18aaaSAndroid Build Coastguard Worker 2022-11-24_9314547 (<release_date>_<build_id>) 1208*c2e18aaaSAndroid Build Coastguard Worker 1209*c2e18aaaSAndroid Build Coastguard Worker If VERSION does not exist (src or local built): 1210*c2e18aaaSAndroid Build Coastguard Worker 2022-11-24_5d448c50 (<commit_date>_<commit_id>) 1211*c2e18aaaSAndroid Build Coastguard Worker 1212*c2e18aaaSAndroid Build Coastguard Worker If the git command fails for unexpected reason: 1213*c2e18aaaSAndroid Build Coastguard Worker 2022-11-24_unknown (<today_date>_unknown) 1214*c2e18aaaSAndroid Build Coastguard Worker """ 1215*c2e18aaaSAndroid Build Coastguard Worker try: 1216*c2e18aaaSAndroid Build Coastguard Worker with importlib.resources.as_file( 1217*c2e18aaaSAndroid Build Coastguard Worker importlib.resources.files('atest').joinpath('VERSION') 1218*c2e18aaaSAndroid Build Coastguard Worker ) as version_file_path: 1219*c2e18aaaSAndroid Build Coastguard Worker return version_file_path.read_text(encoding='utf-8') 1220*c2e18aaaSAndroid Build Coastguard Worker except (ModuleNotFoundError, FileNotFoundError): 1221*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 1222*c2e18aaaSAndroid Build Coastguard Worker 'Failed to load package resource atest/VERSION, possibly due to running' 1223*c2e18aaaSAndroid Build Coastguard Worker ' from atest-dev, atest-src, a prebuilt without embedded launcher, or a' 1224*c2e18aaaSAndroid Build Coastguard Worker ' prebuilt not created by the asuite release tool. Falling back to' 1225*c2e18aaaSAndroid Build Coastguard Worker ' legacy source search.' 1226*c2e18aaaSAndroid Build Coastguard Worker ) 1227*c2e18aaaSAndroid Build Coastguard Worker version_file = Path(__file__).resolve().parent.joinpath('VERSION') 1228*c2e18aaaSAndroid Build Coastguard Worker if Path(version_file).is_file(): 1229*c2e18aaaSAndroid Build Coastguard Worker return open(version_file, encoding='utf-8').read() 1230*c2e18aaaSAndroid Build Coastguard Worker 1231*c2e18aaaSAndroid Build Coastguard Worker # Try fetching commit date (%ci) and commit hash (%h). 1232*c2e18aaaSAndroid Build Coastguard Worker git_cmd = 'git log -1 --pretty=format:"%ci;%h"' 1233*c2e18aaaSAndroid Build Coastguard Worker try: 1234*c2e18aaaSAndroid Build Coastguard Worker # commit date/hash are only available when running from the source 1235*c2e18aaaSAndroid Build Coastguard Worker # and the local built. 1236*c2e18aaaSAndroid Build Coastguard Worker result = subprocess.run( 1237*c2e18aaaSAndroid Build Coastguard Worker git_cmd, 1238*c2e18aaaSAndroid Build Coastguard Worker shell=True, 1239*c2e18aaaSAndroid Build Coastguard Worker check=False, 1240*c2e18aaaSAndroid Build Coastguard Worker capture_output=True, 1241*c2e18aaaSAndroid Build Coastguard Worker cwd=Path(os.getenv(constants.ANDROID_BUILD_TOP), '').joinpath( 1242*c2e18aaaSAndroid Build Coastguard Worker 'tools/asuite/atest' 1243*c2e18aaaSAndroid Build Coastguard Worker ), 1244*c2e18aaaSAndroid Build Coastguard Worker ) 1245*c2e18aaaSAndroid Build Coastguard Worker if result.stderr: 1246*c2e18aaaSAndroid Build Coastguard Worker raise subprocess.CalledProcessError(returncode=0, cmd=git_cmd) 1247*c2e18aaaSAndroid Build Coastguard Worker raw_date, commit = result.stdout.decode().split(';') 1248*c2e18aaaSAndroid Build Coastguard Worker date = datetime.datetime.strptime(raw_date, '%Y-%m-%d %H:%M:%S %z').date() 1249*c2e18aaaSAndroid Build Coastguard Worker # atest_dir doesn't exist will throw FileNotFoundError. 1250*c2e18aaaSAndroid Build Coastguard Worker except (subprocess.CalledProcessError, FileNotFoundError): 1251*c2e18aaaSAndroid Build Coastguard Worker # Use today as the commit date for unexpected conditions. 1252*c2e18aaaSAndroid Build Coastguard Worker date = datetime.datetime.today().date() 1253*c2e18aaaSAndroid Build Coastguard Worker commit = 'unknown' 1254*c2e18aaaSAndroid Build Coastguard Worker return f'{date}_{commit}' 1255*c2e18aaaSAndroid Build Coastguard Worker 1256*c2e18aaaSAndroid Build Coastguard Worker 1257*c2e18aaaSAndroid Build Coastguard Workerdef get_manifest_branch(show_aosp=False): 1258*c2e18aaaSAndroid Build Coastguard Worker """Get the manifest branch. 1259*c2e18aaaSAndroid Build Coastguard Worker 1260*c2e18aaaSAndroid Build Coastguard Worker Args: 1261*c2e18aaaSAndroid Build Coastguard Worker show_aosp: A boolean that shows 'aosp' prefix by checking the 'remote' 1262*c2e18aaaSAndroid Build Coastguard Worker attribute. 1263*c2e18aaaSAndroid Build Coastguard Worker 1264*c2e18aaaSAndroid Build Coastguard Worker Returns: 1265*c2e18aaaSAndroid Build Coastguard Worker The value of 'revision' of the included xml or default.xml. 1266*c2e18aaaSAndroid Build Coastguard Worker 1267*c2e18aaaSAndroid Build Coastguard Worker None when no ANDROID_BUILD_TOP or unable to access default.xml. 1268*c2e18aaaSAndroid Build Coastguard Worker """ 1269*c2e18aaaSAndroid Build Coastguard Worker # (portal xml) (default xml) 1270*c2e18aaaSAndroid Build Coastguard Worker # +--------------------+ _get_include() +-----------------------------+ 1271*c2e18aaaSAndroid Build Coastguard Worker # | .repo/manifest.xml |--------------->| .repo/manifests/default.xml | 1272*c2e18aaaSAndroid Build Coastguard Worker # +--------------------+ +---------------+-------------+ 1273*c2e18aaaSAndroid Build Coastguard Worker # <default revision="master" | 1274*c2e18aaaSAndroid Build Coastguard Worker # remote="aosp" | _get_revision() 1275*c2e18aaaSAndroid Build Coastguard Worker # sync-j="4"/> V 1276*c2e18aaaSAndroid Build Coastguard Worker # +--------+ 1277*c2e18aaaSAndroid Build Coastguard Worker # | master | 1278*c2e18aaaSAndroid Build Coastguard Worker # +--------+ 1279*c2e18aaaSAndroid Build Coastguard Worker build_top = os.getenv(constants.ANDROID_BUILD_TOP) 1280*c2e18aaaSAndroid Build Coastguard Worker if not build_top: 1281*c2e18aaaSAndroid Build Coastguard Worker return None 1282*c2e18aaaSAndroid Build Coastguard Worker portal_xml = Path(build_top).joinpath('.repo', 'manifest.xml') 1283*c2e18aaaSAndroid Build Coastguard Worker default_xml = Path(build_top).joinpath('.repo/manifests', 'default.xml') 1284*c2e18aaaSAndroid Build Coastguard Worker 1285*c2e18aaaSAndroid Build Coastguard Worker def _get_revision(xml): 1286*c2e18aaaSAndroid Build Coastguard Worker try: 1287*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(xml).getroot() 1288*c2e18aaaSAndroid Build Coastguard Worker except (IOError, OSError, ET.ParseError): 1289*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/274989179) Change back to warning once warning if not going 1290*c2e18aaaSAndroid Build Coastguard Worker # to be treat as test failure. Or test_get_manifest_branch unit test 1291*c2e18aaaSAndroid Build Coastguard Worker # could be fix if return None if portal_xml or default_xml not 1292*c2e18aaaSAndroid Build Coastguard Worker # exist. 1293*c2e18aaaSAndroid Build Coastguard Worker logging.info('%s could not be read.', xml) 1294*c2e18aaaSAndroid Build Coastguard Worker return '' 1295*c2e18aaaSAndroid Build Coastguard Worker default_tags = xml_root.findall('./default') 1296*c2e18aaaSAndroid Build Coastguard Worker if default_tags: 1297*c2e18aaaSAndroid Build Coastguard Worker prefix = '' 1298*c2e18aaaSAndroid Build Coastguard Worker for tag in default_tags: 1299*c2e18aaaSAndroid Build Coastguard Worker branch = tag.attrib.get('revision') 1300*c2e18aaaSAndroid Build Coastguard Worker if show_aosp and tag.attrib.get('remote') == 'aosp': 1301*c2e18aaaSAndroid Build Coastguard Worker prefix = 'aosp-' 1302*c2e18aaaSAndroid Build Coastguard Worker return f'{prefix}{branch}' 1303*c2e18aaaSAndroid Build Coastguard Worker return '' 1304*c2e18aaaSAndroid Build Coastguard Worker 1305*c2e18aaaSAndroid Build Coastguard Worker def _get_include(xml): 1306*c2e18aaaSAndroid Build Coastguard Worker try: 1307*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(xml).getroot() 1308*c2e18aaaSAndroid Build Coastguard Worker except (IOError, OSError, ET.ParseError): 1309*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/274989179) Change back to warning once warning if not going 1310*c2e18aaaSAndroid Build Coastguard Worker # to be treat as test failure. Or test_get_manifest_branch unit test 1311*c2e18aaaSAndroid Build Coastguard Worker # could be fix if return None if portal_xml or default_xml not 1312*c2e18aaaSAndroid Build Coastguard Worker # exist. 1313*c2e18aaaSAndroid Build Coastguard Worker logging.info('%s could not be read.', xml) 1314*c2e18aaaSAndroid Build Coastguard Worker return Path() 1315*c2e18aaaSAndroid Build Coastguard Worker include_tags = xml_root.findall('./include') 1316*c2e18aaaSAndroid Build Coastguard Worker if include_tags: 1317*c2e18aaaSAndroid Build Coastguard Worker for tag in include_tags: 1318*c2e18aaaSAndroid Build Coastguard Worker name = tag.attrib.get('name') 1319*c2e18aaaSAndroid Build Coastguard Worker if name: 1320*c2e18aaaSAndroid Build Coastguard Worker return Path(build_top).joinpath('.repo/manifests', name) 1321*c2e18aaaSAndroid Build Coastguard Worker return default_xml 1322*c2e18aaaSAndroid Build Coastguard Worker 1323*c2e18aaaSAndroid Build Coastguard Worker # 1. Try getting revision from .repo/manifests/default.xml 1324*c2e18aaaSAndroid Build Coastguard Worker if default_xml.is_file(): 1325*c2e18aaaSAndroid Build Coastguard Worker return _get_revision(default_xml) 1326*c2e18aaaSAndroid Build Coastguard Worker # 2. Try getting revision from the included xml of .repo/manifest.xml 1327*c2e18aaaSAndroid Build Coastguard Worker include_xml = _get_include(portal_xml) 1328*c2e18aaaSAndroid Build Coastguard Worker if include_xml.is_file(): 1329*c2e18aaaSAndroid Build Coastguard Worker return _get_revision(include_xml) 1330*c2e18aaaSAndroid Build Coastguard Worker # 3. Try getting revision directly from manifest.xml (unlikely to happen) 1331*c2e18aaaSAndroid Build Coastguard Worker return _get_revision(portal_xml) 1332*c2e18aaaSAndroid Build Coastguard Worker 1333*c2e18aaaSAndroid Build Coastguard Worker 1334*c2e18aaaSAndroid Build Coastguard Workerdef get_build_target(): 1335*c2e18aaaSAndroid Build Coastguard Worker """Get the build target form system environment TARGET_PRODUCT.""" 1336*c2e18aaaSAndroid Build Coastguard Worker build_target = '%s-%s-%s' % ( 1337*c2e18aaaSAndroid Build Coastguard Worker os.getenv(constants.ANDROID_TARGET_PRODUCT, None), 1338*c2e18aaaSAndroid Build Coastguard Worker os.getenv('TARGET_RELEASE', None), 1339*c2e18aaaSAndroid Build Coastguard Worker os.getenv(constants.TARGET_BUILD_VARIANT, None), 1340*c2e18aaaSAndroid Build Coastguard Worker ) 1341*c2e18aaaSAndroid Build Coastguard Worker return build_target 1342*c2e18aaaSAndroid Build Coastguard Worker 1343*c2e18aaaSAndroid Build Coastguard Worker 1344*c2e18aaaSAndroid Build Coastguard Workerdef has_wildcard(test_name): 1345*c2e18aaaSAndroid Build Coastguard Worker """Tell whether the test_name(either a list or string) contains wildcard 1346*c2e18aaaSAndroid Build Coastguard Worker 1347*c2e18aaaSAndroid Build Coastguard Worker symbols. 1348*c2e18aaaSAndroid Build Coastguard Worker 1349*c2e18aaaSAndroid Build Coastguard Worker Args: 1350*c2e18aaaSAndroid Build Coastguard Worker test_name: A list or a str. 1351*c2e18aaaSAndroid Build Coastguard Worker 1352*c2e18aaaSAndroid Build Coastguard Worker Return: 1353*c2e18aaaSAndroid Build Coastguard Worker True if test_name contains wildcard, False otherwise. 1354*c2e18aaaSAndroid Build Coastguard Worker """ 1355*c2e18aaaSAndroid Build Coastguard Worker if isinstance(test_name, str): 1356*c2e18aaaSAndroid Build Coastguard Worker return any(char in test_name for char in _WILDCARD_CHARS) 1357*c2e18aaaSAndroid Build Coastguard Worker if isinstance(test_name, list): 1358*c2e18aaaSAndroid Build Coastguard Worker for name in test_name: 1359*c2e18aaaSAndroid Build Coastguard Worker if has_wildcard(name): 1360*c2e18aaaSAndroid Build Coastguard Worker return True 1361*c2e18aaaSAndroid Build Coastguard Worker return False 1362*c2e18aaaSAndroid Build Coastguard Worker 1363*c2e18aaaSAndroid Build Coastguard Worker 1364*c2e18aaaSAndroid Build Coastguard Workerdef is_build_file(path): 1365*c2e18aaaSAndroid Build Coastguard Worker """If input file is one of an android build file. 1366*c2e18aaaSAndroid Build Coastguard Worker 1367*c2e18aaaSAndroid Build Coastguard Worker Args: 1368*c2e18aaaSAndroid Build Coastguard Worker path: A string of file path. 1369*c2e18aaaSAndroid Build Coastguard Worker 1370*c2e18aaaSAndroid Build Coastguard Worker Return: 1371*c2e18aaaSAndroid Build Coastguard Worker True if path is android build file, False otherwise. 1372*c2e18aaaSAndroid Build Coastguard Worker """ 1373*c2e18aaaSAndroid Build Coastguard Worker return bool(os.path.splitext(path)[-1] in _ANDROID_BUILD_EXT) 1374*c2e18aaaSAndroid Build Coastguard Worker 1375*c2e18aaaSAndroid Build Coastguard Worker 1376*c2e18aaaSAndroid Build Coastguard Workerdef quote(input_str): 1377*c2e18aaaSAndroid Build Coastguard Worker """If the input string -- especially in custom args -- contains shell-aware 1378*c2e18aaaSAndroid Build Coastguard Worker 1379*c2e18aaaSAndroid Build Coastguard Worker characters, insert a pair of "\" to the input string. 1380*c2e18aaaSAndroid Build Coastguard Worker 1381*c2e18aaaSAndroid Build Coastguard Worker e.g. unit(test|testing|testing) -> 'unit(test|testing|testing)' 1382*c2e18aaaSAndroid Build Coastguard Worker 1383*c2e18aaaSAndroid Build Coastguard Worker Args: 1384*c2e18aaaSAndroid Build Coastguard Worker input_str: A string from user input. 1385*c2e18aaaSAndroid Build Coastguard Worker 1386*c2e18aaaSAndroid Build Coastguard Worker Returns: A string with single quotes if regex chars were detected. 1387*c2e18aaaSAndroid Build Coastguard Worker """ 1388*c2e18aaaSAndroid Build Coastguard Worker if has_chars(input_str, _REGEX_CHARS): 1389*c2e18aaaSAndroid Build Coastguard Worker return "'" + input_str + "'" 1390*c2e18aaaSAndroid Build Coastguard Worker return input_str 1391*c2e18aaaSAndroid Build Coastguard Worker 1392*c2e18aaaSAndroid Build Coastguard Worker 1393*c2e18aaaSAndroid Build Coastguard Workerdef has_chars(input_str, chars): 1394*c2e18aaaSAndroid Build Coastguard Worker """Check if the input string contains one of the designated characters. 1395*c2e18aaaSAndroid Build Coastguard Worker 1396*c2e18aaaSAndroid Build Coastguard Worker Args: 1397*c2e18aaaSAndroid Build Coastguard Worker input_str: A string from user input. 1398*c2e18aaaSAndroid Build Coastguard Worker chars: An iterable object. 1399*c2e18aaaSAndroid Build Coastguard Worker 1400*c2e18aaaSAndroid Build Coastguard Worker Returns: 1401*c2e18aaaSAndroid Build Coastguard Worker True if the input string contains one of the special chars. 1402*c2e18aaaSAndroid Build Coastguard Worker """ 1403*c2e18aaaSAndroid Build Coastguard Worker for char in chars: 1404*c2e18aaaSAndroid Build Coastguard Worker if char in input_str: 1405*c2e18aaaSAndroid Build Coastguard Worker return True 1406*c2e18aaaSAndroid Build Coastguard Worker return False 1407*c2e18aaaSAndroid Build Coastguard Worker 1408*c2e18aaaSAndroid Build Coastguard Worker 1409*c2e18aaaSAndroid Build Coastguard Workerdef prompt_with_yn_result(msg, default=True): 1410*c2e18aaaSAndroid Build Coastguard Worker """Prompt message and get yes or no result. 1411*c2e18aaaSAndroid Build Coastguard Worker 1412*c2e18aaaSAndroid Build Coastguard Worker Args: 1413*c2e18aaaSAndroid Build Coastguard Worker msg: The question you want asking. 1414*c2e18aaaSAndroid Build Coastguard Worker default: boolean to True/Yes or False/No 1415*c2e18aaaSAndroid Build Coastguard Worker 1416*c2e18aaaSAndroid Build Coastguard Worker Returns: 1417*c2e18aaaSAndroid Build Coastguard Worker default value if get KeyboardInterrupt or ValueError exception. 1418*c2e18aaaSAndroid Build Coastguard Worker """ 1419*c2e18aaaSAndroid Build Coastguard Worker suffix = '[Y/n]: ' if default else '[y/N]: ' 1420*c2e18aaaSAndroid Build Coastguard Worker try: 1421*c2e18aaaSAndroid Build Coastguard Worker return strtobool(input(msg + suffix)) 1422*c2e18aaaSAndroid Build Coastguard Worker except (ValueError, KeyboardInterrupt): 1423*c2e18aaaSAndroid Build Coastguard Worker return default 1424*c2e18aaaSAndroid Build Coastguard Worker 1425*c2e18aaaSAndroid Build Coastguard Worker 1426*c2e18aaaSAndroid Build Coastguard Workerdef strtobool(val): 1427*c2e18aaaSAndroid Build Coastguard Worker """Convert a string representation of truth to True or False. 1428*c2e18aaaSAndroid Build Coastguard Worker 1429*c2e18aaaSAndroid Build Coastguard Worker Args: 1430*c2e18aaaSAndroid Build Coastguard Worker val: a string of input value. 1431*c2e18aaaSAndroid Build Coastguard Worker 1432*c2e18aaaSAndroid Build Coastguard Worker Returns: 1433*c2e18aaaSAndroid Build Coastguard Worker True when values are 'y', 'yes', 't', 'true', 'on', and '1'; 1434*c2e18aaaSAndroid Build Coastguard Worker False when 'n', 'no', 'f', 'false', 'off', and '0'. 1435*c2e18aaaSAndroid Build Coastguard Worker Raises ValueError if 'val' is anything else. 1436*c2e18aaaSAndroid Build Coastguard Worker """ 1437*c2e18aaaSAndroid Build Coastguard Worker if val.lower() in ('y', 'yes', 't', 'true', 'on', '1'): 1438*c2e18aaaSAndroid Build Coastguard Worker return True 1439*c2e18aaaSAndroid Build Coastguard Worker if val.lower() in ('n', 'no', 'f', 'false', 'off', '0'): 1440*c2e18aaaSAndroid Build Coastguard Worker return False 1441*c2e18aaaSAndroid Build Coastguard Worker raise ValueError('invalid truth value %r' % (val,)) 1442*c2e18aaaSAndroid Build Coastguard Worker 1443*c2e18aaaSAndroid Build Coastguard Worker 1444*c2e18aaaSAndroid Build Coastguard Workerdef get_android_junit_config_filters(test_config): 1445*c2e18aaaSAndroid Build Coastguard Worker """Get the dictionary of a input config for junit config's filters 1446*c2e18aaaSAndroid Build Coastguard Worker 1447*c2e18aaaSAndroid Build Coastguard Worker Args: 1448*c2e18aaaSAndroid Build Coastguard Worker test_config: The path of the test config. 1449*c2e18aaaSAndroid Build Coastguard Worker 1450*c2e18aaaSAndroid Build Coastguard Worker Returns: 1451*c2e18aaaSAndroid Build Coastguard Worker A dictionary include all the filters in the input config. 1452*c2e18aaaSAndroid Build Coastguard Worker """ 1453*c2e18aaaSAndroid Build Coastguard Worker filter_dict = {} 1454*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(test_config).getroot() 1455*c2e18aaaSAndroid Build Coastguard Worker option_tags = xml_root.findall('.//option') 1456*c2e18aaaSAndroid Build Coastguard Worker for tag in option_tags: 1457*c2e18aaaSAndroid Build Coastguard Worker name = tag.attrib['name'].strip() 1458*c2e18aaaSAndroid Build Coastguard Worker if name in constants.SUPPORTED_FILTERS: 1459*c2e18aaaSAndroid Build Coastguard Worker filter_values = filter_dict.get(name, []) 1460*c2e18aaaSAndroid Build Coastguard Worker value = tag.attrib['value'].strip() 1461*c2e18aaaSAndroid Build Coastguard Worker filter_values.append(value) 1462*c2e18aaaSAndroid Build Coastguard Worker filter_dict.update({name: filter_values}) 1463*c2e18aaaSAndroid Build Coastguard Worker return filter_dict 1464*c2e18aaaSAndroid Build Coastguard Worker 1465*c2e18aaaSAndroid Build Coastguard Worker 1466*c2e18aaaSAndroid Build Coastguard Workerdef get_config_parameter(test_config): 1467*c2e18aaaSAndroid Build Coastguard Worker """Get all the parameter values for the input config 1468*c2e18aaaSAndroid Build Coastguard Worker 1469*c2e18aaaSAndroid Build Coastguard Worker Args: 1470*c2e18aaaSAndroid Build Coastguard Worker test_config: The path of the test config. 1471*c2e18aaaSAndroid Build Coastguard Worker 1472*c2e18aaaSAndroid Build Coastguard Worker Returns: 1473*c2e18aaaSAndroid Build Coastguard Worker A set include all the parameters of the input config. 1474*c2e18aaaSAndroid Build Coastguard Worker """ 1475*c2e18aaaSAndroid Build Coastguard Worker parameters = set() 1476*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(test_config).getroot() 1477*c2e18aaaSAndroid Build Coastguard Worker option_tags = xml_root.findall('.//option') 1478*c2e18aaaSAndroid Build Coastguard Worker for tag in option_tags: 1479*c2e18aaaSAndroid Build Coastguard Worker name = tag.attrib['name'].strip() 1480*c2e18aaaSAndroid Build Coastguard Worker if name == constants.CONFIG_DESCRIPTOR: 1481*c2e18aaaSAndroid Build Coastguard Worker key = tag.attrib['key'].strip() 1482*c2e18aaaSAndroid Build Coastguard Worker if key == constants.PARAMETER_KEY: 1483*c2e18aaaSAndroid Build Coastguard Worker value = tag.attrib['value'].strip() 1484*c2e18aaaSAndroid Build Coastguard Worker parameters.add(value) 1485*c2e18aaaSAndroid Build Coastguard Worker return parameters 1486*c2e18aaaSAndroid Build Coastguard Worker 1487*c2e18aaaSAndroid Build Coastguard Worker 1488*c2e18aaaSAndroid Build Coastguard Workerdef get_config_device(test_config): 1489*c2e18aaaSAndroid Build Coastguard Worker """Get all the device names from the input config 1490*c2e18aaaSAndroid Build Coastguard Worker 1491*c2e18aaaSAndroid Build Coastguard Worker Args: 1492*c2e18aaaSAndroid Build Coastguard Worker test_config: The path of the test config. 1493*c2e18aaaSAndroid Build Coastguard Worker 1494*c2e18aaaSAndroid Build Coastguard Worker Returns: 1495*c2e18aaaSAndroid Build Coastguard Worker A set include all the device name of the input config. 1496*c2e18aaaSAndroid Build Coastguard Worker """ 1497*c2e18aaaSAndroid Build Coastguard Worker devices = set() 1498*c2e18aaaSAndroid Build Coastguard Worker try: 1499*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(test_config).getroot() 1500*c2e18aaaSAndroid Build Coastguard Worker device_tags = xml_root.findall('.//device') 1501*c2e18aaaSAndroid Build Coastguard Worker for tag in device_tags: 1502*c2e18aaaSAndroid Build Coastguard Worker name = tag.attrib['name'].strip() 1503*c2e18aaaSAndroid Build Coastguard Worker devices.add(name) 1504*c2e18aaaSAndroid Build Coastguard Worker except ET.ParseError as e: 1505*c2e18aaaSAndroid Build Coastguard Worker colorful_print('Config has invalid format.', constants.RED) 1506*c2e18aaaSAndroid Build Coastguard Worker colorful_print('File %s : %s' % (test_config, str(e)), constants.YELLOW) 1507*c2e18aaaSAndroid Build Coastguard Worker sys.exit(ExitCode.CONFIG_INVALID_FORMAT) 1508*c2e18aaaSAndroid Build Coastguard Worker return devices 1509*c2e18aaaSAndroid Build Coastguard Worker 1510*c2e18aaaSAndroid Build Coastguard Worker 1511*c2e18aaaSAndroid Build Coastguard Workerdef get_mainline_param(test_config): 1512*c2e18aaaSAndroid Build Coastguard Worker """Get all the mainline-param values for the input config 1513*c2e18aaaSAndroid Build Coastguard Worker 1514*c2e18aaaSAndroid Build Coastguard Worker Args: 1515*c2e18aaaSAndroid Build Coastguard Worker test_config: The path of the test config. 1516*c2e18aaaSAndroid Build Coastguard Worker 1517*c2e18aaaSAndroid Build Coastguard Worker Returns: 1518*c2e18aaaSAndroid Build Coastguard Worker A set include all the parameters of the input config. 1519*c2e18aaaSAndroid Build Coastguard Worker """ 1520*c2e18aaaSAndroid Build Coastguard Worker mainline_param = set() 1521*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(test_config).getroot() 1522*c2e18aaaSAndroid Build Coastguard Worker option_tags = xml_root.findall('.//option') 1523*c2e18aaaSAndroid Build Coastguard Worker for tag in option_tags: 1524*c2e18aaaSAndroid Build Coastguard Worker name = tag.attrib['name'].strip() 1525*c2e18aaaSAndroid Build Coastguard Worker if name == constants.CONFIG_DESCRIPTOR: 1526*c2e18aaaSAndroid Build Coastguard Worker key = tag.attrib['key'].strip() 1527*c2e18aaaSAndroid Build Coastguard Worker if key == constants.MAINLINE_PARAM_KEY: 1528*c2e18aaaSAndroid Build Coastguard Worker value = tag.attrib['value'].strip() 1529*c2e18aaaSAndroid Build Coastguard Worker mainline_param.add(value) 1530*c2e18aaaSAndroid Build Coastguard Worker return mainline_param 1531*c2e18aaaSAndroid Build Coastguard Worker 1532*c2e18aaaSAndroid Build Coastguard Worker 1533*c2e18aaaSAndroid Build Coastguard Workerdef get_adb_devices(): 1534*c2e18aaaSAndroid Build Coastguard Worker """Run `adb devices` and return a list of devices. 1535*c2e18aaaSAndroid Build Coastguard Worker 1536*c2e18aaaSAndroid Build Coastguard Worker Returns: 1537*c2e18aaaSAndroid Build Coastguard Worker A list of devices. e.g. 1538*c2e18aaaSAndroid Build Coastguard Worker ['127.0.0.1:40623', '127.0.0.1:40625'] 1539*c2e18aaaSAndroid Build Coastguard Worker """ 1540*c2e18aaaSAndroid Build Coastguard Worker probe_cmd = 'adb devices | egrep -v "^List|^$"||true' 1541*c2e18aaaSAndroid Build Coastguard Worker suts = subprocess.check_output(probe_cmd, shell=True).decode().splitlines() 1542*c2e18aaaSAndroid Build Coastguard Worker return [sut.split('\t')[0] for sut in suts] 1543*c2e18aaaSAndroid Build Coastguard Worker 1544*c2e18aaaSAndroid Build Coastguard Worker 1545*c2e18aaaSAndroid Build Coastguard Workerdef get_android_config(): 1546*c2e18aaaSAndroid Build Coastguard Worker """Get Android config as "printconfig" shows. 1547*c2e18aaaSAndroid Build Coastguard Worker 1548*c2e18aaaSAndroid Build Coastguard Worker Returns: 1549*c2e18aaaSAndroid Build Coastguard Worker A dict of Android configurations. 1550*c2e18aaaSAndroid Build Coastguard Worker """ 1551*c2e18aaaSAndroid Build Coastguard Worker dump_cmd = get_build_cmd(dump=True) 1552*c2e18aaaSAndroid Build Coastguard Worker raw_config = subprocess.check_output(dump_cmd).decode('utf-8') 1553*c2e18aaaSAndroid Build Coastguard Worker android_config = {} 1554*c2e18aaaSAndroid Build Coastguard Worker for element in raw_config.splitlines(): 1555*c2e18aaaSAndroid Build Coastguard Worker if not element.startswith('='): 1556*c2e18aaaSAndroid Build Coastguard Worker key, value = tuple(element.split('=', 1)) 1557*c2e18aaaSAndroid Build Coastguard Worker android_config.setdefault(key, value) 1558*c2e18aaaSAndroid Build Coastguard Worker return android_config 1559*c2e18aaaSAndroid Build Coastguard Worker 1560*c2e18aaaSAndroid Build Coastguard Worker 1561*c2e18aaaSAndroid Build Coastguard Workerdef get_config_gtest_args(test_config): 1562*c2e18aaaSAndroid Build Coastguard Worker """Get gtest's module-name and device-path option from the input config 1563*c2e18aaaSAndroid Build Coastguard Worker 1564*c2e18aaaSAndroid Build Coastguard Worker Args: 1565*c2e18aaaSAndroid Build Coastguard Worker test_config: The path of the test config. 1566*c2e18aaaSAndroid Build Coastguard Worker 1567*c2e18aaaSAndroid Build Coastguard Worker Returns: 1568*c2e18aaaSAndroid Build Coastguard Worker A string of gtest's module name. 1569*c2e18aaaSAndroid Build Coastguard Worker A string of gtest's device path. 1570*c2e18aaaSAndroid Build Coastguard Worker """ 1571*c2e18aaaSAndroid Build Coastguard Worker module_name = '' 1572*c2e18aaaSAndroid Build Coastguard Worker device_path = '' 1573*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(test_config).getroot() 1574*c2e18aaaSAndroid Build Coastguard Worker option_tags = xml_root.findall('.//option') 1575*c2e18aaaSAndroid Build Coastguard Worker for tag in option_tags: 1576*c2e18aaaSAndroid Build Coastguard Worker name = tag.attrib['name'].strip() 1577*c2e18aaaSAndroid Build Coastguard Worker value = tag.attrib['value'].strip() 1578*c2e18aaaSAndroid Build Coastguard Worker if name == 'native-test-device-path': 1579*c2e18aaaSAndroid Build Coastguard Worker device_path = value 1580*c2e18aaaSAndroid Build Coastguard Worker elif name == 'module-name': 1581*c2e18aaaSAndroid Build Coastguard Worker module_name = value 1582*c2e18aaaSAndroid Build Coastguard Worker return module_name, device_path 1583*c2e18aaaSAndroid Build Coastguard Worker 1584*c2e18aaaSAndroid Build Coastguard Worker 1585*c2e18aaaSAndroid Build Coastguard Workerdef get_arch_name(module_name, is_64=False): 1586*c2e18aaaSAndroid Build Coastguard Worker """Get the arch folder name for the input module. 1587*c2e18aaaSAndroid Build Coastguard Worker 1588*c2e18aaaSAndroid Build Coastguard Worker Scan the test case folders to get the matched arch folder name. 1589*c2e18aaaSAndroid Build Coastguard Worker 1590*c2e18aaaSAndroid Build Coastguard Worker Args: 1591*c2e18aaaSAndroid Build Coastguard Worker module_name: The module_name of test 1592*c2e18aaaSAndroid Build Coastguard Worker is_64: If need 64 bit arch name, False otherwise. 1593*c2e18aaaSAndroid Build Coastguard Worker 1594*c2e18aaaSAndroid Build Coastguard Worker Returns: 1595*c2e18aaaSAndroid Build Coastguard Worker A string of the arch name. 1596*c2e18aaaSAndroid Build Coastguard Worker """ 1597*c2e18aaaSAndroid Build Coastguard Worker arch_32 = ['arm', 'x86'] 1598*c2e18aaaSAndroid Build Coastguard Worker arch_64 = ['arm64', 'x86_64'] 1599*c2e18aaaSAndroid Build Coastguard Worker arch_list = arch_32 1600*c2e18aaaSAndroid Build Coastguard Worker if is_64: 1601*c2e18aaaSAndroid Build Coastguard Worker arch_list = arch_64 1602*c2e18aaaSAndroid Build Coastguard Worker test_case_root = os.path.join( 1603*c2e18aaaSAndroid Build Coastguard Worker os.environ.get(constants.ANDROID_TARGET_OUT_TESTCASES, ''), module_name 1604*c2e18aaaSAndroid Build Coastguard Worker ) 1605*c2e18aaaSAndroid Build Coastguard Worker if not os.path.isdir(test_case_root): 1606*c2e18aaaSAndroid Build Coastguard Worker logging.debug('%s does not exist.', test_case_root) 1607*c2e18aaaSAndroid Build Coastguard Worker return '' 1608*c2e18aaaSAndroid Build Coastguard Worker for f in os.listdir(test_case_root): 1609*c2e18aaaSAndroid Build Coastguard Worker if f in arch_list: 1610*c2e18aaaSAndroid Build Coastguard Worker return f 1611*c2e18aaaSAndroid Build Coastguard Worker return '' 1612*c2e18aaaSAndroid Build Coastguard Worker 1613*c2e18aaaSAndroid Build Coastguard Worker 1614*c2e18aaaSAndroid Build Coastguard Workerdef copy_single_arch_native_symbols( 1615*c2e18aaaSAndroid Build Coastguard Worker symbol_root, module_name, device_path, is_64=False 1616*c2e18aaaSAndroid Build Coastguard Worker): 1617*c2e18aaaSAndroid Build Coastguard Worker """Copy symbol files for native tests which belong to input arch. 1618*c2e18aaaSAndroid Build Coastguard Worker 1619*c2e18aaaSAndroid Build Coastguard Worker Args: 1620*c2e18aaaSAndroid Build Coastguard Worker module_name: The module_name of test 1621*c2e18aaaSAndroid Build Coastguard Worker device_path: The device path define in test config. 1622*c2e18aaaSAndroid Build Coastguard Worker is_64: True if need to copy 64bit symbols, False otherwise. 1623*c2e18aaaSAndroid Build Coastguard Worker """ 1624*c2e18aaaSAndroid Build Coastguard Worker src_symbol = os.path.join(symbol_root, 'data', 'nativetest', module_name) 1625*c2e18aaaSAndroid Build Coastguard Worker if is_64: 1626*c2e18aaaSAndroid Build Coastguard Worker src_symbol = os.path.join(symbol_root, 'data', 'nativetest64', module_name) 1627*c2e18aaaSAndroid Build Coastguard Worker dst_symbol = os.path.join( 1628*c2e18aaaSAndroid Build Coastguard Worker symbol_root, 1629*c2e18aaaSAndroid Build Coastguard Worker device_path[1:], 1630*c2e18aaaSAndroid Build Coastguard Worker module_name, 1631*c2e18aaaSAndroid Build Coastguard Worker get_arch_name(module_name, is_64), 1632*c2e18aaaSAndroid Build Coastguard Worker ) 1633*c2e18aaaSAndroid Build Coastguard Worker if os.path.isdir(src_symbol): 1634*c2e18aaaSAndroid Build Coastguard Worker # TODO: Use shutil.copytree(src, dst, dirs_exist_ok=True) after 1635*c2e18aaaSAndroid Build Coastguard Worker # python3.8 1636*c2e18aaaSAndroid Build Coastguard Worker if os.path.isdir(dst_symbol): 1637*c2e18aaaSAndroid Build Coastguard Worker shutil.rmtree(dst_symbol) 1638*c2e18aaaSAndroid Build Coastguard Worker shutil.copytree(src_symbol, dst_symbol) 1639*c2e18aaaSAndroid Build Coastguard Worker 1640*c2e18aaaSAndroid Build Coastguard Worker 1641*c2e18aaaSAndroid Build Coastguard Workerdef copy_native_symbols(module_name, device_path): 1642*c2e18aaaSAndroid Build Coastguard Worker """Copy symbol files for native tests to match with tradefed file structure. 1643*c2e18aaaSAndroid Build Coastguard Worker 1644*c2e18aaaSAndroid Build Coastguard Worker The original symbols will locate at 1645*c2e18aaaSAndroid Build Coastguard Worker $(PRODUCT_OUT)/symbols/data/nativetest(64)/$(module)/$(stem). 1646*c2e18aaaSAndroid Build Coastguard Worker From TF, the test binary will locate at 1647*c2e18aaaSAndroid Build Coastguard Worker /data/local/tmp/$(module)/$(arch)/$(stem). 1648*c2e18aaaSAndroid Build Coastguard Worker In order to make trace work need to copy the original symbol to 1649*c2e18aaaSAndroid Build Coastguard Worker $(PRODUCT_OUT)/symbols/data/local/tmp/$(module)/$(arch)/$(stem) 1650*c2e18aaaSAndroid Build Coastguard Worker 1651*c2e18aaaSAndroid Build Coastguard Worker Args: 1652*c2e18aaaSAndroid Build Coastguard Worker module_name: The module_name of test 1653*c2e18aaaSAndroid Build Coastguard Worker device_path: The device path define in test config. 1654*c2e18aaaSAndroid Build Coastguard Worker """ 1655*c2e18aaaSAndroid Build Coastguard Worker symbol_root = os.path.join( 1656*c2e18aaaSAndroid Build Coastguard Worker os.environ.get(constants.ANDROID_PRODUCT_OUT, ''), 'symbols' 1657*c2e18aaaSAndroid Build Coastguard Worker ) 1658*c2e18aaaSAndroid Build Coastguard Worker if not os.path.isdir(symbol_root): 1659*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Symbol dir:%s not exist, skip copy symbols.', symbol_root) 1660*c2e18aaaSAndroid Build Coastguard Worker return 1661*c2e18aaaSAndroid Build Coastguard Worker # Copy 32 bit symbols 1662*c2e18aaaSAndroid Build Coastguard Worker if get_arch_name(module_name, is_64=False): 1663*c2e18aaaSAndroid Build Coastguard Worker copy_single_arch_native_symbols( 1664*c2e18aaaSAndroid Build Coastguard Worker symbol_root, module_name, device_path, is_64=False 1665*c2e18aaaSAndroid Build Coastguard Worker ) 1666*c2e18aaaSAndroid Build Coastguard Worker # Copy 64 bit symbols 1667*c2e18aaaSAndroid Build Coastguard Worker if get_arch_name(module_name, is_64=True): 1668*c2e18aaaSAndroid Build Coastguard Worker copy_single_arch_native_symbols( 1669*c2e18aaaSAndroid Build Coastguard Worker symbol_root, module_name, device_path, is_64=True 1670*c2e18aaaSAndroid Build Coastguard Worker ) 1671*c2e18aaaSAndroid Build Coastguard Worker 1672*c2e18aaaSAndroid Build Coastguard Worker 1673*c2e18aaaSAndroid Build Coastguard Workerdef get_config_preparer_options(test_config, class_name): 1674*c2e18aaaSAndroid Build Coastguard Worker """Get all the parameter values for the input config 1675*c2e18aaaSAndroid Build Coastguard Worker 1676*c2e18aaaSAndroid Build Coastguard Worker Args: 1677*c2e18aaaSAndroid Build Coastguard Worker test_config: The path of the test config. 1678*c2e18aaaSAndroid Build Coastguard Worker class_name: A string of target_preparer 1679*c2e18aaaSAndroid Build Coastguard Worker 1680*c2e18aaaSAndroid Build Coastguard Worker Returns: 1681*c2e18aaaSAndroid Build Coastguard Worker A set include all the parameters of the input config. 1682*c2e18aaaSAndroid Build Coastguard Worker """ 1683*c2e18aaaSAndroid Build Coastguard Worker options = {} 1684*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(test_config).getroot() 1685*c2e18aaaSAndroid Build Coastguard Worker option_tags = xml_root.findall( 1686*c2e18aaaSAndroid Build Coastguard Worker './/target_preparer[@class="%s"]/option' % class_name 1687*c2e18aaaSAndroid Build Coastguard Worker ) 1688*c2e18aaaSAndroid Build Coastguard Worker for tag in option_tags: 1689*c2e18aaaSAndroid Build Coastguard Worker name = tag.attrib['name'].strip() 1690*c2e18aaaSAndroid Build Coastguard Worker value = tag.attrib['value'].strip() 1691*c2e18aaaSAndroid Build Coastguard Worker options[name] = value 1692*c2e18aaaSAndroid Build Coastguard Worker return options 1693*c2e18aaaSAndroid Build Coastguard Worker 1694*c2e18aaaSAndroid Build Coastguard Worker 1695*c2e18aaaSAndroid Build Coastguard Workerdef get_verify_key(tests, extra_args): 1696*c2e18aaaSAndroid Build Coastguard Worker """Compose test command key. 1697*c2e18aaaSAndroid Build Coastguard Worker 1698*c2e18aaaSAndroid Build Coastguard Worker Args: 1699*c2e18aaaSAndroid Build Coastguard Worker test_name: A list of input tests. 1700*c2e18aaaSAndroid Build Coastguard Worker extra_args: Dict of extra args to add to test run. 1701*c2e18aaaSAndroid Build Coastguard Worker 1702*c2e18aaaSAndroid Build Coastguard Worker Returns: 1703*c2e18aaaSAndroid Build Coastguard Worker A composed test commands. 1704*c2e18aaaSAndroid Build Coastguard Worker """ 1705*c2e18aaaSAndroid Build Coastguard Worker # test_commands is a concatenated string of sorted test_ref+extra_args. 1706*c2e18aaaSAndroid Build Coastguard Worker # For example, "ITERATIONS=5 hello_world_test" 1707*c2e18aaaSAndroid Build Coastguard Worker test_commands = tests 1708*c2e18aaaSAndroid Build Coastguard Worker for key, value in extra_args.items(): 1709*c2e18aaaSAndroid Build Coastguard Worker test_commands.append('%s=%s' % (key, str(value))) 1710*c2e18aaaSAndroid Build Coastguard Worker test_commands.sort() 1711*c2e18aaaSAndroid Build Coastguard Worker return ' '.join(test_commands) 1712*c2e18aaaSAndroid Build Coastguard Worker 1713*c2e18aaaSAndroid Build Coastguard Worker 1714*c2e18aaaSAndroid Build Coastguard Workerdef save_build_files_timestamp(): 1715*c2e18aaaSAndroid Build Coastguard Worker """Method that generate timestamp of Android.{bp,mk} files. 1716*c2e18aaaSAndroid Build Coastguard Worker 1717*c2e18aaaSAndroid Build Coastguard Worker The checksum of build files are stores in 1718*c2e18aaaSAndroid Build Coastguard Worker $ANDROID_HOST_OUT/indices/buildfiles.stp 1719*c2e18aaaSAndroid Build Coastguard Worker """ 1720*c2e18aaaSAndroid Build Coastguard Worker plocate_db = get_index_path(constants.LOCATE_CACHE) 1721*c2e18aaaSAndroid Build Coastguard Worker plocate_db_exist = plocate_db.is_file() 1722*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 1723*c2e18aaaSAndroid Build Coastguard Worker 'Build files timestamp db file %s exists: %s', 1724*c2e18aaaSAndroid Build Coastguard Worker plocate_db, 1725*c2e18aaaSAndroid Build Coastguard Worker plocate_db_exist, 1726*c2e18aaaSAndroid Build Coastguard Worker ) 1727*c2e18aaaSAndroid Build Coastguard Worker 1728*c2e18aaaSAndroid Build Coastguard Worker if plocate_db_exist: 1729*c2e18aaaSAndroid Build Coastguard Worker cmd = f'locate -d{plocate_db} --existing ' r'--regex "/Android\.(bp|mk)$"' 1730*c2e18aaaSAndroid Build Coastguard Worker results = subprocess.getoutput(cmd) 1731*c2e18aaaSAndroid Build Coastguard Worker if results: 1732*c2e18aaaSAndroid Build Coastguard Worker timestamp = {} 1733*c2e18aaaSAndroid Build Coastguard Worker for build_file in results.splitlines(): 1734*c2e18aaaSAndroid Build Coastguard Worker timestamp.update({build_file: Path(build_file).stat().st_mtime}) 1735*c2e18aaaSAndroid Build Coastguard Worker 1736*c2e18aaaSAndroid Build Coastguard Worker timestamp_file = get_index_path(constants.BUILDFILES_STP) 1737*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Writing to build files timestamp db %s', timestamp_file) 1738*c2e18aaaSAndroid Build Coastguard Worker with open(timestamp_file, 'w', encoding='utf-8') as _file: 1739*c2e18aaaSAndroid Build Coastguard Worker json.dump(timestamp, _file) 1740*c2e18aaaSAndroid Build Coastguard Worker 1741*c2e18aaaSAndroid Build Coastguard Worker 1742*c2e18aaaSAndroid Build Coastguard Workerdef run_multi_proc(func, *args, **kwargs) -> Process: 1743*c2e18aaaSAndroid Build Coastguard Worker """Start a process with multiprocessing and return Process object. 1744*c2e18aaaSAndroid Build Coastguard Worker 1745*c2e18aaaSAndroid Build Coastguard Worker Args: 1746*c2e18aaaSAndroid Build Coastguard Worker func: A string of function name which will be the target name. 1747*c2e18aaaSAndroid Build Coastguard Worker args/kwargs: check doc page: 1748*c2e18aaaSAndroid Build Coastguard Worker https://docs.python.org/3.8/library/multiprocessing.html#process-and-exceptions 1749*c2e18aaaSAndroid Build Coastguard Worker 1750*c2e18aaaSAndroid Build Coastguard Worker Returns: 1751*c2e18aaaSAndroid Build Coastguard Worker multiprocessing.Process object. 1752*c2e18aaaSAndroid Build Coastguard Worker """ 1753*c2e18aaaSAndroid Build Coastguard Worker proc = Process(target=func, *args, **kwargs) 1754*c2e18aaaSAndroid Build Coastguard Worker proc.start() 1755*c2e18aaaSAndroid Build Coastguard Worker return proc 1756*c2e18aaaSAndroid Build Coastguard Worker 1757*c2e18aaaSAndroid Build Coastguard Worker 1758*c2e18aaaSAndroid Build Coastguard Workerdef start_threading(target, *args, **kwargs) -> Thread: 1759*c2e18aaaSAndroid Build Coastguard Worker """Start a Thread-based parallelism. 1760*c2e18aaaSAndroid Build Coastguard Worker 1761*c2e18aaaSAndroid Build Coastguard Worker Args: 1762*c2e18aaaSAndroid Build Coastguard Worker target: A string of function name which will be the target name. 1763*c2e18aaaSAndroid Build Coastguard Worker args/kwargs: check doc page: 1764*c2e18aaaSAndroid Build Coastguard Worker https://docs.python.org/3/library/threading.html#threading.Thread 1765*c2e18aaaSAndroid Build Coastguard Worker 1766*c2e18aaaSAndroid Build Coastguard Worker Returns: 1767*c2e18aaaSAndroid Build Coastguard Worker threading.Thread object. 1768*c2e18aaaSAndroid Build Coastguard Worker """ 1769*c2e18aaaSAndroid Build Coastguard Worker proc = Thread(target=target, *args, **kwargs) 1770*c2e18aaaSAndroid Build Coastguard Worker proc.start() 1771*c2e18aaaSAndroid Build Coastguard Worker return proc 1772*c2e18aaaSAndroid Build Coastguard Worker 1773*c2e18aaaSAndroid Build Coastguard Worker 1774*c2e18aaaSAndroid Build Coastguard Workerdef get_prebuilt_sdk_tools_dir(): 1775*c2e18aaaSAndroid Build Coastguard Worker """Get the path for the prebuilt sdk tools root dir. 1776*c2e18aaaSAndroid Build Coastguard Worker 1777*c2e18aaaSAndroid Build Coastguard Worker Returns: The absolute path of prebuilt sdk tools directory. 1778*c2e18aaaSAndroid Build Coastguard Worker """ 1779*c2e18aaaSAndroid Build Coastguard Worker build_top = Path(os.environ.get(constants.ANDROID_BUILD_TOP, '')) 1780*c2e18aaaSAndroid Build Coastguard Worker return build_top.joinpath( 1781*c2e18aaaSAndroid Build Coastguard Worker 'prebuilts/sdk/tools/', str(platform.system()).lower(), 'bin' 1782*c2e18aaaSAndroid Build Coastguard Worker ) 1783*c2e18aaaSAndroid Build Coastguard Worker 1784*c2e18aaaSAndroid Build Coastguard Worker 1785*c2e18aaaSAndroid Build Coastguard Workerdef is_writable(path): 1786*c2e18aaaSAndroid Build Coastguard Worker """Check if the given path is writable. 1787*c2e18aaaSAndroid Build Coastguard Worker 1788*c2e18aaaSAndroid Build Coastguard Worker Returns: True if input path is writable, False otherwise. 1789*c2e18aaaSAndroid Build Coastguard Worker """ 1790*c2e18aaaSAndroid Build Coastguard Worker if not os.path.exists(path): 1791*c2e18aaaSAndroid Build Coastguard Worker return is_writable(os.path.dirname(path)) 1792*c2e18aaaSAndroid Build Coastguard Worker return os.access(path, os.W_OK) 1793*c2e18aaaSAndroid Build Coastguard Worker 1794*c2e18aaaSAndroid Build Coastguard Worker 1795*c2e18aaaSAndroid Build Coastguard Workerdef get_misc_dir(): 1796*c2e18aaaSAndroid Build Coastguard Worker """Get the path for the ATest data root dir. 1797*c2e18aaaSAndroid Build Coastguard Worker 1798*c2e18aaaSAndroid Build Coastguard Worker Returns: The absolute path of the ATest data root dir. 1799*c2e18aaaSAndroid Build Coastguard Worker """ 1800*c2e18aaaSAndroid Build Coastguard Worker home_dir = os.path.expanduser('~') 1801*c2e18aaaSAndroid Build Coastguard Worker if is_writable(home_dir): 1802*c2e18aaaSAndroid Build Coastguard Worker return home_dir 1803*c2e18aaaSAndroid Build Coastguard Worker return get_build_out_dir() 1804*c2e18aaaSAndroid Build Coastguard Worker 1805*c2e18aaaSAndroid Build Coastguard Worker 1806*c2e18aaaSAndroid Build Coastguard Workerdef get_config_folder() -> Path: 1807*c2e18aaaSAndroid Build Coastguard Worker """Returns the config folder path where upload config is stored.""" 1808*c2e18aaaSAndroid Build Coastguard Worker return Path(get_misc_dir()).joinpath('.atest') 1809*c2e18aaaSAndroid Build Coastguard Worker 1810*c2e18aaaSAndroid Build Coastguard Worker 1811*c2e18aaaSAndroid Build Coastguard Workerdef get_full_annotation_class_name(module_info, class_name): 1812*c2e18aaaSAndroid Build Coastguard Worker """Get fully qualified class name from a class name. 1813*c2e18aaaSAndroid Build Coastguard Worker 1814*c2e18aaaSAndroid Build Coastguard Worker If the given keyword(class_name) is "smalltest", this method can search 1815*c2e18aaaSAndroid Build Coastguard Worker among source codes and grep the accurate annotation class name: 1816*c2e18aaaSAndroid Build Coastguard Worker 1817*c2e18aaaSAndroid Build Coastguard Worker androidx.test.filters.SmallTest 1818*c2e18aaaSAndroid Build Coastguard Worker 1819*c2e18aaaSAndroid Build Coastguard Worker Args: 1820*c2e18aaaSAndroid Build Coastguard Worker module_info: A dict of module_info. 1821*c2e18aaaSAndroid Build Coastguard Worker class_name: A string of class name. 1822*c2e18aaaSAndroid Build Coastguard Worker 1823*c2e18aaaSAndroid Build Coastguard Worker Returns: 1824*c2e18aaaSAndroid Build Coastguard Worker A string of fully qualified class name, empty string otherwise. 1825*c2e18aaaSAndroid Build Coastguard Worker """ 1826*c2e18aaaSAndroid Build Coastguard Worker fullname_re = re.compile( 1827*c2e18aaaSAndroid Build Coastguard Worker r'import\s+(?P<fqcn>{})(|;)$'.format(class_name), re.I 1828*c2e18aaaSAndroid Build Coastguard Worker ) 1829*c2e18aaaSAndroid Build Coastguard Worker keyword_re = re.compile( 1830*c2e18aaaSAndroid Build Coastguard Worker r'import\s+(?P<fqcn>.*\.{})(|;)$'.format(class_name), re.I 1831*c2e18aaaSAndroid Build Coastguard Worker ) 1832*c2e18aaaSAndroid Build Coastguard Worker build_top = Path(os.environ.get(constants.ANDROID_BUILD_TOP, '')) 1833*c2e18aaaSAndroid Build Coastguard Worker for f in module_info.get(constants.MODULE_SRCS, []): 1834*c2e18aaaSAndroid Build Coastguard Worker full_path = build_top.joinpath(f) 1835*c2e18aaaSAndroid Build Coastguard Worker with open(full_path, 'r', encoding='utf-8') as cache: 1836*c2e18aaaSAndroid Build Coastguard Worker for line in cache.readlines(): 1837*c2e18aaaSAndroid Build Coastguard Worker # Accept full class name. 1838*c2e18aaaSAndroid Build Coastguard Worker match = fullname_re.match(line) 1839*c2e18aaaSAndroid Build Coastguard Worker if match: 1840*c2e18aaaSAndroid Build Coastguard Worker return match.group('fqcn') 1841*c2e18aaaSAndroid Build Coastguard Worker # Search annotation class from keyword. 1842*c2e18aaaSAndroid Build Coastguard Worker match = keyword_re.match(line) 1843*c2e18aaaSAndroid Build Coastguard Worker if match: 1844*c2e18aaaSAndroid Build Coastguard Worker return match.group('fqcn') 1845*c2e18aaaSAndroid Build Coastguard Worker return '' 1846*c2e18aaaSAndroid Build Coastguard Worker 1847*c2e18aaaSAndroid Build Coastguard Worker 1848*c2e18aaaSAndroid Build Coastguard Workerdef has_mixed_type_filters(test_infos): 1849*c2e18aaaSAndroid Build Coastguard Worker """There are different types in a test module. 1850*c2e18aaaSAndroid Build Coastguard Worker 1851*c2e18aaaSAndroid Build Coastguard Worker Dict test_to_types is mapping module name and the set of types. 1852*c2e18aaaSAndroid Build Coastguard Worker For example, 1853*c2e18aaaSAndroid Build Coastguard Worker { 1854*c2e18aaaSAndroid Build Coastguard Worker 'module_1': {'wildcard class_method'}, 1855*c2e18aaaSAndroid Build Coastguard Worker 'module_2': {'wildcard class_method', 'regular class_method'}, 1856*c2e18aaaSAndroid Build Coastguard Worker 'module_3': set() 1857*c2e18aaaSAndroid Build Coastguard Worker } 1858*c2e18aaaSAndroid Build Coastguard Worker 1859*c2e18aaaSAndroid Build Coastguard Worker Args: 1860*c2e18aaaSAndroid Build Coastguard Worker test_infos: A set of TestInfos. 1861*c2e18aaaSAndroid Build Coastguard Worker 1862*c2e18aaaSAndroid Build Coastguard Worker Returns: 1863*c2e18aaaSAndroid Build Coastguard Worker True if more than one filter type in a test module, False otherwise. 1864*c2e18aaaSAndroid Build Coastguard Worker """ 1865*c2e18aaaSAndroid Build Coastguard Worker test_to_types = {} 1866*c2e18aaaSAndroid Build Coastguard Worker for test_info in test_infos: 1867*c2e18aaaSAndroid Build Coastguard Worker filters = test_info.data.get(constants.TI_FILTER, []) 1868*c2e18aaaSAndroid Build Coastguard Worker filter_types = set() 1869*c2e18aaaSAndroid Build Coastguard Worker for flt in filters: 1870*c2e18aaaSAndroid Build Coastguard Worker filter_types |= get_filter_types(flt.to_list_of_tf_strings()) 1871*c2e18aaaSAndroid Build Coastguard Worker filter_types |= test_to_types.get(test_info.test_name, set()) 1872*c2e18aaaSAndroid Build Coastguard Worker test_to_types[test_info.test_name] = filter_types 1873*c2e18aaaSAndroid Build Coastguard Worker for _, types in test_to_types.items(): 1874*c2e18aaaSAndroid Build Coastguard Worker if len(types) > 1: 1875*c2e18aaaSAndroid Build Coastguard Worker return True 1876*c2e18aaaSAndroid Build Coastguard Worker return False 1877*c2e18aaaSAndroid Build Coastguard Worker 1878*c2e18aaaSAndroid Build Coastguard Worker 1879*c2e18aaaSAndroid Build Coastguard Workerdef get_filter_types(tf_filter_set): 1880*c2e18aaaSAndroid Build Coastguard Worker """Get filter types. 1881*c2e18aaaSAndroid Build Coastguard Worker 1882*c2e18aaaSAndroid Build Coastguard Worker Args: 1883*c2e18aaaSAndroid Build Coastguard Worker tf_filter_set: A list of tf filter strings. 1884*c2e18aaaSAndroid Build Coastguard Worker 1885*c2e18aaaSAndroid Build Coastguard Worker Returns: 1886*c2e18aaaSAndroid Build Coastguard Worker A set of FilterType. 1887*c2e18aaaSAndroid Build Coastguard Worker """ 1888*c2e18aaaSAndroid Build Coastguard Worker type_set = set() 1889*c2e18aaaSAndroid Build Coastguard Worker for tf_filter in tf_filter_set: 1890*c2e18aaaSAndroid Build Coastguard Worker if _WILDCARD_FILTER_RE.match(tf_filter): 1891*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 1892*c2e18aaaSAndroid Build Coastguard Worker 'Filter and type: (%s, %s)', 1893*c2e18aaaSAndroid Build Coastguard Worker tf_filter, 1894*c2e18aaaSAndroid Build Coastguard Worker FilterType.WILDCARD_FILTER.value, 1895*c2e18aaaSAndroid Build Coastguard Worker ) 1896*c2e18aaaSAndroid Build Coastguard Worker type_set.add(FilterType.WILDCARD_FILTER.value) 1897*c2e18aaaSAndroid Build Coastguard Worker if _REGULAR_FILTER_RE.match(tf_filter): 1898*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 1899*c2e18aaaSAndroid Build Coastguard Worker 'Filter and type: (%s, %s)', 1900*c2e18aaaSAndroid Build Coastguard Worker tf_filter, 1901*c2e18aaaSAndroid Build Coastguard Worker FilterType.REGULAR_FILTER.value, 1902*c2e18aaaSAndroid Build Coastguard Worker ) 1903*c2e18aaaSAndroid Build Coastguard Worker type_set.add(FilterType.REGULAR_FILTER.value) 1904*c2e18aaaSAndroid Build Coastguard Worker return type_set 1905*c2e18aaaSAndroid Build Coastguard Worker 1906*c2e18aaaSAndroid Build Coastguard Worker 1907*c2e18aaaSAndroid Build Coastguard Workerdef has_command(cmd: str) -> bool: 1908*c2e18aaaSAndroid Build Coastguard Worker """Detect if the command is available in PATH. 1909*c2e18aaaSAndroid Build Coastguard Worker 1910*c2e18aaaSAndroid Build Coastguard Worker Args: 1911*c2e18aaaSAndroid Build Coastguard Worker cmd: A string of the tested command. 1912*c2e18aaaSAndroid Build Coastguard Worker 1913*c2e18aaaSAndroid Build Coastguard Worker Returns: 1914*c2e18aaaSAndroid Build Coastguard Worker True if found, False otherwise. 1915*c2e18aaaSAndroid Build Coastguard Worker """ 1916*c2e18aaaSAndroid Build Coastguard Worker return bool(shutil.which(cmd)) 1917*c2e18aaaSAndroid Build Coastguard Worker 1918*c2e18aaaSAndroid Build Coastguard Worker 1919*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=anomalous-backslash-in-string,too-many-branches 1920*c2e18aaaSAndroid Build Coastguard Workerdef get_bp_content(filename: Path, module_type: str) -> Dict: 1921*c2e18aaaSAndroid Build Coastguard Worker """Get essential content info from an Android.bp. 1922*c2e18aaaSAndroid Build Coastguard Worker 1923*c2e18aaaSAndroid Build Coastguard Worker By specifying module_type (e.g. 'android_test', 'android_app'), this method 1924*c2e18aaaSAndroid Build Coastguard Worker can parse the given starting point and grab 'name', 'instrumentation_for' and 1925*c2e18aaaSAndroid Build Coastguard Worker 'manifest'. 1926*c2e18aaaSAndroid Build Coastguard Worker 1927*c2e18aaaSAndroid Build Coastguard Worker Returns: 1928*c2e18aaaSAndroid Build Coastguard Worker A dict of mapping test module and target module; e.g. 1929*c2e18aaaSAndroid Build Coastguard Worker { 1930*c2e18aaaSAndroid Build Coastguard Worker 'FooUnitTests': 1931*c2e18aaaSAndroid Build Coastguard Worker {'manifest': 'AndroidManifest.xml', 'target_module': 'Foo'}, 1932*c2e18aaaSAndroid Build Coastguard Worker 'Foo': 1933*c2e18aaaSAndroid Build Coastguard Worker {'manifest': 'AndroidManifest-common.xml', 'target_module': ''} 1934*c2e18aaaSAndroid Build Coastguard Worker } 1935*c2e18aaaSAndroid Build Coastguard Worker Null dict if there is no content of the given module_type. 1936*c2e18aaaSAndroid Build Coastguard Worker """ 1937*c2e18aaaSAndroid Build Coastguard Worker build_file = Path(filename) 1938*c2e18aaaSAndroid Build Coastguard Worker if not any((build_file.suffix == '.bp', build_file.is_file())): 1939*c2e18aaaSAndroid Build Coastguard Worker return {} 1940*c2e18aaaSAndroid Build Coastguard Worker start_from = re.compile(f'^{module_type}\s*\{{') 1941*c2e18aaaSAndroid Build Coastguard Worker end_with = re.compile(r'^\}$') 1942*c2e18aaaSAndroid Build Coastguard Worker context_re = re.compile( 1943*c2e18aaaSAndroid Build Coastguard Worker r'\s*(?P<key>(name|manifest|instrumentation_for))\s*:' 1944*c2e18aaaSAndroid Build Coastguard Worker r'\s*\"(?P<value>.*)\"\s*,', 1945*c2e18aaaSAndroid Build Coastguard Worker re.M, 1946*c2e18aaaSAndroid Build Coastguard Worker ) 1947*c2e18aaaSAndroid Build Coastguard Worker with open(build_file, 'r', encoding='utf-8') as cache: 1948*c2e18aaaSAndroid Build Coastguard Worker data = cache.readlines() 1949*c2e18aaaSAndroid Build Coastguard Worker content_dict = {} 1950*c2e18aaaSAndroid Build Coastguard Worker start_recording = False 1951*c2e18aaaSAndroid Build Coastguard Worker for _line in data: 1952*c2e18aaaSAndroid Build Coastguard Worker line = _line.strip() 1953*c2e18aaaSAndroid Build Coastguard Worker if re.match(start_from, line): 1954*c2e18aaaSAndroid Build Coastguard Worker start_recording = True 1955*c2e18aaaSAndroid Build Coastguard Worker _dict = {} 1956*c2e18aaaSAndroid Build Coastguard Worker continue 1957*c2e18aaaSAndroid Build Coastguard Worker if start_recording: 1958*c2e18aaaSAndroid Build Coastguard Worker if not re.match(end_with, line): 1959*c2e18aaaSAndroid Build Coastguard Worker match = re.match(context_re, line) 1960*c2e18aaaSAndroid Build Coastguard Worker if match: 1961*c2e18aaaSAndroid Build Coastguard Worker _dict.update({match.group('key'): match.group('value')}) 1962*c2e18aaaSAndroid Build Coastguard Worker else: 1963*c2e18aaaSAndroid Build Coastguard Worker start_recording = False 1964*c2e18aaaSAndroid Build Coastguard Worker module_name = _dict.get('name') 1965*c2e18aaaSAndroid Build Coastguard Worker if module_name: 1966*c2e18aaaSAndroid Build Coastguard Worker content_dict.update({ 1967*c2e18aaaSAndroid Build Coastguard Worker module_name: { 1968*c2e18aaaSAndroid Build Coastguard Worker 'manifest': _dict.get('manifest', 'AndroidManifest.xml'), 1969*c2e18aaaSAndroid Build Coastguard Worker 'target_module': _dict.get('instrumentation_for', ''), 1970*c2e18aaaSAndroid Build Coastguard Worker } 1971*c2e18aaaSAndroid Build Coastguard Worker }) 1972*c2e18aaaSAndroid Build Coastguard Worker return content_dict 1973*c2e18aaaSAndroid Build Coastguard Worker 1974*c2e18aaaSAndroid Build Coastguard Worker 1975*c2e18aaaSAndroid Build Coastguard Workerdef get_manifest_info(manifest: Path) -> Dict[str, Any]: 1976*c2e18aaaSAndroid Build Coastguard Worker """Get the essential info from the given manifest file. 1977*c2e18aaaSAndroid Build Coastguard Worker 1978*c2e18aaaSAndroid Build Coastguard Worker This method cares only three attributes: 1979*c2e18aaaSAndroid Build Coastguard Worker 1980*c2e18aaaSAndroid Build Coastguard Worker * package 1981*c2e18aaaSAndroid Build Coastguard Worker * targetPackage 1982*c2e18aaaSAndroid Build Coastguard Worker * persistent 1983*c2e18aaaSAndroid Build Coastguard Worker For an instrumentation test, the result will be like: 1984*c2e18aaaSAndroid Build Coastguard Worker { 1985*c2e18aaaSAndroid Build Coastguard Worker 'package': 'com.android.foo.tests.unit', 1986*c2e18aaaSAndroid Build Coastguard Worker 'targetPackage': 'com.android.foo', 1987*c2e18aaaSAndroid Build Coastguard Worker 'persistent': False 1988*c2e18aaaSAndroid Build Coastguard Worker } 1989*c2e18aaaSAndroid Build Coastguard Worker For a target module of the instrumentation test: 1990*c2e18aaaSAndroid Build Coastguard Worker { 1991*c2e18aaaSAndroid Build Coastguard Worker 'package': 'com.android.foo', 1992*c2e18aaaSAndroid Build Coastguard Worker 'targetPackage': '', 1993*c2e18aaaSAndroid Build Coastguard Worker 'persistent': True 1994*c2e18aaaSAndroid Build Coastguard Worker } 1995*c2e18aaaSAndroid Build Coastguard Worker """ 1996*c2e18aaaSAndroid Build Coastguard Worker mdict = {'package': '', 'target_package': '', 'persistent': False} 1997*c2e18aaaSAndroid Build Coastguard Worker try: 1998*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(manifest).getroot() 1999*c2e18aaaSAndroid Build Coastguard Worker except (ET.ParseError, FileNotFoundError): 2000*c2e18aaaSAndroid Build Coastguard Worker return mdict 2001*c2e18aaaSAndroid Build Coastguard Worker manifest_package_re = re.compile(r'[a-z][\w]+(\.[\w]+)*') 2002*c2e18aaaSAndroid Build Coastguard Worker # 1. Must probe 'package' name from the top. 2003*c2e18aaaSAndroid Build Coastguard Worker for item in xml_root.findall('.'): 2004*c2e18aaaSAndroid Build Coastguard Worker if 'package' in item.attrib.keys(): 2005*c2e18aaaSAndroid Build Coastguard Worker pkg = item.attrib.get('package') 2006*c2e18aaaSAndroid Build Coastguard Worker match = manifest_package_re.match(pkg) 2007*c2e18aaaSAndroid Build Coastguard Worker if match: 2008*c2e18aaaSAndroid Build Coastguard Worker mdict['package'] = pkg 2009*c2e18aaaSAndroid Build Coastguard Worker break 2010*c2e18aaaSAndroid Build Coastguard Worker for item in xml_root.findall('*'): 2011*c2e18aaaSAndroid Build Coastguard Worker # 2. Probe 'targetPackage' in 'instrumentation' tag. 2012*c2e18aaaSAndroid Build Coastguard Worker if item.tag == 'instrumentation': 2013*c2e18aaaSAndroid Build Coastguard Worker for key, value in item.attrib.items(): 2014*c2e18aaaSAndroid Build Coastguard Worker if 'targetPackage' in key: 2015*c2e18aaaSAndroid Build Coastguard Worker mdict['target_package'] = value 2016*c2e18aaaSAndroid Build Coastguard Worker break 2017*c2e18aaaSAndroid Build Coastguard Worker # 3. Probe 'persistent' in any tags. 2018*c2e18aaaSAndroid Build Coastguard Worker for key, value in item.attrib.items(): 2019*c2e18aaaSAndroid Build Coastguard Worker if 'persistent' in key: 2020*c2e18aaaSAndroid Build Coastguard Worker mdict['persistent'] = value.lower() == 'true' 2021*c2e18aaaSAndroid Build Coastguard Worker break 2022*c2e18aaaSAndroid Build Coastguard Worker return mdict 2023*c2e18aaaSAndroid Build Coastguard Worker 2024*c2e18aaaSAndroid Build Coastguard Worker 2025*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=broad-except 2026*c2e18aaaSAndroid Build Coastguard Workerdef generate_result_html(result_file: Path) -> Path: 2027*c2e18aaaSAndroid Build Coastguard Worker """Generate a html that collects all log files.""" 2028*c2e18aaaSAndroid Build Coastguard Worker result_file = Path(result_file) 2029*c2e18aaaSAndroid Build Coastguard Worker search_dir = Path(result_file).parent 2030*c2e18aaaSAndroid Build Coastguard Worker result_html = Path(result_file.parent, 'local_log_file_list.html') 2031*c2e18aaaSAndroid Build Coastguard Worker try: 2032*c2e18aaaSAndroid Build Coastguard Worker logs = sorted(find_files(str(search_dir), file_name='*', followlinks=True)) 2033*c2e18aaaSAndroid Build Coastguard Worker with open(result_html, 'w', encoding='utf-8') as cache: 2034*c2e18aaaSAndroid Build Coastguard Worker cache.write('<!DOCTYPE html><html><body>') 2035*c2e18aaaSAndroid Build Coastguard Worker result = load_json_safely(result_file) 2036*c2e18aaaSAndroid Build Coastguard Worker if result: 2037*c2e18aaaSAndroid Build Coastguard Worker cache.write(f'<h1>{"atest " + result.get("args")}</h1>') 2038*c2e18aaaSAndroid Build Coastguard Worker timestamp = datetime.datetime.fromtimestamp(result_file.stat().st_ctime) 2039*c2e18aaaSAndroid Build Coastguard Worker cache.write(f'<h2>{timestamp}</h2>') 2040*c2e18aaaSAndroid Build Coastguard Worker for log in logs: 2041*c2e18aaaSAndroid Build Coastguard Worker cache.write( 2042*c2e18aaaSAndroid Build Coastguard Worker f'<p><a href="{urllib.parse.quote(log)}">' 2043*c2e18aaaSAndroid Build Coastguard Worker f'{html.escape(Path(log).relative_to(search_dir).as_posix())}</a></p>' 2044*c2e18aaaSAndroid Build Coastguard Worker ) 2045*c2e18aaaSAndroid Build Coastguard Worker cache.write('</body></html>') 2046*c2e18aaaSAndroid Build Coastguard Worker send_tradeded_elapsed_time_metric(search_dir) 2047*c2e18aaaSAndroid Build Coastguard Worker return result_html 2048*c2e18aaaSAndroid Build Coastguard Worker except Exception as e: 2049*c2e18aaaSAndroid Build Coastguard Worker logging.debug('Did not generate log html for reason: %s', e) 2050*c2e18aaaSAndroid Build Coastguard Worker return None 2051*c2e18aaaSAndroid Build Coastguard Worker 2052*c2e18aaaSAndroid Build Coastguard Worker 2053*c2e18aaaSAndroid Build Coastguard Workerdef send_tradeded_elapsed_time_metric(search_dir: Path): 2054*c2e18aaaSAndroid Build Coastguard Worker """Method which sends Tradefed elapsed time to the metrics.""" 2055*c2e18aaaSAndroid Build Coastguard Worker test, prep, teardown = get_tradefed_invocation_time(search_dir) 2056*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 2057*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.TF_TOTAL_RUN_MS, result=test + prep + teardown 2058*c2e18aaaSAndroid Build Coastguard Worker ) 2059*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 2060*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.TF_PREPARATION_MS, result=prep 2061*c2e18aaaSAndroid Build Coastguard Worker ) 2062*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent(detect_type=DetectType.TF_TEST_MS, result=test) 2063*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 2064*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.TF_TEARDOWN_MS, result=teardown 2065*c2e18aaaSAndroid Build Coastguard Worker ) 2066*c2e18aaaSAndroid Build Coastguard Worker 2067*c2e18aaaSAndroid Build Coastguard Worker 2068*c2e18aaaSAndroid Build Coastguard Workerdef get_tradefed_invocation_time(search_dir: Path) -> Tuple[int, int, int]: 2069*c2e18aaaSAndroid Build Coastguard Worker """Return a tuple of testing, preparation and teardown time.""" 2070*c2e18aaaSAndroid Build Coastguard Worker test, prep, teardown = 0, 0, 0 2071*c2e18aaaSAndroid Build Coastguard Worker end_host_log_files = find_files( 2072*c2e18aaaSAndroid Build Coastguard Worker path=search_dir, file_name='end_host_log_*.txt', followlinks=True 2073*c2e18aaaSAndroid Build Coastguard Worker ) 2074*c2e18aaaSAndroid Build Coastguard Worker for log in end_host_log_files: 2075*c2e18aaaSAndroid Build Coastguard Worker with open(log, 'r', encoding='utf-8') as cache: 2076*c2e18aaaSAndroid Build Coastguard Worker contents = cache.read().splitlines() 2077*c2e18aaaSAndroid Build Coastguard Worker 2078*c2e18aaaSAndroid Build Coastguard Worker parse_test_time, parse_prep_time = False, False 2079*c2e18aaaSAndroid Build Coastguard Worker # ============================================ 2080*c2e18aaaSAndroid Build Coastguard Worker # ================= Results ================== 2081*c2e18aaaSAndroid Build Coastguard Worker # =============== Consumed Time ============== 2082*c2e18aaaSAndroid Build Coastguard Worker # x86_64 HelloWorldTests: 1s 2083*c2e18aaaSAndroid Build Coastguard Worker # x86_64 hallo-welt: 866 ms 2084*c2e18aaaSAndroid Build Coastguard Worker # Total aggregated tests run time: 1s 2085*c2e18aaaSAndroid Build Coastguard Worker # ============== Modules Preparation Times ============== 2086*c2e18aaaSAndroid Build Coastguard Worker # x86_64 HelloWorldTests => prep = 2483 ms || clean = 294 ms 2087*c2e18aaaSAndroid Build Coastguard Worker # x86_64 hallo-welt => prep = 1845 ms || clean = 292 ms 2088*c2e18aaaSAndroid Build Coastguard Worker # Total preparation time: 4s || Total tear down time: 586 ms 2089*c2e18aaaSAndroid Build Coastguard Worker # ======================================================= 2090*c2e18aaaSAndroid Build Coastguard Worker # =============== Summary =============== 2091*c2e18aaaSAndroid Build Coastguard Worker # Total Run time: 6s 2092*c2e18aaaSAndroid Build Coastguard Worker # 2/2 modules completed 2093*c2e18aaaSAndroid Build Coastguard Worker # Total Tests : 3 2094*c2e18aaaSAndroid Build Coastguard Worker # PASSED : 3 2095*c2e18aaaSAndroid Build Coastguard Worker # FAILED : 0 2096*c2e18aaaSAndroid Build Coastguard Worker # ============== End of Results ============== 2097*c2e18aaaSAndroid Build Coastguard Worker # ============================================ 2098*c2e18aaaSAndroid Build Coastguard Worker for line in contents: 2099*c2e18aaaSAndroid Build Coastguard Worker if re.match(r'[=]+.*consumed.*time.*[=]+', line, re.I): 2100*c2e18aaaSAndroid Build Coastguard Worker parse_test_time, parse_prep_time = True, False 2101*c2e18aaaSAndroid Build Coastguard Worker continue 2102*c2e18aaaSAndroid Build Coastguard Worker if re.match(r'[=]+.*preparation.*time.*[=]+', line, re.I): 2103*c2e18aaaSAndroid Build Coastguard Worker parse_test_time, parse_prep_time = False, True 2104*c2e18aaaSAndroid Build Coastguard Worker continue 2105*c2e18aaaSAndroid Build Coastguard Worker # Close parsing when `Total` keyword starts at the beginning. 2106*c2e18aaaSAndroid Build Coastguard Worker if re.match(r'^(Total.*)', line, re.I): 2107*c2e18aaaSAndroid Build Coastguard Worker parse_test_time, parse_prep_time = False, False 2108*c2e18aaaSAndroid Build Coastguard Worker continue 2109*c2e18aaaSAndroid Build Coastguard Worker if parse_test_time: 2110*c2e18aaaSAndroid Build Coastguard Worker match = re.search(r'^[\s]+\w.*:\s+(?P<timestr>.*)$', line, re.I) 2111*c2e18aaaSAndroid Build Coastguard Worker if match: 2112*c2e18aaaSAndroid Build Coastguard Worker test += convert_timestr_to_ms(match.group('timestr')) 2113*c2e18aaaSAndroid Build Coastguard Worker continue 2114*c2e18aaaSAndroid Build Coastguard Worker if parse_prep_time: 2115*c2e18aaaSAndroid Build Coastguard Worker # SuiteResultReporter.java defines elapsed prep time only in ms. 2116*c2e18aaaSAndroid Build Coastguard Worker match = re.search( 2117*c2e18aaaSAndroid Build Coastguard Worker r'prep = (?P<prep>\d+ ms) \|\| clean = (?P<clean>\d+ ms)$', 2118*c2e18aaaSAndroid Build Coastguard Worker line, 2119*c2e18aaaSAndroid Build Coastguard Worker re.I, 2120*c2e18aaaSAndroid Build Coastguard Worker ) 2121*c2e18aaaSAndroid Build Coastguard Worker if match: 2122*c2e18aaaSAndroid Build Coastguard Worker prep += convert_timestr_to_ms(match.group('prep')) 2123*c2e18aaaSAndroid Build Coastguard Worker teardown += convert_timestr_to_ms(match.group('clean')) 2124*c2e18aaaSAndroid Build Coastguard Worker continue 2125*c2e18aaaSAndroid Build Coastguard Worker 2126*c2e18aaaSAndroid Build Coastguard Worker return test, prep, teardown 2127*c2e18aaaSAndroid Build Coastguard Worker 2128*c2e18aaaSAndroid Build Coastguard Worker 2129*c2e18aaaSAndroid Build Coastguard Workerdef convert_timestr_to_ms(time_string: str = None) -> int: 2130*c2e18aaaSAndroid Build Coastguard Worker """Convert time string to an integer in millisecond. 2131*c2e18aaaSAndroid Build Coastguard Worker 2132*c2e18aaaSAndroid Build Coastguard Worker Possible time strings are: 2133*c2e18aaaSAndroid Build Coastguard Worker 1h 21m 15s 2134*c2e18aaaSAndroid Build Coastguard Worker 1m 5s 2135*c2e18aaaSAndroid Build Coastguard Worker 25s 2136*c2e18aaaSAndroid Build Coastguard Worker If elapsed time is less than 1 sec, the time will be in millisecond. 2137*c2e18aaaSAndroid Build Coastguard Worker 233 ms 2138*c2e18aaaSAndroid Build Coastguard Worker """ 2139*c2e18aaaSAndroid Build Coastguard Worker if not time_string: 2140*c2e18aaaSAndroid Build Coastguard Worker return 0 2141*c2e18aaaSAndroid Build Coastguard Worker 2142*c2e18aaaSAndroid Build Coastguard Worker hours, minutes, seconds = 0, 0, 0 2143*c2e18aaaSAndroid Build Coastguard Worker # Extract hour(<h>), minute(<m>), second(<s>), or millisecond(<ms>). 2144*c2e18aaaSAndroid Build Coastguard Worker match = re.match( 2145*c2e18aaaSAndroid Build Coastguard Worker r'(((?P<h>\d+)h\s+)?(?P<m>\d+)m\s+)?(?P<s>\d+)s|(?P<ms>\d+)\s*ms', 2146*c2e18aaaSAndroid Build Coastguard Worker time_string, 2147*c2e18aaaSAndroid Build Coastguard Worker ) 2148*c2e18aaaSAndroid Build Coastguard Worker if match: 2149*c2e18aaaSAndroid Build Coastguard Worker hours = int(match.group('h')) if match.group('h') else 0 2150*c2e18aaaSAndroid Build Coastguard Worker minutes = int(match.group('m')) if match.group('m') else 0 2151*c2e18aaaSAndroid Build Coastguard Worker seconds = int(match.group('s')) if match.group('s') else 0 2152*c2e18aaaSAndroid Build Coastguard Worker milliseconds = int(match.group('ms')) if match.group('ms') else 0 2153*c2e18aaaSAndroid Build Coastguard Worker 2154*c2e18aaaSAndroid Build Coastguard Worker return ( 2155*c2e18aaaSAndroid Build Coastguard Worker hours * 3600 * 1000 + minutes * 60 * 1000 + seconds * 1000 + milliseconds 2156*c2e18aaaSAndroid Build Coastguard Worker ) 2157*c2e18aaaSAndroid Build Coastguard Worker 2158*c2e18aaaSAndroid Build Coastguard Worker 2159*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=broad-except 2160*c2e18aaaSAndroid Build Coastguard Workerdef prompt_suggestions(result_file: Path): 2161*c2e18aaaSAndroid Build Coastguard Worker """Generate suggestions when detecting keywords in logs.""" 2162*c2e18aaaSAndroid Build Coastguard Worker result_file = Path(result_file) 2163*c2e18aaaSAndroid Build Coastguard Worker search_dir = Path(result_file).parent.joinpath('log') 2164*c2e18aaaSAndroid Build Coastguard Worker logs = sorted(find_files(str(search_dir), file_name='*')) 2165*c2e18aaaSAndroid Build Coastguard Worker for log in logs: 2166*c2e18aaaSAndroid Build Coastguard Worker for keyword, suggestion in SUGGESTIONS.items(): 2167*c2e18aaaSAndroid Build Coastguard Worker try: 2168*c2e18aaaSAndroid Build Coastguard Worker with open(log, 'r', encoding='utf-8') as cache: 2169*c2e18aaaSAndroid Build Coastguard Worker content = cache.read() 2170*c2e18aaaSAndroid Build Coastguard Worker if keyword in content: 2171*c2e18aaaSAndroid Build Coastguard Worker colorful_print('[Suggestion] ' + suggestion, color=constants.RED) 2172*c2e18aaaSAndroid Build Coastguard Worker break 2173*c2e18aaaSAndroid Build Coastguard Worker # If the given is not a plain text, just ignore it. 2174*c2e18aaaSAndroid Build Coastguard Worker except Exception: 2175*c2e18aaaSAndroid Build Coastguard Worker pass 2176*c2e18aaaSAndroid Build Coastguard Worker 2177*c2e18aaaSAndroid Build Coastguard Worker 2178*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=invalid-name 2179*c2e18aaaSAndroid Build Coastguard Workerdef get_rbe_and_customized_out_state() -> int: 2180*c2e18aaaSAndroid Build Coastguard Worker """Return decimal state of RBE and customized out. 2181*c2e18aaaSAndroid Build Coastguard Worker 2182*c2e18aaaSAndroid Build Coastguard Worker Customizing out dir (OUT_DIR/OUT_DIR_COMMON_BASE) dramatically slows down 2183*c2e18aaaSAndroid Build Coastguard Worker the RBE performance; by collecting the combined state of the two states, 2184*c2e18aaaSAndroid Build Coastguard Worker we can profile the performance relationship between RBE and the build time. 2185*c2e18aaaSAndroid Build Coastguard Worker 2186*c2e18aaaSAndroid Build Coastguard Worker Returns: 2187*c2e18aaaSAndroid Build Coastguard Worker An integer that describes the combined state. 2188*c2e18aaaSAndroid Build Coastguard Worker """ 2189*c2e18aaaSAndroid Build Coastguard Worker # RBE | out_dir | decimal 2190*c2e18aaaSAndroid Build Coastguard Worker # --------+---------+--------- 2191*c2e18aaaSAndroid Build Coastguard Worker # 0 | 0 | 0 2192*c2e18aaaSAndroid Build Coastguard Worker # 0 | 1 | 1 2193*c2e18aaaSAndroid Build Coastguard Worker # 1 | 0 | 2 2194*c2e18aaaSAndroid Build Coastguard Worker # 1 | 1 | 3 --> Caution for poor performance. 2195*c2e18aaaSAndroid Build Coastguard Worker ON = '1' 2196*c2e18aaaSAndroid Build Coastguard Worker OFF = '0' 2197*c2e18aaaSAndroid Build Coastguard Worker # 1. ensure RBE is enabled during the build. 2198*c2e18aaaSAndroid Build Coastguard Worker actual_out_dir = get_build_out_dir() 2199*c2e18aaaSAndroid Build Coastguard Worker log_path = actual_out_dir.joinpath('soong.log') 2200*c2e18aaaSAndroid Build Coastguard Worker rbe_enabled = not bool( 2201*c2e18aaaSAndroid Build Coastguard Worker subprocess.call(f'grep -q USE_RBE=true {log_path}'.split()) 2202*c2e18aaaSAndroid Build Coastguard Worker ) 2203*c2e18aaaSAndroid Build Coastguard Worker rbe_state = ON if rbe_enabled else OFF 2204*c2e18aaaSAndroid Build Coastguard Worker 2205*c2e18aaaSAndroid Build Coastguard Worker # 2. The customized out path will be different from the regular one. 2206*c2e18aaaSAndroid Build Coastguard Worker regular_out_dir = Path(os.getenv(constants.ANDROID_BUILD_TOP), 'out') 2207*c2e18aaaSAndroid Build Coastguard Worker customized_out = OFF if actual_out_dir == regular_out_dir else ON 2208*c2e18aaaSAndroid Build Coastguard Worker 2209*c2e18aaaSAndroid Build Coastguard Worker return int(rbe_state + customized_out, 2) 2210*c2e18aaaSAndroid Build Coastguard Worker 2211*c2e18aaaSAndroid Build Coastguard Worker 2212*c2e18aaaSAndroid Build Coastguard Workerdef build_files_integrity_is_ok() -> bool: 2213*c2e18aaaSAndroid Build Coastguard Worker """Return Whether the integrity of build files is OK.""" 2214*c2e18aaaSAndroid Build Coastguard Worker # 0. Missing timestamp file or plocate.db means a fresh repo sync. 2215*c2e18aaaSAndroid Build Coastguard Worker timestamp_file = get_index_path(constants.BUILDFILES_STP) 2216*c2e18aaaSAndroid Build Coastguard Worker locate_cache = get_index_path(constants.LOCATE_CACHE) 2217*c2e18aaaSAndroid Build Coastguard Worker if not timestamp_file.is_file(): 2218*c2e18aaaSAndroid Build Coastguard Worker logging.debug('timestamp_file %s is missing', timestamp_file) 2219*c2e18aaaSAndroid Build Coastguard Worker return False 2220*c2e18aaaSAndroid Build Coastguard Worker if not locate_cache.is_file(): 2221*c2e18aaaSAndroid Build Coastguard Worker logging.debug('locate_cache file %s is missing', locate_cache) 2222*c2e18aaaSAndroid Build Coastguard Worker return False 2223*c2e18aaaSAndroid Build Coastguard Worker 2224*c2e18aaaSAndroid Build Coastguard Worker # 1. Ensure no build files were added/deleted. 2225*c2e18aaaSAndroid Build Coastguard Worker recorded_amount = len(load_json_safely(timestamp_file).keys()) 2226*c2e18aaaSAndroid Build Coastguard Worker cmd_out = subprocess.getoutput( 2227*c2e18aaaSAndroid Build Coastguard Worker f'locate -e -d{locate_cache} --regex ' r'"/Android\.(bp|mk)$" | wc -l' 2228*c2e18aaaSAndroid Build Coastguard Worker ) 2229*c2e18aaaSAndroid Build Coastguard Worker if int(cmd_out) != recorded_amount: 2230*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 2231*c2e18aaaSAndroid Build Coastguard Worker 'Some build files are added/deleted. Recorded number of files: %s,' 2232*c2e18aaaSAndroid Build Coastguard Worker ' actual: %s', 2233*c2e18aaaSAndroid Build Coastguard Worker recorded_amount, 2234*c2e18aaaSAndroid Build Coastguard Worker cmd_out, 2235*c2e18aaaSAndroid Build Coastguard Worker ) 2236*c2e18aaaSAndroid Build Coastguard Worker return False 2237*c2e18aaaSAndroid Build Coastguard Worker 2238*c2e18aaaSAndroid Build Coastguard Worker # 2. Ensure the consistency of all build files. 2239*c2e18aaaSAndroid Build Coastguard Worker for file, timestamp in load_json_safely(timestamp_file).items(): 2240*c2e18aaaSAndroid Build Coastguard Worker if Path(file).exists() and Path(file).stat().st_mtime != timestamp: 2241*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 2242*c2e18aaaSAndroid Build Coastguard Worker 'A build file is changed: %s. Recorded timestamp: %s, actual' 2243*c2e18aaaSAndroid Build Coastguard Worker ' timestamp: %s', 2244*c2e18aaaSAndroid Build Coastguard Worker file, 2245*c2e18aaaSAndroid Build Coastguard Worker timestamp, 2246*c2e18aaaSAndroid Build Coastguard Worker Path(file).stat().st_mtime, 2247*c2e18aaaSAndroid Build Coastguard Worker ) 2248*c2e18aaaSAndroid Build Coastguard Worker return False 2249*c2e18aaaSAndroid Build Coastguard Worker return True 2250*c2e18aaaSAndroid Build Coastguard Worker 2251*c2e18aaaSAndroid Build Coastguard Worker 2252*c2e18aaaSAndroid Build Coastguard Workerdef _build_env_profiling() -> BuildEnvProfiler: 2253*c2e18aaaSAndroid Build Coastguard Worker """Determine the status profile before build. 2254*c2e18aaaSAndroid Build Coastguard Worker 2255*c2e18aaaSAndroid Build Coastguard Worker The BuildEnvProfiler object can help use determine whether a build is: 2256*c2e18aaaSAndroid Build Coastguard Worker 1. clean build. (empty out/ dir) 2257*c2e18aaaSAndroid Build Coastguard Worker 2. Build files Integrity (Android.bp/Android.mk changes). 2258*c2e18aaaSAndroid Build Coastguard Worker 3. Environment variables consistency. 2259*c2e18aaaSAndroid Build Coastguard Worker 4. New Ninja file generated. (mtime of soong/build.ninja) 2260*c2e18aaaSAndroid Build Coastguard Worker 2261*c2e18aaaSAndroid Build Coastguard Worker Returns: 2262*c2e18aaaSAndroid Build Coastguard Worker the BuildProfile object. 2263*c2e18aaaSAndroid Build Coastguard Worker """ 2264*c2e18aaaSAndroid Build Coastguard Worker out_dir = get_build_out_dir() 2265*c2e18aaaSAndroid Build Coastguard Worker ninja_file = out_dir.joinpath('soong/build.ninja') 2266*c2e18aaaSAndroid Build Coastguard Worker mtime = ninja_file.stat().st_mtime if ninja_file.is_file() else 0 2267*c2e18aaaSAndroid Build Coastguard Worker variables_file = out_dir.joinpath('soong/soong.environment.used.build') 2268*c2e18aaaSAndroid Build Coastguard Worker 2269*c2e18aaaSAndroid Build Coastguard Worker return BuildEnvProfiler( 2270*c2e18aaaSAndroid Build Coastguard Worker ninja_file=ninja_file, 2271*c2e18aaaSAndroid Build Coastguard Worker ninja_file_mtime=mtime, 2272*c2e18aaaSAndroid Build Coastguard Worker variable_file=variables_file, 2273*c2e18aaaSAndroid Build Coastguard Worker variable_file_md5=md5sum(variables_file), 2274*c2e18aaaSAndroid Build Coastguard Worker clean_out=not ninja_file.exists(), 2275*c2e18aaaSAndroid Build Coastguard Worker build_files_integrity=build_files_integrity_is_ok(), 2276*c2e18aaaSAndroid Build Coastguard Worker ) 2277*c2e18aaaSAndroid Build Coastguard Worker 2278*c2e18aaaSAndroid Build Coastguard Worker 2279*c2e18aaaSAndroid Build Coastguard Workerdef _send_build_condition_metrics( 2280*c2e18aaaSAndroid Build Coastguard Worker build_profile: BuildEnvProfiler, cmd: List[str] 2281*c2e18aaaSAndroid Build Coastguard Worker): 2282*c2e18aaaSAndroid Build Coastguard Worker """Send build conditions by comparing build env profilers.""" 2283*c2e18aaaSAndroid Build Coastguard Worker 2284*c2e18aaaSAndroid Build Coastguard Worker # when build module-info.json only, 'module-info.json' will be 2285*c2e18aaaSAndroid Build Coastguard Worker # the last element. 2286*c2e18aaaSAndroid Build Coastguard Worker m_mod_info_only = 'module-info.json' in cmd.pop() 2287*c2e18aaaSAndroid Build Coastguard Worker 2288*c2e18aaaSAndroid Build Coastguard Worker def ninja_file_is_changed(env_profiler: BuildEnvProfiler) -> bool: 2289*c2e18aaaSAndroid Build Coastguard Worker """Determine whether the ninja file had been renewal.""" 2290*c2e18aaaSAndroid Build Coastguard Worker if not env_profiler.ninja_file.is_file(): 2291*c2e18aaaSAndroid Build Coastguard Worker return True 2292*c2e18aaaSAndroid Build Coastguard Worker return ( 2293*c2e18aaaSAndroid Build Coastguard Worker env_profiler.ninja_file.stat().st_mtime != env_profiler.ninja_file_mtime 2294*c2e18aaaSAndroid Build Coastguard Worker ) 2295*c2e18aaaSAndroid Build Coastguard Worker 2296*c2e18aaaSAndroid Build Coastguard Worker def env_var_is_changed(env_profiler: BuildEnvProfiler) -> bool: 2297*c2e18aaaSAndroid Build Coastguard Worker """Determine whether soong-related variables had changed.""" 2298*c2e18aaaSAndroid Build Coastguard Worker return md5sum(env_profiler.variable_file) != env_profiler.variable_file_md5 2299*c2e18aaaSAndroid Build Coastguard Worker 2300*c2e18aaaSAndroid Build Coastguard Worker def send_data(detect_type, value=1): 2301*c2e18aaaSAndroid Build Coastguard Worker """A simple wrapper of metrics.LocalDetectEvent.""" 2302*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent(detect_type=detect_type, result=value) 2303*c2e18aaaSAndroid Build Coastguard Worker 2304*c2e18aaaSAndroid Build Coastguard Worker send_data(DetectType.RBE_STATE, get_rbe_and_customized_out_state()) 2305*c2e18aaaSAndroid Build Coastguard Worker 2306*c2e18aaaSAndroid Build Coastguard Worker # Determine the correct detect type before profiling. 2307*c2e18aaaSAndroid Build Coastguard Worker # (build module-info.json or build dependencies.) 2308*c2e18aaaSAndroid Build Coastguard Worker clean_out = ( 2309*c2e18aaaSAndroid Build Coastguard Worker DetectType.MODULE_INFO_CLEAN_OUT 2310*c2e18aaaSAndroid Build Coastguard Worker if m_mod_info_only 2311*c2e18aaaSAndroid Build Coastguard Worker else DetectType.BUILD_CLEAN_OUT 2312*c2e18aaaSAndroid Build Coastguard Worker ) 2313*c2e18aaaSAndroid Build Coastguard Worker ninja_generation = ( 2314*c2e18aaaSAndroid Build Coastguard Worker DetectType.MODULE_INFO_GEN_NINJA 2315*c2e18aaaSAndroid Build Coastguard Worker if m_mod_info_only 2316*c2e18aaaSAndroid Build Coastguard Worker else DetectType.BUILD_GEN_NINJA 2317*c2e18aaaSAndroid Build Coastguard Worker ) 2318*c2e18aaaSAndroid Build Coastguard Worker bpmk_change = ( 2319*c2e18aaaSAndroid Build Coastguard Worker DetectType.MODULE_INFO_BPMK_CHANGE 2320*c2e18aaaSAndroid Build Coastguard Worker if m_mod_info_only 2321*c2e18aaaSAndroid Build Coastguard Worker else DetectType.BUILD_BPMK_CHANGE 2322*c2e18aaaSAndroid Build Coastguard Worker ) 2323*c2e18aaaSAndroid Build Coastguard Worker env_change = ( 2324*c2e18aaaSAndroid Build Coastguard Worker DetectType.MODULE_INFO_ENV_CHANGE 2325*c2e18aaaSAndroid Build Coastguard Worker if m_mod_info_only 2326*c2e18aaaSAndroid Build Coastguard Worker else DetectType.BUILD_ENV_CHANGE 2327*c2e18aaaSAndroid Build Coastguard Worker ) 2328*c2e18aaaSAndroid Build Coastguard Worker src_change = ( 2329*c2e18aaaSAndroid Build Coastguard Worker DetectType.MODULE_INFO_SRC_CHANGE 2330*c2e18aaaSAndroid Build Coastguard Worker if m_mod_info_only 2331*c2e18aaaSAndroid Build Coastguard Worker else DetectType.BUILD_SRC_CHANGE 2332*c2e18aaaSAndroid Build Coastguard Worker ) 2333*c2e18aaaSAndroid Build Coastguard Worker other = ( 2334*c2e18aaaSAndroid Build Coastguard Worker DetectType.MODULE_INFO_OTHER 2335*c2e18aaaSAndroid Build Coastguard Worker if m_mod_info_only 2336*c2e18aaaSAndroid Build Coastguard Worker else DetectType.BUILD_OTHER 2337*c2e18aaaSAndroid Build Coastguard Worker ) 2338*c2e18aaaSAndroid Build Coastguard Worker incremental = ( 2339*c2e18aaaSAndroid Build Coastguard Worker DetectType.MODULE_INFO_INCREMENTAL 2340*c2e18aaaSAndroid Build Coastguard Worker if m_mod_info_only 2341*c2e18aaaSAndroid Build Coastguard Worker else DetectType.BUILD_INCREMENTAL 2342*c2e18aaaSAndroid Build Coastguard Worker ) 2343*c2e18aaaSAndroid Build Coastguard Worker 2344*c2e18aaaSAndroid Build Coastguard Worker if build_profile.clean_out: 2345*c2e18aaaSAndroid Build Coastguard Worker send_data(clean_out) 2346*c2e18aaaSAndroid Build Coastguard Worker else: 2347*c2e18aaaSAndroid Build Coastguard Worker send_data(incremental) 2348*c2e18aaaSAndroid Build Coastguard Worker 2349*c2e18aaaSAndroid Build Coastguard Worker if ninja_file_is_changed(build_profile): 2350*c2e18aaaSAndroid Build Coastguard Worker send_data(ninja_generation) 2351*c2e18aaaSAndroid Build Coastguard Worker 2352*c2e18aaaSAndroid Build Coastguard Worker other_condition = True 2353*c2e18aaaSAndroid Build Coastguard Worker if not build_profile.build_files_integrity: 2354*c2e18aaaSAndroid Build Coastguard Worker send_data(bpmk_change) 2355*c2e18aaaSAndroid Build Coastguard Worker other_condition = False 2356*c2e18aaaSAndroid Build Coastguard Worker if env_var_is_changed(build_profile): 2357*c2e18aaaSAndroid Build Coastguard Worker send_data(env_change) 2358*c2e18aaaSAndroid Build Coastguard Worker other_condition = False 2359*c2e18aaaSAndroid Build Coastguard Worker if bool(get_modified_files(os.getcwd())): 2360*c2e18aaaSAndroid Build Coastguard Worker send_data(src_change) 2361*c2e18aaaSAndroid Build Coastguard Worker other_condition = False 2362*c2e18aaaSAndroid Build Coastguard Worker if other_condition: 2363*c2e18aaaSAndroid Build Coastguard Worker send_data(other) 2364