1*c2e18aaaSAndroid Build Coastguard Worker# Copyright 2021, 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"""Implementation of Atest's Bazel mode. 16*c2e18aaaSAndroid Build Coastguard Worker 17*c2e18aaaSAndroid Build Coastguard WorkerBazel mode runs tests using Bazel by generating a synthetic workspace that 18*c2e18aaaSAndroid Build Coastguard Workercontains test targets. Using Bazel allows Atest to leverage features such as 19*c2e18aaaSAndroid Build Coastguard Workersandboxing, caching, and remote execution. 20*c2e18aaaSAndroid Build Coastguard Worker""" 21*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=missing-function-docstring 22*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=missing-class-docstring 23*c2e18aaaSAndroid Build Coastguard Worker# pylint: disable=too-many-lines 24*c2e18aaaSAndroid Build Coastguard Worker 25*c2e18aaaSAndroid Build Coastguard Workerfrom __future__ import annotations 26*c2e18aaaSAndroid Build Coastguard Worker 27*c2e18aaaSAndroid Build Coastguard Workerfrom abc import ABC, abstractmethod 28*c2e18aaaSAndroid Build Coastguard Workerimport argparse 29*c2e18aaaSAndroid Build Coastguard Workerimport atexit 30*c2e18aaaSAndroid Build Coastguard Workerfrom collections import OrderedDict, defaultdict, deque 31*c2e18aaaSAndroid Build Coastguard Workerfrom collections.abc import Iterable 32*c2e18aaaSAndroid Build Coastguard Workerimport contextlib 33*c2e18aaaSAndroid Build Coastguard Workerimport dataclasses 34*c2e18aaaSAndroid Build Coastguard Workerimport enum 35*c2e18aaaSAndroid Build Coastguard Workerimport functools 36*c2e18aaaSAndroid Build Coastguard Workerimport importlib.resources 37*c2e18aaaSAndroid Build Coastguard Workerimport logging 38*c2e18aaaSAndroid Build Coastguard Workerimport os 39*c2e18aaaSAndroid Build Coastguard Workerimport pathlib 40*c2e18aaaSAndroid Build Coastguard Workerimport re 41*c2e18aaaSAndroid Build Coastguard Workerimport shlex 42*c2e18aaaSAndroid Build Coastguard Workerimport shutil 43*c2e18aaaSAndroid Build Coastguard Workerimport subprocess 44*c2e18aaaSAndroid Build Coastguard Workerimport tempfile 45*c2e18aaaSAndroid Build Coastguard Workerimport time 46*c2e18aaaSAndroid Build Coastguard Workerfrom types import MappingProxyType 47*c2e18aaaSAndroid Build Coastguard Workerfrom typing import Any, Callable, Dict, IO, List, Set, Tuple 48*c2e18aaaSAndroid Build Coastguard Workerimport warnings 49*c2e18aaaSAndroid Build Coastguard Workerfrom xml.etree import ElementTree as ET 50*c2e18aaaSAndroid Build Coastguard Worker 51*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_configs 52*c2e18aaaSAndroid Build Coastguard Workerfrom atest import atest_utils 53*c2e18aaaSAndroid Build Coastguard Workerfrom atest import constants 54*c2e18aaaSAndroid Build Coastguard Workerfrom atest import module_info 55*c2e18aaaSAndroid Build Coastguard Workerfrom atest.atest_enum import DetectType, ExitCode 56*c2e18aaaSAndroid Build Coastguard Workerfrom atest.metrics import metrics 57*c2e18aaaSAndroid Build Coastguard Workerfrom atest.proto import file_md5_pb2 58*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_finders import test_finder_base 59*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_finders import test_info 60*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_runners import atest_tf_test_runner as tfr 61*c2e18aaaSAndroid Build Coastguard Workerfrom atest.test_runners import test_runner_base as trb 62*c2e18aaaSAndroid Build Coastguard Workerfrom google.protobuf.message import DecodeError 63*c2e18aaaSAndroid Build Coastguard Worker 64*c2e18aaaSAndroid Build Coastguard Worker 65*c2e18aaaSAndroid Build Coastguard WorkerJDK_PACKAGE_NAME = 'prebuilts/robolectric_jdk' 66*c2e18aaaSAndroid Build Coastguard WorkerJDK_NAME = 'jdk' 67*c2e18aaaSAndroid Build Coastguard WorkerROBOLECTRIC_CONFIG = 'build/make/core/robolectric_test_config_template.xml' 68*c2e18aaaSAndroid Build Coastguard Worker 69*c2e18aaaSAndroid Build Coastguard WorkerBAZEL_TEST_LOGS_DIR_NAME = 'bazel-testlogs' 70*c2e18aaaSAndroid Build Coastguard WorkerTEST_OUTPUT_DIR_NAME = 'test.outputs' 71*c2e18aaaSAndroid Build Coastguard WorkerTEST_OUTPUT_ZIP_NAME = 'outputs.zip' 72*c2e18aaaSAndroid Build Coastguard Worker 73*c2e18aaaSAndroid Build Coastguard Worker_BAZEL_WORKSPACE_DIR = 'atest_bazel_workspace' 74*c2e18aaaSAndroid Build Coastguard Worker_SUPPORTED_BAZEL_ARGS = MappingProxyType({ 75*c2e18aaaSAndroid Build Coastguard Worker # https://docs.bazel.build/versions/main/command-line-reference.html#flag--runs_per_test 76*c2e18aaaSAndroid Build Coastguard Worker constants.ITERATIONS: lambda arg_value: [ 77*c2e18aaaSAndroid Build Coastguard Worker f'--runs_per_test={str(arg_value)}' 78*c2e18aaaSAndroid Build Coastguard Worker ], 79*c2e18aaaSAndroid Build Coastguard Worker # https://docs.bazel.build/versions/main/command-line-reference.html#flag--flaky_test_attempts 80*c2e18aaaSAndroid Build Coastguard Worker constants.RETRY_ANY_FAILURE: lambda arg_value: [ 81*c2e18aaaSAndroid Build Coastguard Worker f'--flaky_test_attempts={str(arg_value)}' 82*c2e18aaaSAndroid Build Coastguard Worker ], 83*c2e18aaaSAndroid Build Coastguard Worker # https://docs.bazel.build/versions/main/command-line-reference.html#flag--test_output 84*c2e18aaaSAndroid Build Coastguard Worker constants.VERBOSE: ( 85*c2e18aaaSAndroid Build Coastguard Worker lambda arg_value: ['--test_output=all'] if arg_value else [] 86*c2e18aaaSAndroid Build Coastguard Worker ), 87*c2e18aaaSAndroid Build Coastguard Worker constants.BAZEL_ARG: lambda arg_value: [ 88*c2e18aaaSAndroid Build Coastguard Worker item for sublist in arg_value for item in sublist 89*c2e18aaaSAndroid Build Coastguard Worker ], 90*c2e18aaaSAndroid Build Coastguard Worker}) 91*c2e18aaaSAndroid Build Coastguard Worker 92*c2e18aaaSAndroid Build Coastguard Worker# Maps Bazel configuration names to Soong variant names. 93*c2e18aaaSAndroid Build Coastguard Worker_CONFIG_TO_VARIANT = { 94*c2e18aaaSAndroid Build Coastguard Worker 'host': 'host', 95*c2e18aaaSAndroid Build Coastguard Worker 'device': 'target', 96*c2e18aaaSAndroid Build Coastguard Worker} 97*c2e18aaaSAndroid Build Coastguard Worker 98*c2e18aaaSAndroid Build Coastguard Worker 99*c2e18aaaSAndroid Build Coastguard Workerclass AbortRunException(Exception): 100*c2e18aaaSAndroid Build Coastguard Worker pass 101*c2e18aaaSAndroid Build Coastguard Worker 102*c2e18aaaSAndroid Build Coastguard Worker 103*c2e18aaaSAndroid Build Coastguard Worker@enum.unique 104*c2e18aaaSAndroid Build Coastguard Workerclass Features(enum.Enum): 105*c2e18aaaSAndroid Build Coastguard Worker NULL_FEATURE = ('--null-feature', 'Enables a no-action feature.', True) 106*c2e18aaaSAndroid Build Coastguard Worker EXPERIMENTAL_DEVICE_DRIVEN_TEST = ( 107*c2e18aaaSAndroid Build Coastguard Worker '--experimental-device-driven-test', 108*c2e18aaaSAndroid Build Coastguard Worker 'Enables running device-driven tests in Bazel mode.', 109*c2e18aaaSAndroid Build Coastguard Worker True, 110*c2e18aaaSAndroid Build Coastguard Worker ) 111*c2e18aaaSAndroid Build Coastguard Worker EXPERIMENTAL_REMOTE_AVD = ( 112*c2e18aaaSAndroid Build Coastguard Worker '--experimental-remote-avd', 113*c2e18aaaSAndroid Build Coastguard Worker 'Enables running device-driven tests in remote AVD.', 114*c2e18aaaSAndroid Build Coastguard Worker False, 115*c2e18aaaSAndroid Build Coastguard Worker ) 116*c2e18aaaSAndroid Build Coastguard Worker EXPERIMENTAL_BES_PUBLISH = ( 117*c2e18aaaSAndroid Build Coastguard Worker '--experimental-bes-publish', 118*c2e18aaaSAndroid Build Coastguard Worker 'Upload test results via BES in Bazel mode.', 119*c2e18aaaSAndroid Build Coastguard Worker False, 120*c2e18aaaSAndroid Build Coastguard Worker ) 121*c2e18aaaSAndroid Build Coastguard Worker EXPERIMENTAL_JAVA_RUNTIME_DEPENDENCIES = ( 122*c2e18aaaSAndroid Build Coastguard Worker '--experimental-java-runtime-dependencies', 123*c2e18aaaSAndroid Build Coastguard Worker ( 124*c2e18aaaSAndroid Build Coastguard Worker 'Mirrors Soong Java `libs` and `static_libs` as Bazel target ' 125*c2e18aaaSAndroid Build Coastguard Worker 'dependencies in the generated workspace. Tradefed test rules use ' 126*c2e18aaaSAndroid Build Coastguard Worker 'these dependencies to set up the execution environment and ensure ' 127*c2e18aaaSAndroid Build Coastguard Worker 'that all transitive runtime dependencies are present.' 128*c2e18aaaSAndroid Build Coastguard Worker ), 129*c2e18aaaSAndroid Build Coastguard Worker True, 130*c2e18aaaSAndroid Build Coastguard Worker ) 131*c2e18aaaSAndroid Build Coastguard Worker EXPERIMENTAL_REMOTE = ( 132*c2e18aaaSAndroid Build Coastguard Worker '--experimental-remote', 133*c2e18aaaSAndroid Build Coastguard Worker 'Use Bazel remote execution and caching where supported.', 134*c2e18aaaSAndroid Build Coastguard Worker False, 135*c2e18aaaSAndroid Build Coastguard Worker ) 136*c2e18aaaSAndroid Build Coastguard Worker EXPERIMENTAL_HOST_DRIVEN_TEST = ( 137*c2e18aaaSAndroid Build Coastguard Worker '--experimental-host-driven-test', 138*c2e18aaaSAndroid Build Coastguard Worker 'Enables running host-driven device tests in Bazel mode.', 139*c2e18aaaSAndroid Build Coastguard Worker True, 140*c2e18aaaSAndroid Build Coastguard Worker ) 141*c2e18aaaSAndroid Build Coastguard Worker EXPERIMENTAL_ROBOLECTRIC_TEST = ( 142*c2e18aaaSAndroid Build Coastguard Worker '--experimental-robolectric-test', 143*c2e18aaaSAndroid Build Coastguard Worker 'Enables running Robolectric tests in Bazel mode.', 144*c2e18aaaSAndroid Build Coastguard Worker True, 145*c2e18aaaSAndroid Build Coastguard Worker ) 146*c2e18aaaSAndroid Build Coastguard Worker NO_BAZEL_DETAILED_SUMMARY = ( 147*c2e18aaaSAndroid Build Coastguard Worker '--no-bazel-detailed-summary', 148*c2e18aaaSAndroid Build Coastguard Worker 'Disables printing detailed summary of Bazel test results.', 149*c2e18aaaSAndroid Build Coastguard Worker False, 150*c2e18aaaSAndroid Build Coastguard Worker ) 151*c2e18aaaSAndroid Build Coastguard Worker 152*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, arg_flag, description, affects_workspace): 153*c2e18aaaSAndroid Build Coastguard Worker self._arg_flag = arg_flag 154*c2e18aaaSAndroid Build Coastguard Worker self._description = description 155*c2e18aaaSAndroid Build Coastguard Worker self.affects_workspace = affects_workspace 156*c2e18aaaSAndroid Build Coastguard Worker 157*c2e18aaaSAndroid Build Coastguard Worker @property 158*c2e18aaaSAndroid Build Coastguard Worker def arg_flag(self): 159*c2e18aaaSAndroid Build Coastguard Worker return self._arg_flag 160*c2e18aaaSAndroid Build Coastguard Worker 161*c2e18aaaSAndroid Build Coastguard Worker @property 162*c2e18aaaSAndroid Build Coastguard Worker def description(self): 163*c2e18aaaSAndroid Build Coastguard Worker return self._description 164*c2e18aaaSAndroid Build Coastguard Worker 165*c2e18aaaSAndroid Build Coastguard Worker 166*c2e18aaaSAndroid Build Coastguard Workerdef add_parser_arguments(parser: argparse.ArgumentParser, dest: str): 167*c2e18aaaSAndroid Build Coastguard Worker for _, member in Features.__members__.items(): 168*c2e18aaaSAndroid Build Coastguard Worker parser.add_argument( 169*c2e18aaaSAndroid Build Coastguard Worker member.arg_flag, 170*c2e18aaaSAndroid Build Coastguard Worker action='append_const', 171*c2e18aaaSAndroid Build Coastguard Worker const=member, 172*c2e18aaaSAndroid Build Coastguard Worker dest=dest, 173*c2e18aaaSAndroid Build Coastguard Worker help=member.description, 174*c2e18aaaSAndroid Build Coastguard Worker ) 175*c2e18aaaSAndroid Build Coastguard Worker 176*c2e18aaaSAndroid Build Coastguard Worker 177*c2e18aaaSAndroid Build Coastguard Workerdef get_bazel_workspace_dir() -> pathlib.Path: 178*c2e18aaaSAndroid Build Coastguard Worker return atest_utils.get_build_out_dir(_BAZEL_WORKSPACE_DIR) 179*c2e18aaaSAndroid Build Coastguard Worker 180*c2e18aaaSAndroid Build Coastguard Worker 181*c2e18aaaSAndroid Build Coastguard Workerdef generate_bazel_workspace( 182*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, enabled_features: Set[Features] = None 183*c2e18aaaSAndroid Build Coastguard Worker): 184*c2e18aaaSAndroid Build Coastguard Worker """Generate or update the Bazel workspace used for running tests.""" 185*c2e18aaaSAndroid Build Coastguard Worker 186*c2e18aaaSAndroid Build Coastguard Worker start = time.time() 187*c2e18aaaSAndroid Build Coastguard Worker src_root_path = pathlib.Path(os.environ.get(constants.ANDROID_BUILD_TOP)) 188*c2e18aaaSAndroid Build Coastguard Worker workspace_path = get_bazel_workspace_dir() 189*c2e18aaaSAndroid Build Coastguard Worker resource_manager = ResourceManager( 190*c2e18aaaSAndroid Build Coastguard Worker src_root_path=src_root_path, 191*c2e18aaaSAndroid Build Coastguard Worker resource_root_path=_get_resource_root(), 192*c2e18aaaSAndroid Build Coastguard Worker product_out_path=pathlib.Path( 193*c2e18aaaSAndroid Build Coastguard Worker os.environ.get(constants.ANDROID_PRODUCT_OUT) 194*c2e18aaaSAndroid Build Coastguard Worker ), 195*c2e18aaaSAndroid Build Coastguard Worker md5_checksum_file_path=workspace_path.joinpath('workspace_md5_checksum'), 196*c2e18aaaSAndroid Build Coastguard Worker ) 197*c2e18aaaSAndroid Build Coastguard Worker jdk_path = _read_robolectric_jdk_path( 198*c2e18aaaSAndroid Build Coastguard Worker resource_manager.get_src_file_path(ROBOLECTRIC_CONFIG, True) 199*c2e18aaaSAndroid Build Coastguard Worker ) 200*c2e18aaaSAndroid Build Coastguard Worker 201*c2e18aaaSAndroid Build Coastguard Worker workspace_generator = WorkspaceGenerator( 202*c2e18aaaSAndroid Build Coastguard Worker resource_manager=resource_manager, 203*c2e18aaaSAndroid Build Coastguard Worker workspace_out_path=workspace_path, 204*c2e18aaaSAndroid Build Coastguard Worker host_out_path=pathlib.Path(os.environ.get(constants.ANDROID_HOST_OUT)), 205*c2e18aaaSAndroid Build Coastguard Worker build_out_dir=atest_utils.get_build_out_dir(), 206*c2e18aaaSAndroid Build Coastguard Worker mod_info=mod_info, 207*c2e18aaaSAndroid Build Coastguard Worker jdk_path=jdk_path, 208*c2e18aaaSAndroid Build Coastguard Worker enabled_features=enabled_features, 209*c2e18aaaSAndroid Build Coastguard Worker ) 210*c2e18aaaSAndroid Build Coastguard Worker workspace_generator.generate() 211*c2e18aaaSAndroid Build Coastguard Worker 212*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 213*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.BAZEL_WORKSPACE_GENERATE_TIME, 214*c2e18aaaSAndroid Build Coastguard Worker result=int(time.time() - start), 215*c2e18aaaSAndroid Build Coastguard Worker ) 216*c2e18aaaSAndroid Build Coastguard Worker 217*c2e18aaaSAndroid Build Coastguard Worker 218*c2e18aaaSAndroid Build Coastguard Workerdef get_default_build_metadata(): 219*c2e18aaaSAndroid Build Coastguard Worker return BuildMetadata( 220*c2e18aaaSAndroid Build Coastguard Worker atest_utils.get_manifest_branch(), atest_utils.get_build_target() 221*c2e18aaaSAndroid Build Coastguard Worker ) 222*c2e18aaaSAndroid Build Coastguard Worker 223*c2e18aaaSAndroid Build Coastguard Worker 224*c2e18aaaSAndroid Build Coastguard Workerclass ResourceManager: 225*c2e18aaaSAndroid Build Coastguard Worker """Class for managing files required to generate a Bazel Workspace.""" 226*c2e18aaaSAndroid Build Coastguard Worker 227*c2e18aaaSAndroid Build Coastguard Worker def __init__( 228*c2e18aaaSAndroid Build Coastguard Worker self, 229*c2e18aaaSAndroid Build Coastguard Worker src_root_path: pathlib.Path, 230*c2e18aaaSAndroid Build Coastguard Worker resource_root_path: pathlib.Path, 231*c2e18aaaSAndroid Build Coastguard Worker product_out_path: pathlib.Path, 232*c2e18aaaSAndroid Build Coastguard Worker md5_checksum_file_path: pathlib.Path, 233*c2e18aaaSAndroid Build Coastguard Worker ): 234*c2e18aaaSAndroid Build Coastguard Worker self._root_type_to_path = { 235*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.RootType.SRC_ROOT: src_root_path, 236*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.RootType.RESOURCE_ROOT: resource_root_path, 237*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.RootType.ABS_PATH: pathlib.Path(), 238*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.RootType.PRODUCT_OUT: product_out_path, 239*c2e18aaaSAndroid Build Coastguard Worker } 240*c2e18aaaSAndroid Build Coastguard Worker self._md5_checksum_file = md5_checksum_file_path 241*c2e18aaaSAndroid Build Coastguard Worker self._file_checksum_list = file_md5_pb2.FileChecksumList() 242*c2e18aaaSAndroid Build Coastguard Worker 243*c2e18aaaSAndroid Build Coastguard Worker def get_src_file_path( 244*c2e18aaaSAndroid Build Coastguard Worker self, rel_path: pathlib.Path = None, affects_workspace: bool = False 245*c2e18aaaSAndroid Build Coastguard Worker ) -> pathlib.Path: 246*c2e18aaaSAndroid Build Coastguard Worker """Get the abs file path from the relative path of source_root. 247*c2e18aaaSAndroid Build Coastguard Worker 248*c2e18aaaSAndroid Build Coastguard Worker Args: 249*c2e18aaaSAndroid Build Coastguard Worker rel_path: A relative path of the source_root. 250*c2e18aaaSAndroid Build Coastguard Worker affects_workspace: A boolean of whether the file affects the workspace. 251*c2e18aaaSAndroid Build Coastguard Worker 252*c2e18aaaSAndroid Build Coastguard Worker Returns: 253*c2e18aaaSAndroid Build Coastguard Worker A abs path of the file. 254*c2e18aaaSAndroid Build Coastguard Worker """ 255*c2e18aaaSAndroid Build Coastguard Worker return self._get_file_path( 256*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.RootType.SRC_ROOT, rel_path, affects_workspace 257*c2e18aaaSAndroid Build Coastguard Worker ) 258*c2e18aaaSAndroid Build Coastguard Worker 259*c2e18aaaSAndroid Build Coastguard Worker def get_resource_file_path( 260*c2e18aaaSAndroid Build Coastguard Worker self, 261*c2e18aaaSAndroid Build Coastguard Worker rel_path: pathlib.Path = None, 262*c2e18aaaSAndroid Build Coastguard Worker affects_workspace: bool = False, 263*c2e18aaaSAndroid Build Coastguard Worker ) -> pathlib.Path: 264*c2e18aaaSAndroid Build Coastguard Worker """Get the abs file path from the relative path of resource_root. 265*c2e18aaaSAndroid Build Coastguard Worker 266*c2e18aaaSAndroid Build Coastguard Worker Args: 267*c2e18aaaSAndroid Build Coastguard Worker rel_path: A relative path of the resource_root. 268*c2e18aaaSAndroid Build Coastguard Worker affects_workspace: A boolean of whether the file affects the workspace. 269*c2e18aaaSAndroid Build Coastguard Worker 270*c2e18aaaSAndroid Build Coastguard Worker Returns: 271*c2e18aaaSAndroid Build Coastguard Worker A abs path of the file. 272*c2e18aaaSAndroid Build Coastguard Worker """ 273*c2e18aaaSAndroid Build Coastguard Worker return self._get_file_path( 274*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.RootType.RESOURCE_ROOT, rel_path, affects_workspace 275*c2e18aaaSAndroid Build Coastguard Worker ) 276*c2e18aaaSAndroid Build Coastguard Worker 277*c2e18aaaSAndroid Build Coastguard Worker def get_product_out_file_path( 278*c2e18aaaSAndroid Build Coastguard Worker self, rel_path: pathlib.Path = None, affects_workspace: bool = False 279*c2e18aaaSAndroid Build Coastguard Worker ) -> pathlib.Path: 280*c2e18aaaSAndroid Build Coastguard Worker """Get the abs file path from the relative path of product out. 281*c2e18aaaSAndroid Build Coastguard Worker 282*c2e18aaaSAndroid Build Coastguard Worker Args: 283*c2e18aaaSAndroid Build Coastguard Worker rel_path: A relative path to the product out. 284*c2e18aaaSAndroid Build Coastguard Worker affects_workspace: A boolean of whether the file affects the workspace. 285*c2e18aaaSAndroid Build Coastguard Worker 286*c2e18aaaSAndroid Build Coastguard Worker Returns: 287*c2e18aaaSAndroid Build Coastguard Worker An abs path of the file. 288*c2e18aaaSAndroid Build Coastguard Worker """ 289*c2e18aaaSAndroid Build Coastguard Worker return self._get_file_path( 290*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.RootType.PRODUCT_OUT, rel_path, affects_workspace 291*c2e18aaaSAndroid Build Coastguard Worker ) 292*c2e18aaaSAndroid Build Coastguard Worker 293*c2e18aaaSAndroid Build Coastguard Worker def _get_file_path( 294*c2e18aaaSAndroid Build Coastguard Worker self, 295*c2e18aaaSAndroid Build Coastguard Worker root_type: file_md5_pb2.RootType, 296*c2e18aaaSAndroid Build Coastguard Worker rel_path: pathlib.Path, 297*c2e18aaaSAndroid Build Coastguard Worker affects_workspace: bool = True, 298*c2e18aaaSAndroid Build Coastguard Worker ) -> pathlib.Path: 299*c2e18aaaSAndroid Build Coastguard Worker abs_path = self._root_type_to_path[root_type].joinpath( 300*c2e18aaaSAndroid Build Coastguard Worker rel_path or pathlib.Path() 301*c2e18aaaSAndroid Build Coastguard Worker ) 302*c2e18aaaSAndroid Build Coastguard Worker 303*c2e18aaaSAndroid Build Coastguard Worker if not affects_workspace: 304*c2e18aaaSAndroid Build Coastguard Worker return abs_path 305*c2e18aaaSAndroid Build Coastguard Worker 306*c2e18aaaSAndroid Build Coastguard Worker if abs_path.is_dir(): 307*c2e18aaaSAndroid Build Coastguard Worker for file in abs_path.glob('**/*'): 308*c2e18aaaSAndroid Build Coastguard Worker self._register_file(root_type, file) 309*c2e18aaaSAndroid Build Coastguard Worker else: 310*c2e18aaaSAndroid Build Coastguard Worker self._register_file(root_type, abs_path) 311*c2e18aaaSAndroid Build Coastguard Worker return abs_path 312*c2e18aaaSAndroid Build Coastguard Worker 313*c2e18aaaSAndroid Build Coastguard Worker def _register_file( 314*c2e18aaaSAndroid Build Coastguard Worker self, root_type: file_md5_pb2.RootType, abs_path: pathlib.Path 315*c2e18aaaSAndroid Build Coastguard Worker ): 316*c2e18aaaSAndroid Build Coastguard Worker if not abs_path.is_file(): 317*c2e18aaaSAndroid Build Coastguard Worker logging.debug(' ignore %s: not a file.', abs_path) 318*c2e18aaaSAndroid Build Coastguard Worker return 319*c2e18aaaSAndroid Build Coastguard Worker 320*c2e18aaaSAndroid Build Coastguard Worker rel_path = abs_path 321*c2e18aaaSAndroid Build Coastguard Worker if abs_path.is_relative_to(self._root_type_to_path[root_type]): 322*c2e18aaaSAndroid Build Coastguard Worker rel_path = abs_path.relative_to(self._root_type_to_path[root_type]) 323*c2e18aaaSAndroid Build Coastguard Worker 324*c2e18aaaSAndroid Build Coastguard Worker self._file_checksum_list.file_checksums.append( 325*c2e18aaaSAndroid Build Coastguard Worker file_md5_pb2.FileChecksum( 326*c2e18aaaSAndroid Build Coastguard Worker root_type=root_type, 327*c2e18aaaSAndroid Build Coastguard Worker rel_path=str(rel_path), 328*c2e18aaaSAndroid Build Coastguard Worker md5sum=atest_utils.md5sum(abs_path), 329*c2e18aaaSAndroid Build Coastguard Worker ) 330*c2e18aaaSAndroid Build Coastguard Worker ) 331*c2e18aaaSAndroid Build Coastguard Worker 332*c2e18aaaSAndroid Build Coastguard Worker def register_file_with_abs_path(self, abs_path: pathlib.Path): 333*c2e18aaaSAndroid Build Coastguard Worker """Register a file which affects the workspace. 334*c2e18aaaSAndroid Build Coastguard Worker 335*c2e18aaaSAndroid Build Coastguard Worker Args: 336*c2e18aaaSAndroid Build Coastguard Worker abs_path: A abs path of the file. 337*c2e18aaaSAndroid Build Coastguard Worker """ 338*c2e18aaaSAndroid Build Coastguard Worker self._register_file(file_md5_pb2.RootType.ABS_PATH, abs_path) 339*c2e18aaaSAndroid Build Coastguard Worker 340*c2e18aaaSAndroid Build Coastguard Worker def save_affects_files_md5(self): 341*c2e18aaaSAndroid Build Coastguard Worker with open(self._md5_checksum_file, 'wb') as f: 342*c2e18aaaSAndroid Build Coastguard Worker f.write(self._file_checksum_list.SerializeToString()) 343*c2e18aaaSAndroid Build Coastguard Worker 344*c2e18aaaSAndroid Build Coastguard Worker def check_affects_files_md5(self): 345*c2e18aaaSAndroid Build Coastguard Worker """Check all affect files are consistent with the actual MD5.""" 346*c2e18aaaSAndroid Build Coastguard Worker if not self._md5_checksum_file.is_file(): 347*c2e18aaaSAndroid Build Coastguard Worker return False 348*c2e18aaaSAndroid Build Coastguard Worker 349*c2e18aaaSAndroid Build Coastguard Worker with open(self._md5_checksum_file, 'rb') as f: 350*c2e18aaaSAndroid Build Coastguard Worker file_md5_list = file_md5_pb2.FileChecksumList() 351*c2e18aaaSAndroid Build Coastguard Worker 352*c2e18aaaSAndroid Build Coastguard Worker try: 353*c2e18aaaSAndroid Build Coastguard Worker file_md5_list.ParseFromString(f.read()) 354*c2e18aaaSAndroid Build Coastguard Worker except DecodeError: 355*c2e18aaaSAndroid Build Coastguard Worker atest_utils.print_and_log_warning( 356*c2e18aaaSAndroid Build Coastguard Worker 'Failed to parse the workspace md5 checksum file.' 357*c2e18aaaSAndroid Build Coastguard Worker ) 358*c2e18aaaSAndroid Build Coastguard Worker return False 359*c2e18aaaSAndroid Build Coastguard Worker 360*c2e18aaaSAndroid Build Coastguard Worker for file_md5 in file_md5_list.file_checksums: 361*c2e18aaaSAndroid Build Coastguard Worker abs_path = pathlib.Path( 362*c2e18aaaSAndroid Build Coastguard Worker self._root_type_to_path[file_md5.root_type] 363*c2e18aaaSAndroid Build Coastguard Worker ).joinpath(file_md5.rel_path) 364*c2e18aaaSAndroid Build Coastguard Worker if not abs_path.is_file(): 365*c2e18aaaSAndroid Build Coastguard Worker return False 366*c2e18aaaSAndroid Build Coastguard Worker if atest_utils.md5sum(abs_path) != file_md5.md5sum: 367*c2e18aaaSAndroid Build Coastguard Worker return False 368*c2e18aaaSAndroid Build Coastguard Worker return True 369*c2e18aaaSAndroid Build Coastguard Worker 370*c2e18aaaSAndroid Build Coastguard Worker 371*c2e18aaaSAndroid Build Coastguard Workerclass WorkspaceGenerator: 372*c2e18aaaSAndroid Build Coastguard Worker """Class for generating a Bazel workspace.""" 373*c2e18aaaSAndroid Build Coastguard Worker 374*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=too-many-arguments 375*c2e18aaaSAndroid Build Coastguard Worker def __init__( 376*c2e18aaaSAndroid Build Coastguard Worker self, 377*c2e18aaaSAndroid Build Coastguard Worker resource_manager: ResourceManager, 378*c2e18aaaSAndroid Build Coastguard Worker workspace_out_path: pathlib.Path, 379*c2e18aaaSAndroid Build Coastguard Worker host_out_path: pathlib.Path, 380*c2e18aaaSAndroid Build Coastguard Worker build_out_dir: pathlib.Path, 381*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, 382*c2e18aaaSAndroid Build Coastguard Worker jdk_path: pathlib.Path = None, 383*c2e18aaaSAndroid Build Coastguard Worker enabled_features: Set[Features] = None, 384*c2e18aaaSAndroid Build Coastguard Worker ): 385*c2e18aaaSAndroid Build Coastguard Worker """Initializes the generator. 386*c2e18aaaSAndroid Build Coastguard Worker 387*c2e18aaaSAndroid Build Coastguard Worker Args: 388*c2e18aaaSAndroid Build Coastguard Worker workspace_out_path: Path where the workspace will be output. 389*c2e18aaaSAndroid Build Coastguard Worker host_out_path: Path of the ANDROID_HOST_OUT. 390*c2e18aaaSAndroid Build Coastguard Worker build_out_dir: Path of OUT_DIR 391*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo object. 392*c2e18aaaSAndroid Build Coastguard Worker enabled_features: Set of enabled features. 393*c2e18aaaSAndroid Build Coastguard Worker """ 394*c2e18aaaSAndroid Build Coastguard Worker if ( 395*c2e18aaaSAndroid Build Coastguard Worker enabled_features 396*c2e18aaaSAndroid Build Coastguard Worker and Features.EXPERIMENTAL_REMOTE_AVD in enabled_features 397*c2e18aaaSAndroid Build Coastguard Worker and Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST not in enabled_features 398*c2e18aaaSAndroid Build Coastguard Worker ): 399*c2e18aaaSAndroid Build Coastguard Worker raise ValueError( 400*c2e18aaaSAndroid Build Coastguard Worker 'Cannot run remote device test because ' 401*c2e18aaaSAndroid Build Coastguard Worker '"--experimental-device-driven-test" flag is' 402*c2e18aaaSAndroid Build Coastguard Worker ' not set.' 403*c2e18aaaSAndroid Build Coastguard Worker ) 404*c2e18aaaSAndroid Build Coastguard Worker self.enabled_features = enabled_features or set() 405*c2e18aaaSAndroid Build Coastguard Worker self.resource_manager = resource_manager 406*c2e18aaaSAndroid Build Coastguard Worker self.workspace_out_path = workspace_out_path 407*c2e18aaaSAndroid Build Coastguard Worker self.host_out_path = host_out_path 408*c2e18aaaSAndroid Build Coastguard Worker self.build_out_dir = build_out_dir 409*c2e18aaaSAndroid Build Coastguard Worker self.mod_info = mod_info 410*c2e18aaaSAndroid Build Coastguard Worker self.path_to_package = {} 411*c2e18aaaSAndroid Build Coastguard Worker self.jdk_path = jdk_path 412*c2e18aaaSAndroid Build Coastguard Worker 413*c2e18aaaSAndroid Build Coastguard Worker def generate(self): 414*c2e18aaaSAndroid Build Coastguard Worker """Generate a Bazel workspace. 415*c2e18aaaSAndroid Build Coastguard Worker 416*c2e18aaaSAndroid Build Coastguard Worker If the workspace md5 checksum file doesn't exist or is stale, a new 417*c2e18aaaSAndroid Build Coastguard Worker workspace will be generated. Otherwise, the existing workspace will be 418*c2e18aaaSAndroid Build Coastguard Worker reused. 419*c2e18aaaSAndroid Build Coastguard Worker """ 420*c2e18aaaSAndroid Build Coastguard Worker start = time.time() 421*c2e18aaaSAndroid Build Coastguard Worker enabled_features_file = self.workspace_out_path.joinpath( 422*c2e18aaaSAndroid Build Coastguard Worker 'atest_bazel_mode_enabled_features' 423*c2e18aaaSAndroid Build Coastguard Worker ) 424*c2e18aaaSAndroid Build Coastguard Worker enabled_features_file_contents = '\n'.join( 425*c2e18aaaSAndroid Build Coastguard Worker sorted(f.name for f in self.enabled_features if f.affects_workspace) 426*c2e18aaaSAndroid Build Coastguard Worker ) 427*c2e18aaaSAndroid Build Coastguard Worker 428*c2e18aaaSAndroid Build Coastguard Worker if self.workspace_out_path.exists(): 429*c2e18aaaSAndroid Build Coastguard Worker # Update the file with the set of the currently enabled features to 430*c2e18aaaSAndroid Build Coastguard Worker # make sure that changes are detected in the workspace checksum. 431*c2e18aaaSAndroid Build Coastguard Worker enabled_features_file.write_text(enabled_features_file_contents) 432*c2e18aaaSAndroid Build Coastguard Worker if self.resource_manager.check_affects_files_md5(): 433*c2e18aaaSAndroid Build Coastguard Worker return 434*c2e18aaaSAndroid Build Coastguard Worker 435*c2e18aaaSAndroid Build Coastguard Worker # We raise an exception if rmtree fails to avoid leaving stale 436*c2e18aaaSAndroid Build Coastguard Worker # files in the workspace that could interfere with execution. 437*c2e18aaaSAndroid Build Coastguard Worker shutil.rmtree(self.workspace_out_path) 438*c2e18aaaSAndroid Build Coastguard Worker 439*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print('Generating Bazel workspace.\n', constants.RED) 440*c2e18aaaSAndroid Build Coastguard Worker 441*c2e18aaaSAndroid Build Coastguard Worker self._add_test_module_targets() 442*c2e18aaaSAndroid Build Coastguard Worker 443*c2e18aaaSAndroid Build Coastguard Worker self.workspace_out_path.mkdir(parents=True) 444*c2e18aaaSAndroid Build Coastguard Worker self._generate_artifacts() 445*c2e18aaaSAndroid Build Coastguard Worker 446*c2e18aaaSAndroid Build Coastguard Worker # Note that we write the set of enabled features despite having written 447*c2e18aaaSAndroid Build Coastguard Worker # it above since the workspace no longer exists at this point. 448*c2e18aaaSAndroid Build Coastguard Worker enabled_features_file.write_text(enabled_features_file_contents) 449*c2e18aaaSAndroid Build Coastguard Worker 450*c2e18aaaSAndroid Build Coastguard Worker self.resource_manager.get_product_out_file_path( 451*c2e18aaaSAndroid Build Coastguard Worker self.mod_info.mod_info_file_path.relative_to( 452*c2e18aaaSAndroid Build Coastguard Worker self.resource_manager.get_product_out_file_path() 453*c2e18aaaSAndroid Build Coastguard Worker ), 454*c2e18aaaSAndroid Build Coastguard Worker True, 455*c2e18aaaSAndroid Build Coastguard Worker ) 456*c2e18aaaSAndroid Build Coastguard Worker self.resource_manager.register_file_with_abs_path(enabled_features_file) 457*c2e18aaaSAndroid Build Coastguard Worker self.resource_manager.save_affects_files_md5() 458*c2e18aaaSAndroid Build Coastguard Worker metrics.LocalDetectEvent( 459*c2e18aaaSAndroid Build Coastguard Worker detect_type=DetectType.FULL_GENERATE_BAZEL_WORKSPACE_TIME, 460*c2e18aaaSAndroid Build Coastguard Worker result=int(time.time() - start), 461*c2e18aaaSAndroid Build Coastguard Worker ) 462*c2e18aaaSAndroid Build Coastguard Worker 463*c2e18aaaSAndroid Build Coastguard Worker def _add_test_module_targets(self): 464*c2e18aaaSAndroid Build Coastguard Worker seen = set() 465*c2e18aaaSAndroid Build Coastguard Worker 466*c2e18aaaSAndroid Build Coastguard Worker for name, info in self.mod_info.name_to_module_info.items(): 467*c2e18aaaSAndroid Build Coastguard Worker # Ignore modules that have a 'host_cross_' prefix since they are 468*c2e18aaaSAndroid Build Coastguard Worker # duplicates of existing modules. For example, 469*c2e18aaaSAndroid Build Coastguard Worker # 'host_cross_aapt2_tests' is a duplicate of 'aapt2_tests'. We also 470*c2e18aaaSAndroid Build Coastguard Worker # ignore modules with a '_32' suffix since these also are redundant 471*c2e18aaaSAndroid Build Coastguard Worker # given that modules have both 32 and 64-bit variants built by 472*c2e18aaaSAndroid Build Coastguard Worker # default. See b/77288544#comment6 and b/23566667 for more context. 473*c2e18aaaSAndroid Build Coastguard Worker if name.endswith('_32') or name.startswith('host_cross_'): 474*c2e18aaaSAndroid Build Coastguard Worker continue 475*c2e18aaaSAndroid Build Coastguard Worker 476*c2e18aaaSAndroid Build Coastguard Worker if ( 477*c2e18aaaSAndroid Build Coastguard Worker Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST in self.enabled_features 478*c2e18aaaSAndroid Build Coastguard Worker and self.mod_info.is_device_driven_test(info) 479*c2e18aaaSAndroid Build Coastguard Worker ): 480*c2e18aaaSAndroid Build Coastguard Worker self._resolve_dependencies( 481*c2e18aaaSAndroid Build Coastguard Worker self._add_device_test_target(info, False), seen 482*c2e18aaaSAndroid Build Coastguard Worker ) 483*c2e18aaaSAndroid Build Coastguard Worker 484*c2e18aaaSAndroid Build Coastguard Worker if self.mod_info.is_host_unit_test(info): 485*c2e18aaaSAndroid Build Coastguard Worker self._resolve_dependencies(self._add_deviceless_test_target(info), seen) 486*c2e18aaaSAndroid Build Coastguard Worker elif ( 487*c2e18aaaSAndroid Build Coastguard Worker Features.EXPERIMENTAL_ROBOLECTRIC_TEST in self.enabled_features 488*c2e18aaaSAndroid Build Coastguard Worker and self.mod_info.is_modern_robolectric_test(info) 489*c2e18aaaSAndroid Build Coastguard Worker ): 490*c2e18aaaSAndroid Build Coastguard Worker self._resolve_dependencies( 491*c2e18aaaSAndroid Build Coastguard Worker self._add_tradefed_robolectric_test_target(info), seen 492*c2e18aaaSAndroid Build Coastguard Worker ) 493*c2e18aaaSAndroid Build Coastguard Worker elif ( 494*c2e18aaaSAndroid Build Coastguard Worker Features.EXPERIMENTAL_HOST_DRIVEN_TEST in self.enabled_features 495*c2e18aaaSAndroid Build Coastguard Worker and self.mod_info.is_host_driven_test(info) 496*c2e18aaaSAndroid Build Coastguard Worker ): 497*c2e18aaaSAndroid Build Coastguard Worker self._resolve_dependencies( 498*c2e18aaaSAndroid Build Coastguard Worker self._add_device_test_target(info, True), seen 499*c2e18aaaSAndroid Build Coastguard Worker ) 500*c2e18aaaSAndroid Build Coastguard Worker 501*c2e18aaaSAndroid Build Coastguard Worker def _resolve_dependencies(self, top_level_target: Target, seen: Set[Target]): 502*c2e18aaaSAndroid Build Coastguard Worker 503*c2e18aaaSAndroid Build Coastguard Worker stack = [deque([top_level_target])] 504*c2e18aaaSAndroid Build Coastguard Worker 505*c2e18aaaSAndroid Build Coastguard Worker while stack: 506*c2e18aaaSAndroid Build Coastguard Worker top = stack[-1] 507*c2e18aaaSAndroid Build Coastguard Worker 508*c2e18aaaSAndroid Build Coastguard Worker if not top: 509*c2e18aaaSAndroid Build Coastguard Worker stack.pop() 510*c2e18aaaSAndroid Build Coastguard Worker continue 511*c2e18aaaSAndroid Build Coastguard Worker 512*c2e18aaaSAndroid Build Coastguard Worker target = top.popleft() 513*c2e18aaaSAndroid Build Coastguard Worker 514*c2e18aaaSAndroid Build Coastguard Worker # Note that we're relying on Python's default identity-based hash 515*c2e18aaaSAndroid Build Coastguard Worker # and equality methods. This is fine since we actually DO want 516*c2e18aaaSAndroid Build Coastguard Worker # reference-equality semantics for Target objects in this context. 517*c2e18aaaSAndroid Build Coastguard Worker if target in seen: 518*c2e18aaaSAndroid Build Coastguard Worker continue 519*c2e18aaaSAndroid Build Coastguard Worker 520*c2e18aaaSAndroid Build Coastguard Worker seen.add(target) 521*c2e18aaaSAndroid Build Coastguard Worker 522*c2e18aaaSAndroid Build Coastguard Worker next_top = deque() 523*c2e18aaaSAndroid Build Coastguard Worker 524*c2e18aaaSAndroid Build Coastguard Worker for ref in target.dependencies(): 525*c2e18aaaSAndroid Build Coastguard Worker info = ref.info or self._get_module_info(ref.name) 526*c2e18aaaSAndroid Build Coastguard Worker ref.set(self._add_prebuilt_target(info)) 527*c2e18aaaSAndroid Build Coastguard Worker next_top.append(ref.target()) 528*c2e18aaaSAndroid Build Coastguard Worker 529*c2e18aaaSAndroid Build Coastguard Worker stack.append(next_top) 530*c2e18aaaSAndroid Build Coastguard Worker 531*c2e18aaaSAndroid Build Coastguard Worker def _add_device_test_target( 532*c2e18aaaSAndroid Build Coastguard Worker self, info: Dict[str, Any], is_host_driven: bool 533*c2e18aaaSAndroid Build Coastguard Worker ) -> Target: 534*c2e18aaaSAndroid Build Coastguard Worker package_name = self._get_module_path(info) 535*c2e18aaaSAndroid Build Coastguard Worker name_suffix = 'host' if is_host_driven else 'device' 536*c2e18aaaSAndroid Build Coastguard Worker name = f'{info[constants.MODULE_INFO_ID]}_{name_suffix}' 537*c2e18aaaSAndroid Build Coastguard Worker 538*c2e18aaaSAndroid Build Coastguard Worker def create(): 539*c2e18aaaSAndroid Build Coastguard Worker return TestTarget.create_device_test_target( 540*c2e18aaaSAndroid Build Coastguard Worker name, 541*c2e18aaaSAndroid Build Coastguard Worker package_name, 542*c2e18aaaSAndroid Build Coastguard Worker info, 543*c2e18aaaSAndroid Build Coastguard Worker is_host_driven, 544*c2e18aaaSAndroid Build Coastguard Worker ) 545*c2e18aaaSAndroid Build Coastguard Worker 546*c2e18aaaSAndroid Build Coastguard Worker return self._add_target(package_name, name, create) 547*c2e18aaaSAndroid Build Coastguard Worker 548*c2e18aaaSAndroid Build Coastguard Worker def _add_deviceless_test_target(self, info: Dict[str, Any]) -> Target: 549*c2e18aaaSAndroid Build Coastguard Worker package_name = self._get_module_path(info) 550*c2e18aaaSAndroid Build Coastguard Worker name = f'{info[constants.MODULE_INFO_ID]}_host' 551*c2e18aaaSAndroid Build Coastguard Worker 552*c2e18aaaSAndroid Build Coastguard Worker def create(): 553*c2e18aaaSAndroid Build Coastguard Worker return TestTarget.create_deviceless_test_target( 554*c2e18aaaSAndroid Build Coastguard Worker name, 555*c2e18aaaSAndroid Build Coastguard Worker package_name, 556*c2e18aaaSAndroid Build Coastguard Worker info, 557*c2e18aaaSAndroid Build Coastguard Worker ) 558*c2e18aaaSAndroid Build Coastguard Worker 559*c2e18aaaSAndroid Build Coastguard Worker return self._add_target(package_name, name, create) 560*c2e18aaaSAndroid Build Coastguard Worker 561*c2e18aaaSAndroid Build Coastguard Worker def _add_tradefed_robolectric_test_target( 562*c2e18aaaSAndroid Build Coastguard Worker self, info: Dict[str, Any] 563*c2e18aaaSAndroid Build Coastguard Worker ) -> Target: 564*c2e18aaaSAndroid Build Coastguard Worker package_name = self._get_module_path(info) 565*c2e18aaaSAndroid Build Coastguard Worker name = f'{info[constants.MODULE_INFO_ID]}_host' 566*c2e18aaaSAndroid Build Coastguard Worker 567*c2e18aaaSAndroid Build Coastguard Worker return self._add_target( 568*c2e18aaaSAndroid Build Coastguard Worker package_name, 569*c2e18aaaSAndroid Build Coastguard Worker name, 570*c2e18aaaSAndroid Build Coastguard Worker lambda: TestTarget.create_tradefed_robolectric_test_target( 571*c2e18aaaSAndroid Build Coastguard Worker name, package_name, info, f'//{JDK_PACKAGE_NAME}:{JDK_NAME}' 572*c2e18aaaSAndroid Build Coastguard Worker ), 573*c2e18aaaSAndroid Build Coastguard Worker ) 574*c2e18aaaSAndroid Build Coastguard Worker 575*c2e18aaaSAndroid Build Coastguard Worker def _add_prebuilt_target(self, info: Dict[str, Any]) -> Target: 576*c2e18aaaSAndroid Build Coastguard Worker package_name = self._get_module_path(info) 577*c2e18aaaSAndroid Build Coastguard Worker name = info[constants.MODULE_INFO_ID] 578*c2e18aaaSAndroid Build Coastguard Worker 579*c2e18aaaSAndroid Build Coastguard Worker def create(): 580*c2e18aaaSAndroid Build Coastguard Worker return SoongPrebuiltTarget.create( 581*c2e18aaaSAndroid Build Coastguard Worker self, 582*c2e18aaaSAndroid Build Coastguard Worker info, 583*c2e18aaaSAndroid Build Coastguard Worker package_name, 584*c2e18aaaSAndroid Build Coastguard Worker ) 585*c2e18aaaSAndroid Build Coastguard Worker 586*c2e18aaaSAndroid Build Coastguard Worker return self._add_target(package_name, name, create) 587*c2e18aaaSAndroid Build Coastguard Worker 588*c2e18aaaSAndroid Build Coastguard Worker def _add_target( 589*c2e18aaaSAndroid Build Coastguard Worker self, package_path: str, target_name: str, create_fn: Callable 590*c2e18aaaSAndroid Build Coastguard Worker ) -> Target: 591*c2e18aaaSAndroid Build Coastguard Worker 592*c2e18aaaSAndroid Build Coastguard Worker package = self.path_to_package.get(package_path) 593*c2e18aaaSAndroid Build Coastguard Worker 594*c2e18aaaSAndroid Build Coastguard Worker if not package: 595*c2e18aaaSAndroid Build Coastguard Worker package = Package(package_path) 596*c2e18aaaSAndroid Build Coastguard Worker self.path_to_package[package_path] = package 597*c2e18aaaSAndroid Build Coastguard Worker 598*c2e18aaaSAndroid Build Coastguard Worker target = package.get_target(target_name) 599*c2e18aaaSAndroid Build Coastguard Worker 600*c2e18aaaSAndroid Build Coastguard Worker if target: 601*c2e18aaaSAndroid Build Coastguard Worker return target 602*c2e18aaaSAndroid Build Coastguard Worker 603*c2e18aaaSAndroid Build Coastguard Worker target = create_fn() 604*c2e18aaaSAndroid Build Coastguard Worker package.add_target(target) 605*c2e18aaaSAndroid Build Coastguard Worker 606*c2e18aaaSAndroid Build Coastguard Worker return target 607*c2e18aaaSAndroid Build Coastguard Worker 608*c2e18aaaSAndroid Build Coastguard Worker def _get_module_info(self, module_name: str) -> Dict[str, Any]: 609*c2e18aaaSAndroid Build Coastguard Worker info = self.mod_info.get_module_info(module_name) 610*c2e18aaaSAndroid Build Coastguard Worker 611*c2e18aaaSAndroid Build Coastguard Worker if not info: 612*c2e18aaaSAndroid Build Coastguard Worker raise LookupError( 613*c2e18aaaSAndroid Build Coastguard Worker f'Could not find module `{module_name}` in module_info file' 614*c2e18aaaSAndroid Build Coastguard Worker ) 615*c2e18aaaSAndroid Build Coastguard Worker 616*c2e18aaaSAndroid Build Coastguard Worker return info 617*c2e18aaaSAndroid Build Coastguard Worker 618*c2e18aaaSAndroid Build Coastguard Worker def _get_module_path(self, info: Dict[str, Any]) -> str: 619*c2e18aaaSAndroid Build Coastguard Worker mod_path = info.get(constants.MODULE_PATH) 620*c2e18aaaSAndroid Build Coastguard Worker 621*c2e18aaaSAndroid Build Coastguard Worker if len(mod_path) < 1: 622*c2e18aaaSAndroid Build Coastguard Worker module_name = info['module_name'] 623*c2e18aaaSAndroid Build Coastguard Worker raise ValueError(f'Module `{module_name}` does not have any path') 624*c2e18aaaSAndroid Build Coastguard Worker 625*c2e18aaaSAndroid Build Coastguard Worker if len(mod_path) > 1: 626*c2e18aaaSAndroid Build Coastguard Worker module_name = info['module_name'] 627*c2e18aaaSAndroid Build Coastguard Worker # We usually have a single path but there are a few exceptions for 628*c2e18aaaSAndroid Build Coastguard Worker # modules like libLLVM_android and libclang_android. 629*c2e18aaaSAndroid Build Coastguard Worker # TODO(yangbill): Raise an exception for multiple paths once 630*c2e18aaaSAndroid Build Coastguard Worker # b/233581382 is resolved. 631*c2e18aaaSAndroid Build Coastguard Worker warnings.formatwarning = lambda msg, *args, **kwargs: f'{msg}\n' 632*c2e18aaaSAndroid Build Coastguard Worker warnings.warn( 633*c2e18aaaSAndroid Build Coastguard Worker f'Module `{module_name}` has more than one path: `{mod_path}`' 634*c2e18aaaSAndroid Build Coastguard Worker ) 635*c2e18aaaSAndroid Build Coastguard Worker 636*c2e18aaaSAndroid Build Coastguard Worker return mod_path[0] 637*c2e18aaaSAndroid Build Coastguard Worker 638*c2e18aaaSAndroid Build Coastguard Worker def _generate_artifacts(self): 639*c2e18aaaSAndroid Build Coastguard Worker """Generate workspace files on disk.""" 640*c2e18aaaSAndroid Build Coastguard Worker 641*c2e18aaaSAndroid Build Coastguard Worker self._create_base_files() 642*c2e18aaaSAndroid Build Coastguard Worker 643*c2e18aaaSAndroid Build Coastguard Worker self._add_workspace_resource(src='rules', dst='bazel/rules') 644*c2e18aaaSAndroid Build Coastguard Worker self._add_workspace_resource(src='configs', dst='bazel/configs') 645*c2e18aaaSAndroid Build Coastguard Worker 646*c2e18aaaSAndroid Build Coastguard Worker if Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST in self.enabled_features: 647*c2e18aaaSAndroid Build Coastguard Worker self._add_workspace_resource(src='device_def', dst='device_def') 648*c2e18aaaSAndroid Build Coastguard Worker 649*c2e18aaaSAndroid Build Coastguard Worker self._add_bazel_bootstrap_files() 650*c2e18aaaSAndroid Build Coastguard Worker 651*c2e18aaaSAndroid Build Coastguard Worker # Symlink to package with toolchain definitions. 652*c2e18aaaSAndroid Build Coastguard Worker self._symlink(src='prebuilts/build-tools', target='prebuilts/build-tools') 653*c2e18aaaSAndroid Build Coastguard Worker 654*c2e18aaaSAndroid Build Coastguard Worker device_infra_path = 'vendor/google/tools/atest/device_infra' 655*c2e18aaaSAndroid Build Coastguard Worker if self.resource_manager.get_src_file_path(device_infra_path).exists(): 656*c2e18aaaSAndroid Build Coastguard Worker self._symlink(src=device_infra_path, target=device_infra_path) 657*c2e18aaaSAndroid Build Coastguard Worker 658*c2e18aaaSAndroid Build Coastguard Worker self._link_required_src_file_path('external/bazelbuild-rules_python') 659*c2e18aaaSAndroid Build Coastguard Worker self._link_required_src_file_path('external/bazelbuild-rules_java') 660*c2e18aaaSAndroid Build Coastguard Worker 661*c2e18aaaSAndroid Build Coastguard Worker self._create_constants_file() 662*c2e18aaaSAndroid Build Coastguard Worker 663*c2e18aaaSAndroid Build Coastguard Worker self._generate_robolectric_resources() 664*c2e18aaaSAndroid Build Coastguard Worker 665*c2e18aaaSAndroid Build Coastguard Worker for package in self.path_to_package.values(): 666*c2e18aaaSAndroid Build Coastguard Worker package.generate(self.workspace_out_path) 667*c2e18aaaSAndroid Build Coastguard Worker 668*c2e18aaaSAndroid Build Coastguard Worker def _generate_robolectric_resources(self): 669*c2e18aaaSAndroid Build Coastguard Worker if not self.jdk_path: 670*c2e18aaaSAndroid Build Coastguard Worker return 671*c2e18aaaSAndroid Build Coastguard Worker 672*c2e18aaaSAndroid Build Coastguard Worker self._generate_jdk_resources() 673*c2e18aaaSAndroid Build Coastguard Worker self._generate_android_all_resources() 674*c2e18aaaSAndroid Build Coastguard Worker 675*c2e18aaaSAndroid Build Coastguard Worker def _generate_jdk_resources(self): 676*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/265596946): Create the JDK toolchain instead of using 677*c2e18aaaSAndroid Build Coastguard Worker # a filegroup. 678*c2e18aaaSAndroid Build Coastguard Worker return self._add_target( 679*c2e18aaaSAndroid Build Coastguard Worker JDK_PACKAGE_NAME, 680*c2e18aaaSAndroid Build Coastguard Worker JDK_NAME, 681*c2e18aaaSAndroid Build Coastguard Worker lambda: FilegroupTarget( 682*c2e18aaaSAndroid Build Coastguard Worker JDK_PACKAGE_NAME, 683*c2e18aaaSAndroid Build Coastguard Worker JDK_NAME, 684*c2e18aaaSAndroid Build Coastguard Worker self.resource_manager.get_src_file_path(self.jdk_path), 685*c2e18aaaSAndroid Build Coastguard Worker ), 686*c2e18aaaSAndroid Build Coastguard Worker ) 687*c2e18aaaSAndroid Build Coastguard Worker 688*c2e18aaaSAndroid Build Coastguard Worker def _generate_android_all_resources(self): 689*c2e18aaaSAndroid Build Coastguard Worker package_name = 'android-all' 690*c2e18aaaSAndroid Build Coastguard Worker name = 'android-all' 691*c2e18aaaSAndroid Build Coastguard Worker 692*c2e18aaaSAndroid Build Coastguard Worker return self._add_target( 693*c2e18aaaSAndroid Build Coastguard Worker package_name, 694*c2e18aaaSAndroid Build Coastguard Worker name, 695*c2e18aaaSAndroid Build Coastguard Worker lambda: FilegroupTarget( 696*c2e18aaaSAndroid Build Coastguard Worker package_name, name, self.host_out_path.joinpath(f'testcases/{name}') 697*c2e18aaaSAndroid Build Coastguard Worker ), 698*c2e18aaaSAndroid Build Coastguard Worker ) 699*c2e18aaaSAndroid Build Coastguard Worker 700*c2e18aaaSAndroid Build Coastguard Worker def _symlink(self, *, src, target): 701*c2e18aaaSAndroid Build Coastguard Worker """Create a symbolic link in workspace pointing to source file/dir. 702*c2e18aaaSAndroid Build Coastguard Worker 703*c2e18aaaSAndroid Build Coastguard Worker Args: 704*c2e18aaaSAndroid Build Coastguard Worker src: A string of a relative path to root of Android source tree. This is 705*c2e18aaaSAndroid Build Coastguard Worker the source file/dir path for which the symbolic link will be created. 706*c2e18aaaSAndroid Build Coastguard Worker target: A string of a relative path to workspace root. This is the 707*c2e18aaaSAndroid Build Coastguard Worker target file/dir path where the symbolic link will be created. 708*c2e18aaaSAndroid Build Coastguard Worker """ 709*c2e18aaaSAndroid Build Coastguard Worker symlink = self.workspace_out_path.joinpath(target) 710*c2e18aaaSAndroid Build Coastguard Worker symlink.parent.mkdir(parents=True, exist_ok=True) 711*c2e18aaaSAndroid Build Coastguard Worker symlink.symlink_to(self.resource_manager.get_src_file_path(src)) 712*c2e18aaaSAndroid Build Coastguard Worker 713*c2e18aaaSAndroid Build Coastguard Worker def _create_base_files(self): 714*c2e18aaaSAndroid Build Coastguard Worker self._add_workspace_resource(src='WORKSPACE', dst='WORKSPACE') 715*c2e18aaaSAndroid Build Coastguard Worker self._add_workspace_resource(src='bazelrc', dst='.bazelrc') 716*c2e18aaaSAndroid Build Coastguard Worker 717*c2e18aaaSAndroid Build Coastguard Worker self.workspace_out_path.joinpath('BUILD.bazel').touch() 718*c2e18aaaSAndroid Build Coastguard Worker 719*c2e18aaaSAndroid Build Coastguard Worker def _add_bazel_bootstrap_files(self): 720*c2e18aaaSAndroid Build Coastguard Worker self._add_workspace_resource(src='bazel.sh', dst='bazel.sh') 721*c2e18aaaSAndroid Build Coastguard Worker # Restore permissions as execute permissions are not preserved by soong 722*c2e18aaaSAndroid Build Coastguard Worker # packaging. 723*c2e18aaaSAndroid Build Coastguard Worker os.chmod(self.workspace_out_path.joinpath('bazel.sh'), 0o755) 724*c2e18aaaSAndroid Build Coastguard Worker self._symlink( 725*c2e18aaaSAndroid Build Coastguard Worker src='prebuilts/jdk/jdk21/BUILD.bazel', 726*c2e18aaaSAndroid Build Coastguard Worker target='prebuilts/jdk/jdk21/BUILD.bazel', 727*c2e18aaaSAndroid Build Coastguard Worker ) 728*c2e18aaaSAndroid Build Coastguard Worker self._symlink( 729*c2e18aaaSAndroid Build Coastguard Worker src='prebuilts/jdk/jdk21/linux-x86', 730*c2e18aaaSAndroid Build Coastguard Worker target='prebuilts/jdk/jdk21/linux-x86', 731*c2e18aaaSAndroid Build Coastguard Worker ) 732*c2e18aaaSAndroid Build Coastguard Worker self._symlink( 733*c2e18aaaSAndroid Build Coastguard Worker src='prebuilts/bazel/linux-x86_64/bazel', 734*c2e18aaaSAndroid Build Coastguard Worker target='prebuilts/bazel/linux-x86_64/bazel', 735*c2e18aaaSAndroid Build Coastguard Worker ) 736*c2e18aaaSAndroid Build Coastguard Worker 737*c2e18aaaSAndroid Build Coastguard Worker def _add_workspace_resource(self, src, dst): 738*c2e18aaaSAndroid Build Coastguard Worker """Add resource to the given destination in workspace. 739*c2e18aaaSAndroid Build Coastguard Worker 740*c2e18aaaSAndroid Build Coastguard Worker Args: 741*c2e18aaaSAndroid Build Coastguard Worker src: A string of a relative path to root of Bazel artifacts. This is the 742*c2e18aaaSAndroid Build Coastguard Worker source file/dir path that will be added to workspace. 743*c2e18aaaSAndroid Build Coastguard Worker dst: A string of a relative path to workspace root. This is the 744*c2e18aaaSAndroid Build Coastguard Worker destination file/dir path where the artifacts will be added. 745*c2e18aaaSAndroid Build Coastguard Worker """ 746*c2e18aaaSAndroid Build Coastguard Worker src = self.resource_manager.get_resource_file_path(src, True) 747*c2e18aaaSAndroid Build Coastguard Worker dst = self.workspace_out_path.joinpath(dst) 748*c2e18aaaSAndroid Build Coastguard Worker dst.parent.mkdir(parents=True, exist_ok=True) 749*c2e18aaaSAndroid Build Coastguard Worker 750*c2e18aaaSAndroid Build Coastguard Worker if src.is_file(): 751*c2e18aaaSAndroid Build Coastguard Worker shutil.copy(src, dst) 752*c2e18aaaSAndroid Build Coastguard Worker else: 753*c2e18aaaSAndroid Build Coastguard Worker shutil.copytree(src, dst, ignore=shutil.ignore_patterns('__init__.py')) 754*c2e18aaaSAndroid Build Coastguard Worker 755*c2e18aaaSAndroid Build Coastguard Worker def _create_constants_file(self): 756*c2e18aaaSAndroid Build Coastguard Worker def variable_name(target_name): 757*c2e18aaaSAndroid Build Coastguard Worker return re.sub(r'[.-]', '_', target_name) + '_label' 758*c2e18aaaSAndroid Build Coastguard Worker 759*c2e18aaaSAndroid Build Coastguard Worker targets = [] 760*c2e18aaaSAndroid Build Coastguard Worker seen = set() 761*c2e18aaaSAndroid Build Coastguard Worker 762*c2e18aaaSAndroid Build Coastguard Worker for module_name in TestTarget.DEVICELESS_TEST_PREREQUISITES.union( 763*c2e18aaaSAndroid Build Coastguard Worker TestTarget.DEVICE_TEST_PREREQUISITES 764*c2e18aaaSAndroid Build Coastguard Worker ): 765*c2e18aaaSAndroid Build Coastguard Worker info = self.mod_info.get_module_info(module_name) 766*c2e18aaaSAndroid Build Coastguard Worker target = self._add_prebuilt_target(info) 767*c2e18aaaSAndroid Build Coastguard Worker self._resolve_dependencies(target, seen) 768*c2e18aaaSAndroid Build Coastguard Worker targets.append(target) 769*c2e18aaaSAndroid Build Coastguard Worker 770*c2e18aaaSAndroid Build Coastguard Worker with self.workspace_out_path.joinpath('constants.bzl').open('w') as f: 771*c2e18aaaSAndroid Build Coastguard Worker writer = IndentWriter(f) 772*c2e18aaaSAndroid Build Coastguard Worker for target in targets: 773*c2e18aaaSAndroid Build Coastguard Worker writer.write_line( 774*c2e18aaaSAndroid Build Coastguard Worker '%s = "%s"' 775*c2e18aaaSAndroid Build Coastguard Worker % (variable_name(target.name()), target.qualified_name()) 776*c2e18aaaSAndroid Build Coastguard Worker ) 777*c2e18aaaSAndroid Build Coastguard Worker 778*c2e18aaaSAndroid Build Coastguard Worker def _link_required_src_file_path(self, path): 779*c2e18aaaSAndroid Build Coastguard Worker if not self.resource_manager.get_src_file_path(path).exists(): 780*c2e18aaaSAndroid Build Coastguard Worker raise RuntimeError(f'Path `{path}` does not exist in source tree.') 781*c2e18aaaSAndroid Build Coastguard Worker 782*c2e18aaaSAndroid Build Coastguard Worker self._symlink(src=path, target=path) 783*c2e18aaaSAndroid Build Coastguard Worker 784*c2e18aaaSAndroid Build Coastguard Worker 785*c2e18aaaSAndroid Build Coastguard Worker@functools.cache 786*c2e18aaaSAndroid Build Coastguard Workerdef _get_resource_root() -> pathlib.Path: 787*c2e18aaaSAndroid Build Coastguard Worker tmp_resource_dir = pathlib.Path(tempfile.mkdtemp()) 788*c2e18aaaSAndroid Build Coastguard Worker atexit.register(lambda: shutil.rmtree(tmp_resource_dir)) 789*c2e18aaaSAndroid Build Coastguard Worker 790*c2e18aaaSAndroid Build Coastguard Worker def _extract_resources( 791*c2e18aaaSAndroid Build Coastguard Worker resource_path: pathlib.Path, 792*c2e18aaaSAndroid Build Coastguard Worker dst: pathlib.Path, 793*c2e18aaaSAndroid Build Coastguard Worker ignore_file_names: list[str] = None, 794*c2e18aaaSAndroid Build Coastguard Worker ): 795*c2e18aaaSAndroid Build Coastguard Worker resource = importlib.resources.files(resource_path.as_posix()) 796*c2e18aaaSAndroid Build Coastguard Worker dst.mkdir(parents=True, exist_ok=True) 797*c2e18aaaSAndroid Build Coastguard Worker for child in resource.iterdir(): 798*c2e18aaaSAndroid Build Coastguard Worker if child.is_file(): 799*c2e18aaaSAndroid Build Coastguard Worker if child.name in ignore_file_names: 800*c2e18aaaSAndroid Build Coastguard Worker continue 801*c2e18aaaSAndroid Build Coastguard Worker with importlib.resources.as_file(child) as child_file: 802*c2e18aaaSAndroid Build Coastguard Worker shutil.copy(child_file, dst.joinpath(child.name)) 803*c2e18aaaSAndroid Build Coastguard Worker elif child.is_dir(): 804*c2e18aaaSAndroid Build Coastguard Worker _extract_resources( 805*c2e18aaaSAndroid Build Coastguard Worker resource_path.joinpath(child.name), 806*c2e18aaaSAndroid Build Coastguard Worker dst.joinpath(child.name), 807*c2e18aaaSAndroid Build Coastguard Worker ignore_file_names, 808*c2e18aaaSAndroid Build Coastguard Worker ) 809*c2e18aaaSAndroid Build Coastguard Worker else: 810*c2e18aaaSAndroid Build Coastguard Worker atest_utils.print_and_log_warning( 811*c2e18aaaSAndroid Build Coastguard Worker 'Ignoring unknown resource: %s', child 812*c2e18aaaSAndroid Build Coastguard Worker ) 813*c2e18aaaSAndroid Build Coastguard Worker 814*c2e18aaaSAndroid Build Coastguard Worker try: 815*c2e18aaaSAndroid Build Coastguard Worker _extract_resources( 816*c2e18aaaSAndroid Build Coastguard Worker pathlib.Path('atest/bazel/resources'), 817*c2e18aaaSAndroid Build Coastguard Worker tmp_resource_dir, 818*c2e18aaaSAndroid Build Coastguard Worker ignore_file_names=['__init__.py'], 819*c2e18aaaSAndroid Build Coastguard Worker ) 820*c2e18aaaSAndroid Build Coastguard Worker except ModuleNotFoundError as e: 821*c2e18aaaSAndroid Build Coastguard Worker logging.debug( 822*c2e18aaaSAndroid Build Coastguard Worker 'Bazel resource not found from package path, possible due to running' 823*c2e18aaaSAndroid Build Coastguard Worker ' atest from source. Returning resource source path instead: %s', 824*c2e18aaaSAndroid Build Coastguard Worker e, 825*c2e18aaaSAndroid Build Coastguard Worker ) 826*c2e18aaaSAndroid Build Coastguard Worker return pathlib.Path(os.path.dirname(__file__)).joinpath('bazel/resources') 827*c2e18aaaSAndroid Build Coastguard Worker 828*c2e18aaaSAndroid Build Coastguard Worker return tmp_resource_dir 829*c2e18aaaSAndroid Build Coastguard Worker 830*c2e18aaaSAndroid Build Coastguard Worker 831*c2e18aaaSAndroid Build Coastguard Workerclass Package: 832*c2e18aaaSAndroid Build Coastguard Worker """Class for generating an entire Package on disk.""" 833*c2e18aaaSAndroid Build Coastguard Worker 834*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, path: str): 835*c2e18aaaSAndroid Build Coastguard Worker self.path = path 836*c2e18aaaSAndroid Build Coastguard Worker self.imports = defaultdict(set) 837*c2e18aaaSAndroid Build Coastguard Worker self.name_to_target = OrderedDict() 838*c2e18aaaSAndroid Build Coastguard Worker 839*c2e18aaaSAndroid Build Coastguard Worker def add_target(self, target): 840*c2e18aaaSAndroid Build Coastguard Worker target_name = target.name() 841*c2e18aaaSAndroid Build Coastguard Worker 842*c2e18aaaSAndroid Build Coastguard Worker if target_name in self.name_to_target: 843*c2e18aaaSAndroid Build Coastguard Worker raise ValueError( 844*c2e18aaaSAndroid Build Coastguard Worker f'Cannot add target `{target_name}` which already' 845*c2e18aaaSAndroid Build Coastguard Worker f' exists in package `{self.path}`' 846*c2e18aaaSAndroid Build Coastguard Worker ) 847*c2e18aaaSAndroid Build Coastguard Worker 848*c2e18aaaSAndroid Build Coastguard Worker self.name_to_target[target_name] = target 849*c2e18aaaSAndroid Build Coastguard Worker 850*c2e18aaaSAndroid Build Coastguard Worker for i in target.required_imports(): 851*c2e18aaaSAndroid Build Coastguard Worker self.imports[i.bzl_package].add(i.symbol) 852*c2e18aaaSAndroid Build Coastguard Worker 853*c2e18aaaSAndroid Build Coastguard Worker def generate(self, workspace_out_path: pathlib.Path): 854*c2e18aaaSAndroid Build Coastguard Worker package_dir = workspace_out_path.joinpath(self.path) 855*c2e18aaaSAndroid Build Coastguard Worker package_dir.mkdir(parents=True, exist_ok=True) 856*c2e18aaaSAndroid Build Coastguard Worker 857*c2e18aaaSAndroid Build Coastguard Worker self._create_filesystem_layout(package_dir) 858*c2e18aaaSAndroid Build Coastguard Worker self._write_build_file(package_dir) 859*c2e18aaaSAndroid Build Coastguard Worker 860*c2e18aaaSAndroid Build Coastguard Worker def _create_filesystem_layout(self, package_dir: pathlib.Path): 861*c2e18aaaSAndroid Build Coastguard Worker for target in self.name_to_target.values(): 862*c2e18aaaSAndroid Build Coastguard Worker target.create_filesystem_layout(package_dir) 863*c2e18aaaSAndroid Build Coastguard Worker 864*c2e18aaaSAndroid Build Coastguard Worker def _write_build_file(self, package_dir: pathlib.Path): 865*c2e18aaaSAndroid Build Coastguard Worker with package_dir.joinpath('BUILD.bazel').open('w') as f: 866*c2e18aaaSAndroid Build Coastguard Worker f.write('package(default_visibility = ["//visibility:public"])\n') 867*c2e18aaaSAndroid Build Coastguard Worker f.write('\n') 868*c2e18aaaSAndroid Build Coastguard Worker 869*c2e18aaaSAndroid Build Coastguard Worker for bzl_package, symbols in sorted(self.imports.items()): 870*c2e18aaaSAndroid Build Coastguard Worker symbols_text = ', '.join('"%s"' % s for s in sorted(symbols)) 871*c2e18aaaSAndroid Build Coastguard Worker f.write(f'load("{bzl_package}", {symbols_text})\n') 872*c2e18aaaSAndroid Build Coastguard Worker 873*c2e18aaaSAndroid Build Coastguard Worker for target in self.name_to_target.values(): 874*c2e18aaaSAndroid Build Coastguard Worker f.write('\n') 875*c2e18aaaSAndroid Build Coastguard Worker target.write_to_build_file(f) 876*c2e18aaaSAndroid Build Coastguard Worker 877*c2e18aaaSAndroid Build Coastguard Worker def get_target(self, target_name: str) -> Target: 878*c2e18aaaSAndroid Build Coastguard Worker return self.name_to_target.get(target_name, None) 879*c2e18aaaSAndroid Build Coastguard Worker 880*c2e18aaaSAndroid Build Coastguard Worker 881*c2e18aaaSAndroid Build Coastguard Worker@dataclasses.dataclass(frozen=True) 882*c2e18aaaSAndroid Build Coastguard Workerclass Import: 883*c2e18aaaSAndroid Build Coastguard Worker bzl_package: str 884*c2e18aaaSAndroid Build Coastguard Worker symbol: str 885*c2e18aaaSAndroid Build Coastguard Worker 886*c2e18aaaSAndroid Build Coastguard Worker 887*c2e18aaaSAndroid Build Coastguard Worker@dataclasses.dataclass(frozen=True) 888*c2e18aaaSAndroid Build Coastguard Workerclass Config: 889*c2e18aaaSAndroid Build Coastguard Worker name: str 890*c2e18aaaSAndroid Build Coastguard Worker out_path: pathlib.Path 891*c2e18aaaSAndroid Build Coastguard Worker 892*c2e18aaaSAndroid Build Coastguard Worker 893*c2e18aaaSAndroid Build Coastguard Workerclass ModuleRef: 894*c2e18aaaSAndroid Build Coastguard Worker 895*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 896*c2e18aaaSAndroid Build Coastguard Worker def for_info(info) -> ModuleRef: 897*c2e18aaaSAndroid Build Coastguard Worker return ModuleRef(info=info) 898*c2e18aaaSAndroid Build Coastguard Worker 899*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 900*c2e18aaaSAndroid Build Coastguard Worker def for_name(name) -> ModuleRef: 901*c2e18aaaSAndroid Build Coastguard Worker return ModuleRef(name=name) 902*c2e18aaaSAndroid Build Coastguard Worker 903*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, info=None, name=None): 904*c2e18aaaSAndroid Build Coastguard Worker self.info = info 905*c2e18aaaSAndroid Build Coastguard Worker self.name = name 906*c2e18aaaSAndroid Build Coastguard Worker self._target = None 907*c2e18aaaSAndroid Build Coastguard Worker 908*c2e18aaaSAndroid Build Coastguard Worker def target(self) -> Target: 909*c2e18aaaSAndroid Build Coastguard Worker if not self._target: 910*c2e18aaaSAndroid Build Coastguard Worker target_name = self.info[constants.MODULE_INFO_ID] 911*c2e18aaaSAndroid Build Coastguard Worker raise ValueError(f'Target not set for ref `{target_name}`') 912*c2e18aaaSAndroid Build Coastguard Worker 913*c2e18aaaSAndroid Build Coastguard Worker return self._target 914*c2e18aaaSAndroid Build Coastguard Worker 915*c2e18aaaSAndroid Build Coastguard Worker def set(self, target): 916*c2e18aaaSAndroid Build Coastguard Worker self._target = target 917*c2e18aaaSAndroid Build Coastguard Worker 918*c2e18aaaSAndroid Build Coastguard Worker 919*c2e18aaaSAndroid Build Coastguard Workerclass Target(ABC): 920*c2e18aaaSAndroid Build Coastguard Worker """Abstract class for a Bazel target.""" 921*c2e18aaaSAndroid Build Coastguard Worker 922*c2e18aaaSAndroid Build Coastguard Worker @abstractmethod 923*c2e18aaaSAndroid Build Coastguard Worker def name(self) -> str: 924*c2e18aaaSAndroid Build Coastguard Worker pass 925*c2e18aaaSAndroid Build Coastguard Worker 926*c2e18aaaSAndroid Build Coastguard Worker def package_name(self) -> str: 927*c2e18aaaSAndroid Build Coastguard Worker pass 928*c2e18aaaSAndroid Build Coastguard Worker 929*c2e18aaaSAndroid Build Coastguard Worker def qualified_name(self) -> str: 930*c2e18aaaSAndroid Build Coastguard Worker return f'//{self.package_name()}:{self.name()}' 931*c2e18aaaSAndroid Build Coastguard Worker 932*c2e18aaaSAndroid Build Coastguard Worker def required_imports(self) -> Set[Import]: 933*c2e18aaaSAndroid Build Coastguard Worker return set() 934*c2e18aaaSAndroid Build Coastguard Worker 935*c2e18aaaSAndroid Build Coastguard Worker def supported_configs(self) -> Set[Config]: 936*c2e18aaaSAndroid Build Coastguard Worker return set() 937*c2e18aaaSAndroid Build Coastguard Worker 938*c2e18aaaSAndroid Build Coastguard Worker def dependencies(self) -> List[ModuleRef]: 939*c2e18aaaSAndroid Build Coastguard Worker return [] 940*c2e18aaaSAndroid Build Coastguard Worker 941*c2e18aaaSAndroid Build Coastguard Worker def write_to_build_file(self, f: IO): 942*c2e18aaaSAndroid Build Coastguard Worker pass 943*c2e18aaaSAndroid Build Coastguard Worker 944*c2e18aaaSAndroid Build Coastguard Worker def create_filesystem_layout(self, package_dir: pathlib.Path): 945*c2e18aaaSAndroid Build Coastguard Worker pass 946*c2e18aaaSAndroid Build Coastguard Worker 947*c2e18aaaSAndroid Build Coastguard Worker 948*c2e18aaaSAndroid Build Coastguard Workerclass FilegroupTarget(Target): 949*c2e18aaaSAndroid Build Coastguard Worker 950*c2e18aaaSAndroid Build Coastguard Worker def __init__( 951*c2e18aaaSAndroid Build Coastguard Worker self, package_name: str, target_name: str, srcs_root: pathlib.Path 952*c2e18aaaSAndroid Build Coastguard Worker ): 953*c2e18aaaSAndroid Build Coastguard Worker self._package_name = package_name 954*c2e18aaaSAndroid Build Coastguard Worker self._target_name = target_name 955*c2e18aaaSAndroid Build Coastguard Worker self._srcs_root = srcs_root 956*c2e18aaaSAndroid Build Coastguard Worker 957*c2e18aaaSAndroid Build Coastguard Worker def name(self) -> str: 958*c2e18aaaSAndroid Build Coastguard Worker return self._target_name 959*c2e18aaaSAndroid Build Coastguard Worker 960*c2e18aaaSAndroid Build Coastguard Worker def package_name(self) -> str: 961*c2e18aaaSAndroid Build Coastguard Worker return self._package_name 962*c2e18aaaSAndroid Build Coastguard Worker 963*c2e18aaaSAndroid Build Coastguard Worker def write_to_build_file(self, f: IO): 964*c2e18aaaSAndroid Build Coastguard Worker writer = IndentWriter(f) 965*c2e18aaaSAndroid Build Coastguard Worker build_file_writer = BuildFileWriter(writer) 966*c2e18aaaSAndroid Build Coastguard Worker 967*c2e18aaaSAndroid Build Coastguard Worker writer.write_line('filegroup(') 968*c2e18aaaSAndroid Build Coastguard Worker 969*c2e18aaaSAndroid Build Coastguard Worker with writer.indent(): 970*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_string_attribute('name', self._target_name) 971*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_glob_attribute( 972*c2e18aaaSAndroid Build Coastguard Worker 'srcs', [f'{self._target_name}_files/**'] 973*c2e18aaaSAndroid Build Coastguard Worker ) 974*c2e18aaaSAndroid Build Coastguard Worker 975*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(')') 976*c2e18aaaSAndroid Build Coastguard Worker 977*c2e18aaaSAndroid Build Coastguard Worker def create_filesystem_layout(self, package_dir: pathlib.Path): 978*c2e18aaaSAndroid Build Coastguard Worker symlink = package_dir.joinpath(f'{self._target_name}_files') 979*c2e18aaaSAndroid Build Coastguard Worker symlink.symlink_to(self._srcs_root) 980*c2e18aaaSAndroid Build Coastguard Worker 981*c2e18aaaSAndroid Build Coastguard Worker 982*c2e18aaaSAndroid Build Coastguard Workerclass TestTarget(Target): 983*c2e18aaaSAndroid Build Coastguard Worker """Class for generating a test target.""" 984*c2e18aaaSAndroid Build Coastguard Worker 985*c2e18aaaSAndroid Build Coastguard Worker DEVICELESS_TEST_PREREQUISITES = frozenset({ 986*c2e18aaaSAndroid Build Coastguard Worker 'adb', 987*c2e18aaaSAndroid Build Coastguard Worker 'atest-tradefed', 988*c2e18aaaSAndroid Build Coastguard Worker 'atest_script_help.sh', 989*c2e18aaaSAndroid Build Coastguard Worker 'atest_tradefed.sh', 990*c2e18aaaSAndroid Build Coastguard Worker 'tradefed', 991*c2e18aaaSAndroid Build Coastguard Worker 'tradefed-test-framework', 992*c2e18aaaSAndroid Build Coastguard Worker 'bazel-result-reporter', 993*c2e18aaaSAndroid Build Coastguard Worker }) 994*c2e18aaaSAndroid Build Coastguard Worker 995*c2e18aaaSAndroid Build Coastguard Worker DEVICE_TEST_PREREQUISITES = frozenset( 996*c2e18aaaSAndroid Build Coastguard Worker DEVICELESS_TEST_PREREQUISITES.union( 997*c2e18aaaSAndroid Build Coastguard Worker frozenset({ 998*c2e18aaaSAndroid Build Coastguard Worker 'aapt', 999*c2e18aaaSAndroid Build Coastguard Worker 'aapt2', 1000*c2e18aaaSAndroid Build Coastguard Worker 'compatibility-tradefed', 1001*c2e18aaaSAndroid Build Coastguard Worker 'vts-core-tradefed-harness', 1002*c2e18aaaSAndroid Build Coastguard Worker }) 1003*c2e18aaaSAndroid Build Coastguard Worker ) 1004*c2e18aaaSAndroid Build Coastguard Worker ) 1005*c2e18aaaSAndroid Build Coastguard Worker 1006*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 1007*c2e18aaaSAndroid Build Coastguard Worker def create_deviceless_test_target( 1008*c2e18aaaSAndroid Build Coastguard Worker name: str, package_name: str, info: Dict[str, Any] 1009*c2e18aaaSAndroid Build Coastguard Worker ): 1010*c2e18aaaSAndroid Build Coastguard Worker return TestTarget( 1011*c2e18aaaSAndroid Build Coastguard Worker package_name, 1012*c2e18aaaSAndroid Build Coastguard Worker 'tradefed_deviceless_test', 1013*c2e18aaaSAndroid Build Coastguard Worker { 1014*c2e18aaaSAndroid Build Coastguard Worker 'name': name, 1015*c2e18aaaSAndroid Build Coastguard Worker 'test': ModuleRef.for_info(info), 1016*c2e18aaaSAndroid Build Coastguard Worker 'module_name': info['module_name'], 1017*c2e18aaaSAndroid Build Coastguard Worker 'tags': info.get(constants.MODULE_TEST_OPTIONS_TAGS, []), 1018*c2e18aaaSAndroid Build Coastguard Worker }, 1019*c2e18aaaSAndroid Build Coastguard Worker TestTarget.DEVICELESS_TEST_PREREQUISITES, 1020*c2e18aaaSAndroid Build Coastguard Worker ) 1021*c2e18aaaSAndroid Build Coastguard Worker 1022*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 1023*c2e18aaaSAndroid Build Coastguard Worker def create_device_test_target( 1024*c2e18aaaSAndroid Build Coastguard Worker name: str, package_name: str, info: Dict[str, Any], is_host_driven: bool 1025*c2e18aaaSAndroid Build Coastguard Worker ): 1026*c2e18aaaSAndroid Build Coastguard Worker rule = ( 1027*c2e18aaaSAndroid Build Coastguard Worker 'tradefed_host_driven_device_test' 1028*c2e18aaaSAndroid Build Coastguard Worker if is_host_driven 1029*c2e18aaaSAndroid Build Coastguard Worker else 'tradefed_device_driven_test' 1030*c2e18aaaSAndroid Build Coastguard Worker ) 1031*c2e18aaaSAndroid Build Coastguard Worker 1032*c2e18aaaSAndroid Build Coastguard Worker return TestTarget( 1033*c2e18aaaSAndroid Build Coastguard Worker package_name, 1034*c2e18aaaSAndroid Build Coastguard Worker rule, 1035*c2e18aaaSAndroid Build Coastguard Worker { 1036*c2e18aaaSAndroid Build Coastguard Worker 'name': name, 1037*c2e18aaaSAndroid Build Coastguard Worker 'test': ModuleRef.for_info(info), 1038*c2e18aaaSAndroid Build Coastguard Worker 'module_name': info['module_name'], 1039*c2e18aaaSAndroid Build Coastguard Worker 'suites': set(info.get(constants.MODULE_COMPATIBILITY_SUITES, [])), 1040*c2e18aaaSAndroid Build Coastguard Worker 'tradefed_deps': list( 1041*c2e18aaaSAndroid Build Coastguard Worker map( 1042*c2e18aaaSAndroid Build Coastguard Worker ModuleRef.for_name, info.get(constants.MODULE_HOST_DEPS, []) 1043*c2e18aaaSAndroid Build Coastguard Worker ) 1044*c2e18aaaSAndroid Build Coastguard Worker ), 1045*c2e18aaaSAndroid Build Coastguard Worker 'tags': info.get(constants.MODULE_TEST_OPTIONS_TAGS, []), 1046*c2e18aaaSAndroid Build Coastguard Worker }, 1047*c2e18aaaSAndroid Build Coastguard Worker TestTarget.DEVICE_TEST_PREREQUISITES, 1048*c2e18aaaSAndroid Build Coastguard Worker ) 1049*c2e18aaaSAndroid Build Coastguard Worker 1050*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 1051*c2e18aaaSAndroid Build Coastguard Worker def create_tradefed_robolectric_test_target( 1052*c2e18aaaSAndroid Build Coastguard Worker name: str, package_name: str, info: Dict[str, Any], jdk_label: str 1053*c2e18aaaSAndroid Build Coastguard Worker ): 1054*c2e18aaaSAndroid Build Coastguard Worker return TestTarget( 1055*c2e18aaaSAndroid Build Coastguard Worker package_name, 1056*c2e18aaaSAndroid Build Coastguard Worker 'tradefed_robolectric_test', 1057*c2e18aaaSAndroid Build Coastguard Worker { 1058*c2e18aaaSAndroid Build Coastguard Worker 'name': name, 1059*c2e18aaaSAndroid Build Coastguard Worker 'test': ModuleRef.for_info(info), 1060*c2e18aaaSAndroid Build Coastguard Worker 'module_name': info['module_name'], 1061*c2e18aaaSAndroid Build Coastguard Worker 'tags': info.get(constants.MODULE_TEST_OPTIONS_TAGS, []), 1062*c2e18aaaSAndroid Build Coastguard Worker 'jdk': jdk_label, 1063*c2e18aaaSAndroid Build Coastguard Worker }, 1064*c2e18aaaSAndroid Build Coastguard Worker TestTarget.DEVICELESS_TEST_PREREQUISITES, 1065*c2e18aaaSAndroid Build Coastguard Worker ) 1066*c2e18aaaSAndroid Build Coastguard Worker 1067*c2e18aaaSAndroid Build Coastguard Worker def __init__( 1068*c2e18aaaSAndroid Build Coastguard Worker self, 1069*c2e18aaaSAndroid Build Coastguard Worker package_name: str, 1070*c2e18aaaSAndroid Build Coastguard Worker rule_name: str, 1071*c2e18aaaSAndroid Build Coastguard Worker attributes: Dict[str, Any], 1072*c2e18aaaSAndroid Build Coastguard Worker prerequisites=frozenset(), 1073*c2e18aaaSAndroid Build Coastguard Worker ): 1074*c2e18aaaSAndroid Build Coastguard Worker self._attributes = attributes 1075*c2e18aaaSAndroid Build Coastguard Worker self._package_name = package_name 1076*c2e18aaaSAndroid Build Coastguard Worker self._rule_name = rule_name 1077*c2e18aaaSAndroid Build Coastguard Worker self._prerequisites = prerequisites 1078*c2e18aaaSAndroid Build Coastguard Worker 1079*c2e18aaaSAndroid Build Coastguard Worker def name(self) -> str: 1080*c2e18aaaSAndroid Build Coastguard Worker return self._attributes['name'] 1081*c2e18aaaSAndroid Build Coastguard Worker 1082*c2e18aaaSAndroid Build Coastguard Worker def package_name(self) -> str: 1083*c2e18aaaSAndroid Build Coastguard Worker return self._package_name 1084*c2e18aaaSAndroid Build Coastguard Worker 1085*c2e18aaaSAndroid Build Coastguard Worker def required_imports(self) -> Set[Import]: 1086*c2e18aaaSAndroid Build Coastguard Worker return {Import('//bazel/rules:tradefed_test.bzl', self._rule_name)} 1087*c2e18aaaSAndroid Build Coastguard Worker 1088*c2e18aaaSAndroid Build Coastguard Worker def dependencies(self) -> List[ModuleRef]: 1089*c2e18aaaSAndroid Build Coastguard Worker prerequisite_refs = map(ModuleRef.for_name, self._prerequisites) 1090*c2e18aaaSAndroid Build Coastguard Worker 1091*c2e18aaaSAndroid Build Coastguard Worker declared_dep_refs = [] 1092*c2e18aaaSAndroid Build Coastguard Worker for value in self._attributes.values(): 1093*c2e18aaaSAndroid Build Coastguard Worker if isinstance(value, Iterable): 1094*c2e18aaaSAndroid Build Coastguard Worker declared_dep_refs.extend( 1095*c2e18aaaSAndroid Build Coastguard Worker [dep for dep in value if isinstance(dep, ModuleRef)] 1096*c2e18aaaSAndroid Build Coastguard Worker ) 1097*c2e18aaaSAndroid Build Coastguard Worker elif isinstance(value, ModuleRef): 1098*c2e18aaaSAndroid Build Coastguard Worker declared_dep_refs.append(value) 1099*c2e18aaaSAndroid Build Coastguard Worker 1100*c2e18aaaSAndroid Build Coastguard Worker return declared_dep_refs + list(prerequisite_refs) 1101*c2e18aaaSAndroid Build Coastguard Worker 1102*c2e18aaaSAndroid Build Coastguard Worker def write_to_build_file(self, f: IO): 1103*c2e18aaaSAndroid Build Coastguard Worker prebuilt_target_name = self._attributes['test'].target().qualified_name() 1104*c2e18aaaSAndroid Build Coastguard Worker writer = IndentWriter(f) 1105*c2e18aaaSAndroid Build Coastguard Worker build_file_writer = BuildFileWriter(writer) 1106*c2e18aaaSAndroid Build Coastguard Worker 1107*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(f'{self._rule_name}(') 1108*c2e18aaaSAndroid Build Coastguard Worker 1109*c2e18aaaSAndroid Build Coastguard Worker with writer.indent(): 1110*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_string_attribute('name', self._attributes['name']) 1111*c2e18aaaSAndroid Build Coastguard Worker 1112*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_string_attribute( 1113*c2e18aaaSAndroid Build Coastguard Worker 'module_name', self._attributes['module_name'] 1114*c2e18aaaSAndroid Build Coastguard Worker ) 1115*c2e18aaaSAndroid Build Coastguard Worker 1116*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_string_attribute('test', prebuilt_target_name) 1117*c2e18aaaSAndroid Build Coastguard Worker 1118*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_label_list_attribute( 1119*c2e18aaaSAndroid Build Coastguard Worker 'tradefed_deps', self._attributes.get('tradefed_deps') 1120*c2e18aaaSAndroid Build Coastguard Worker ) 1121*c2e18aaaSAndroid Build Coastguard Worker 1122*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_string_list_attribute( 1123*c2e18aaaSAndroid Build Coastguard Worker 'suites', sorted(self._attributes.get('suites', [])) 1124*c2e18aaaSAndroid Build Coastguard Worker ) 1125*c2e18aaaSAndroid Build Coastguard Worker 1126*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_string_list_attribute( 1127*c2e18aaaSAndroid Build Coastguard Worker 'tags', sorted(self._attributes.get('tags', [])) 1128*c2e18aaaSAndroid Build Coastguard Worker ) 1129*c2e18aaaSAndroid Build Coastguard Worker 1130*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_label_attribute( 1131*c2e18aaaSAndroid Build Coastguard Worker 'jdk', self._attributes.get('jdk', None) 1132*c2e18aaaSAndroid Build Coastguard Worker ) 1133*c2e18aaaSAndroid Build Coastguard Worker 1134*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(')') 1135*c2e18aaaSAndroid Build Coastguard Worker 1136*c2e18aaaSAndroid Build Coastguard Worker 1137*c2e18aaaSAndroid Build Coastguard Workerdef _read_robolectric_jdk_path( 1138*c2e18aaaSAndroid Build Coastguard Worker test_xml_config_template: pathlib.Path, 1139*c2e18aaaSAndroid Build Coastguard Worker) -> pathlib.Path: 1140*c2e18aaaSAndroid Build Coastguard Worker if not test_xml_config_template.is_file(): 1141*c2e18aaaSAndroid Build Coastguard Worker return None 1142*c2e18aaaSAndroid Build Coastguard Worker 1143*c2e18aaaSAndroid Build Coastguard Worker xml_root = ET.parse(test_xml_config_template).getroot() 1144*c2e18aaaSAndroid Build Coastguard Worker option = xml_root.find(".//option[@name='java-folder']") 1145*c2e18aaaSAndroid Build Coastguard Worker jdk_path = pathlib.Path(option.get('value', '')) 1146*c2e18aaaSAndroid Build Coastguard Worker 1147*c2e18aaaSAndroid Build Coastguard Worker if not jdk_path.is_relative_to('prebuilts/jdk'): 1148*c2e18aaaSAndroid Build Coastguard Worker raise ValueError( 1149*c2e18aaaSAndroid Build Coastguard Worker f'Failed to get "java-folder" from `{test_xml_config_template}`' 1150*c2e18aaaSAndroid Build Coastguard Worker ) 1151*c2e18aaaSAndroid Build Coastguard Worker 1152*c2e18aaaSAndroid Build Coastguard Worker return jdk_path 1153*c2e18aaaSAndroid Build Coastguard Worker 1154*c2e18aaaSAndroid Build Coastguard Worker 1155*c2e18aaaSAndroid Build Coastguard Workerclass BuildFileWriter: 1156*c2e18aaaSAndroid Build Coastguard Worker """Class for writing BUILD files.""" 1157*c2e18aaaSAndroid Build Coastguard Worker 1158*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, underlying: IndentWriter): 1159*c2e18aaaSAndroid Build Coastguard Worker self._underlying = underlying 1160*c2e18aaaSAndroid Build Coastguard Worker 1161*c2e18aaaSAndroid Build Coastguard Worker def write_string_attribute(self, attribute_name, value): 1162*c2e18aaaSAndroid Build Coastguard Worker if value is None: 1163*c2e18aaaSAndroid Build Coastguard Worker return 1164*c2e18aaaSAndroid Build Coastguard Worker 1165*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'{attribute_name} = "{value}",') 1166*c2e18aaaSAndroid Build Coastguard Worker 1167*c2e18aaaSAndroid Build Coastguard Worker def write_label_attribute(self, attribute_name: str, label_name: str): 1168*c2e18aaaSAndroid Build Coastguard Worker if label_name is None: 1169*c2e18aaaSAndroid Build Coastguard Worker return 1170*c2e18aaaSAndroid Build Coastguard Worker 1171*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'{attribute_name} = "{label_name}",') 1172*c2e18aaaSAndroid Build Coastguard Worker 1173*c2e18aaaSAndroid Build Coastguard Worker def write_string_list_attribute(self, attribute_name, values): 1174*c2e18aaaSAndroid Build Coastguard Worker if not values: 1175*c2e18aaaSAndroid Build Coastguard Worker return 1176*c2e18aaaSAndroid Build Coastguard Worker 1177*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'{attribute_name} = [') 1178*c2e18aaaSAndroid Build Coastguard Worker 1179*c2e18aaaSAndroid Build Coastguard Worker with self._underlying.indent(): 1180*c2e18aaaSAndroid Build Coastguard Worker for value in values: 1181*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'"{value}",') 1182*c2e18aaaSAndroid Build Coastguard Worker 1183*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line('],') 1184*c2e18aaaSAndroid Build Coastguard Worker 1185*c2e18aaaSAndroid Build Coastguard Worker def write_label_list_attribute( 1186*c2e18aaaSAndroid Build Coastguard Worker self, attribute_name: str, modules: List[ModuleRef] 1187*c2e18aaaSAndroid Build Coastguard Worker ): 1188*c2e18aaaSAndroid Build Coastguard Worker if not modules: 1189*c2e18aaaSAndroid Build Coastguard Worker return 1190*c2e18aaaSAndroid Build Coastguard Worker 1191*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'{attribute_name} = [') 1192*c2e18aaaSAndroid Build Coastguard Worker 1193*c2e18aaaSAndroid Build Coastguard Worker with self._underlying.indent(): 1194*c2e18aaaSAndroid Build Coastguard Worker for label in sorted(set(m.target().qualified_name() for m in modules)): 1195*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'"{label}",') 1196*c2e18aaaSAndroid Build Coastguard Worker 1197*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line('],') 1198*c2e18aaaSAndroid Build Coastguard Worker 1199*c2e18aaaSAndroid Build Coastguard Worker def write_glob_attribute(self, attribute_name: str, patterns: List[str]): 1200*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'{attribute_name} = glob([') 1201*c2e18aaaSAndroid Build Coastguard Worker 1202*c2e18aaaSAndroid Build Coastguard Worker with self._underlying.indent(): 1203*c2e18aaaSAndroid Build Coastguard Worker for pattern in patterns: 1204*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(f'"{pattern}",') 1205*c2e18aaaSAndroid Build Coastguard Worker 1206*c2e18aaaSAndroid Build Coastguard Worker self._underlying.write_line(']),') 1207*c2e18aaaSAndroid Build Coastguard Worker 1208*c2e18aaaSAndroid Build Coastguard Worker 1209*c2e18aaaSAndroid Build Coastguard Worker@dataclasses.dataclass(frozen=True) 1210*c2e18aaaSAndroid Build Coastguard Workerclass Dependencies: 1211*c2e18aaaSAndroid Build Coastguard Worker static_dep_refs: List[ModuleRef] 1212*c2e18aaaSAndroid Build Coastguard Worker runtime_dep_refs: List[ModuleRef] 1213*c2e18aaaSAndroid Build Coastguard Worker data_dep_refs: List[ModuleRef] 1214*c2e18aaaSAndroid Build Coastguard Worker device_data_dep_refs: List[ModuleRef] 1215*c2e18aaaSAndroid Build Coastguard Worker 1216*c2e18aaaSAndroid Build Coastguard Worker 1217*c2e18aaaSAndroid Build Coastguard Workerclass SoongPrebuiltTarget(Target): 1218*c2e18aaaSAndroid Build Coastguard Worker """Class for generating a Soong prebuilt target on disk.""" 1219*c2e18aaaSAndroid Build Coastguard Worker 1220*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 1221*c2e18aaaSAndroid Build Coastguard Worker def create( 1222*c2e18aaaSAndroid Build Coastguard Worker gen: WorkspaceGenerator, info: Dict[str, Any], package_name: str = '' 1223*c2e18aaaSAndroid Build Coastguard Worker ): 1224*c2e18aaaSAndroid Build Coastguard Worker module_name = info['module_name'] 1225*c2e18aaaSAndroid Build Coastguard Worker 1226*c2e18aaaSAndroid Build Coastguard Worker configs = [ 1227*c2e18aaaSAndroid Build Coastguard Worker Config('host', gen.host_out_path), 1228*c2e18aaaSAndroid Build Coastguard Worker Config('device', gen.resource_manager.get_product_out_file_path()), 1229*c2e18aaaSAndroid Build Coastguard Worker ] 1230*c2e18aaaSAndroid Build Coastguard Worker 1231*c2e18aaaSAndroid Build Coastguard Worker installed_paths = get_module_installed_paths( 1232*c2e18aaaSAndroid Build Coastguard Worker info, gen.resource_manager.get_src_file_path() 1233*c2e18aaaSAndroid Build Coastguard Worker ) 1234*c2e18aaaSAndroid Build Coastguard Worker config_files = group_paths_by_config(configs, installed_paths) 1235*c2e18aaaSAndroid Build Coastguard Worker 1236*c2e18aaaSAndroid Build Coastguard Worker # For test modules, we only create symbolic link to the 'testcases' 1237*c2e18aaaSAndroid Build Coastguard Worker # directory since the information in module-info is not accurate. 1238*c2e18aaaSAndroid Build Coastguard Worker if gen.mod_info.is_tradefed_testable_module(info): 1239*c2e18aaaSAndroid Build Coastguard Worker config_files = { 1240*c2e18aaaSAndroid Build Coastguard Worker c: [c.out_path.joinpath(f'testcases/{module_name}')] 1241*c2e18aaaSAndroid Build Coastguard Worker for c in config_files.keys() 1242*c2e18aaaSAndroid Build Coastguard Worker } 1243*c2e18aaaSAndroid Build Coastguard Worker 1244*c2e18aaaSAndroid Build Coastguard Worker enabled_features = gen.enabled_features 1245*c2e18aaaSAndroid Build Coastguard Worker 1246*c2e18aaaSAndroid Build Coastguard Worker return SoongPrebuiltTarget( 1247*c2e18aaaSAndroid Build Coastguard Worker info, 1248*c2e18aaaSAndroid Build Coastguard Worker package_name, 1249*c2e18aaaSAndroid Build Coastguard Worker config_files, 1250*c2e18aaaSAndroid Build Coastguard Worker Dependencies( 1251*c2e18aaaSAndroid Build Coastguard Worker static_dep_refs=find_static_dep_refs( 1252*c2e18aaaSAndroid Build Coastguard Worker gen.mod_info, 1253*c2e18aaaSAndroid Build Coastguard Worker info, 1254*c2e18aaaSAndroid Build Coastguard Worker configs, 1255*c2e18aaaSAndroid Build Coastguard Worker gen.resource_manager.get_src_file_path(), 1256*c2e18aaaSAndroid Build Coastguard Worker enabled_features, 1257*c2e18aaaSAndroid Build Coastguard Worker ), 1258*c2e18aaaSAndroid Build Coastguard Worker runtime_dep_refs=find_runtime_dep_refs( 1259*c2e18aaaSAndroid Build Coastguard Worker gen.mod_info, 1260*c2e18aaaSAndroid Build Coastguard Worker info, 1261*c2e18aaaSAndroid Build Coastguard Worker configs, 1262*c2e18aaaSAndroid Build Coastguard Worker gen.resource_manager.get_src_file_path(), 1263*c2e18aaaSAndroid Build Coastguard Worker enabled_features, 1264*c2e18aaaSAndroid Build Coastguard Worker ), 1265*c2e18aaaSAndroid Build Coastguard Worker data_dep_refs=find_data_dep_refs( 1266*c2e18aaaSAndroid Build Coastguard Worker gen.mod_info, 1267*c2e18aaaSAndroid Build Coastguard Worker info, 1268*c2e18aaaSAndroid Build Coastguard Worker configs, 1269*c2e18aaaSAndroid Build Coastguard Worker gen.resource_manager.get_src_file_path(), 1270*c2e18aaaSAndroid Build Coastguard Worker ), 1271*c2e18aaaSAndroid Build Coastguard Worker device_data_dep_refs=find_device_data_dep_refs(gen, info), 1272*c2e18aaaSAndroid Build Coastguard Worker ), 1273*c2e18aaaSAndroid Build Coastguard Worker [ 1274*c2e18aaaSAndroid Build Coastguard Worker c 1275*c2e18aaaSAndroid Build Coastguard Worker for c in configs 1276*c2e18aaaSAndroid Build Coastguard Worker if c.name 1277*c2e18aaaSAndroid Build Coastguard Worker in map(str.lower, info.get(constants.MODULE_SUPPORTED_VARIANTS, [])) 1278*c2e18aaaSAndroid Build Coastguard Worker ], 1279*c2e18aaaSAndroid Build Coastguard Worker ) 1280*c2e18aaaSAndroid Build Coastguard Worker 1281*c2e18aaaSAndroid Build Coastguard Worker def __init__( 1282*c2e18aaaSAndroid Build Coastguard Worker self, 1283*c2e18aaaSAndroid Build Coastguard Worker info: Dict[str, Any], 1284*c2e18aaaSAndroid Build Coastguard Worker package_name: str, 1285*c2e18aaaSAndroid Build Coastguard Worker config_files: Dict[Config, List[pathlib.Path]], 1286*c2e18aaaSAndroid Build Coastguard Worker deps: Dependencies, 1287*c2e18aaaSAndroid Build Coastguard Worker supported_configs: List[Config], 1288*c2e18aaaSAndroid Build Coastguard Worker ): 1289*c2e18aaaSAndroid Build Coastguard Worker self._target_name = info[constants.MODULE_INFO_ID] 1290*c2e18aaaSAndroid Build Coastguard Worker self._module_name = info[constants.MODULE_NAME] 1291*c2e18aaaSAndroid Build Coastguard Worker self._package_name = package_name 1292*c2e18aaaSAndroid Build Coastguard Worker self.config_files = config_files 1293*c2e18aaaSAndroid Build Coastguard Worker self.deps = deps 1294*c2e18aaaSAndroid Build Coastguard Worker self.suites = info.get(constants.MODULE_COMPATIBILITY_SUITES, []) 1295*c2e18aaaSAndroid Build Coastguard Worker self._supported_configs = supported_configs 1296*c2e18aaaSAndroid Build Coastguard Worker 1297*c2e18aaaSAndroid Build Coastguard Worker def name(self) -> str: 1298*c2e18aaaSAndroid Build Coastguard Worker return self._target_name 1299*c2e18aaaSAndroid Build Coastguard Worker 1300*c2e18aaaSAndroid Build Coastguard Worker def package_name(self) -> str: 1301*c2e18aaaSAndroid Build Coastguard Worker return self._package_name 1302*c2e18aaaSAndroid Build Coastguard Worker 1303*c2e18aaaSAndroid Build Coastguard Worker def required_imports(self) -> Set[Import]: 1304*c2e18aaaSAndroid Build Coastguard Worker return { 1305*c2e18aaaSAndroid Build Coastguard Worker Import('//bazel/rules:soong_prebuilt.bzl', self._rule_name()), 1306*c2e18aaaSAndroid Build Coastguard Worker } 1307*c2e18aaaSAndroid Build Coastguard Worker 1308*c2e18aaaSAndroid Build Coastguard Worker @functools.lru_cache(maxsize=128) 1309*c2e18aaaSAndroid Build Coastguard Worker def supported_configs(self) -> Set[Config]: 1310*c2e18aaaSAndroid Build Coastguard Worker # We deduce the supported configs from the installed paths since the 1311*c2e18aaaSAndroid Build Coastguard Worker # build exports incorrect metadata for some module types such as 1312*c2e18aaaSAndroid Build Coastguard Worker # Robolectric. The information exported from the build is only used if 1313*c2e18aaaSAndroid Build Coastguard Worker # the module does not have any installed paths. 1314*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/232929584): Remove this once all modules correctly export the 1315*c2e18aaaSAndroid Build Coastguard Worker # supported variants. 1316*c2e18aaaSAndroid Build Coastguard Worker supported_configs = set(self.config_files.keys()) 1317*c2e18aaaSAndroid Build Coastguard Worker if supported_configs: 1318*c2e18aaaSAndroid Build Coastguard Worker return supported_configs 1319*c2e18aaaSAndroid Build Coastguard Worker 1320*c2e18aaaSAndroid Build Coastguard Worker return self._supported_configs 1321*c2e18aaaSAndroid Build Coastguard Worker 1322*c2e18aaaSAndroid Build Coastguard Worker def dependencies(self) -> List[ModuleRef]: 1323*c2e18aaaSAndroid Build Coastguard Worker all_deps = set(self.deps.runtime_dep_refs) 1324*c2e18aaaSAndroid Build Coastguard Worker all_deps.update(self.deps.data_dep_refs) 1325*c2e18aaaSAndroid Build Coastguard Worker all_deps.update(self.deps.device_data_dep_refs) 1326*c2e18aaaSAndroid Build Coastguard Worker all_deps.update(self.deps.static_dep_refs) 1327*c2e18aaaSAndroid Build Coastguard Worker return list(all_deps) 1328*c2e18aaaSAndroid Build Coastguard Worker 1329*c2e18aaaSAndroid Build Coastguard Worker def write_to_build_file(self, f: IO): 1330*c2e18aaaSAndroid Build Coastguard Worker writer = IndentWriter(f) 1331*c2e18aaaSAndroid Build Coastguard Worker build_file_writer = BuildFileWriter(writer) 1332*c2e18aaaSAndroid Build Coastguard Worker 1333*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(f'{self._rule_name()}(') 1334*c2e18aaaSAndroid Build Coastguard Worker 1335*c2e18aaaSAndroid Build Coastguard Worker with writer.indent(): 1336*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(f'name = "{self._target_name}",') 1337*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(f'module_name = "{self._module_name}",') 1338*c2e18aaaSAndroid Build Coastguard Worker self._write_files_attribute(writer) 1339*c2e18aaaSAndroid Build Coastguard Worker self._write_deps_attribute( 1340*c2e18aaaSAndroid Build Coastguard Worker writer, 'static_deps', self.deps.static_dep_refs 1341*c2e18aaaSAndroid Build Coastguard Worker ) 1342*c2e18aaaSAndroid Build Coastguard Worker self._write_deps_attribute( 1343*c2e18aaaSAndroid Build Coastguard Worker writer, 'runtime_deps', self.deps.runtime_dep_refs 1344*c2e18aaaSAndroid Build Coastguard Worker ) 1345*c2e18aaaSAndroid Build Coastguard Worker self._write_deps_attribute(writer, 'data', self.deps.data_dep_refs) 1346*c2e18aaaSAndroid Build Coastguard Worker 1347*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_label_list_attribute( 1348*c2e18aaaSAndroid Build Coastguard Worker 'device_data', self.deps.device_data_dep_refs 1349*c2e18aaaSAndroid Build Coastguard Worker ) 1350*c2e18aaaSAndroid Build Coastguard Worker build_file_writer.write_string_list_attribute( 1351*c2e18aaaSAndroid Build Coastguard Worker 'suites', sorted(self.suites) 1352*c2e18aaaSAndroid Build Coastguard Worker ) 1353*c2e18aaaSAndroid Build Coastguard Worker 1354*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(')') 1355*c2e18aaaSAndroid Build Coastguard Worker 1356*c2e18aaaSAndroid Build Coastguard Worker def create_filesystem_layout(self, package_dir: pathlib.Path): 1357*c2e18aaaSAndroid Build Coastguard Worker prebuilts_dir = package_dir.joinpath(self._target_name) 1358*c2e18aaaSAndroid Build Coastguard Worker prebuilts_dir.mkdir() 1359*c2e18aaaSAndroid Build Coastguard Worker 1360*c2e18aaaSAndroid Build Coastguard Worker for config, files in self.config_files.items(): 1361*c2e18aaaSAndroid Build Coastguard Worker config_prebuilts_dir = prebuilts_dir.joinpath(config.name) 1362*c2e18aaaSAndroid Build Coastguard Worker config_prebuilts_dir.mkdir() 1363*c2e18aaaSAndroid Build Coastguard Worker 1364*c2e18aaaSAndroid Build Coastguard Worker for f in files: 1365*c2e18aaaSAndroid Build Coastguard Worker rel_path = f.relative_to(config.out_path) 1366*c2e18aaaSAndroid Build Coastguard Worker symlink = config_prebuilts_dir.joinpath(rel_path) 1367*c2e18aaaSAndroid Build Coastguard Worker symlink.parent.mkdir(parents=True, exist_ok=True) 1368*c2e18aaaSAndroid Build Coastguard Worker symlink.symlink_to(f) 1369*c2e18aaaSAndroid Build Coastguard Worker 1370*c2e18aaaSAndroid Build Coastguard Worker def _rule_name(self): 1371*c2e18aaaSAndroid Build Coastguard Worker return ( 1372*c2e18aaaSAndroid Build Coastguard Worker 'soong_prebuilt' if self.config_files else 'soong_uninstalled_prebuilt' 1373*c2e18aaaSAndroid Build Coastguard Worker ) 1374*c2e18aaaSAndroid Build Coastguard Worker 1375*c2e18aaaSAndroid Build Coastguard Worker def _write_files_attribute(self, writer: IndentWriter): 1376*c2e18aaaSAndroid Build Coastguard Worker if not self.config_files: 1377*c2e18aaaSAndroid Build Coastguard Worker return 1378*c2e18aaaSAndroid Build Coastguard Worker 1379*c2e18aaaSAndroid Build Coastguard Worker writer.write('files = ') 1380*c2e18aaaSAndroid Build Coastguard Worker write_config_select( 1381*c2e18aaaSAndroid Build Coastguard Worker writer, 1382*c2e18aaaSAndroid Build Coastguard Worker self.config_files, 1383*c2e18aaaSAndroid Build Coastguard Worker lambda c, _: writer.write( 1384*c2e18aaaSAndroid Build Coastguard Worker f'glob(["{self._target_name}/{c.name}/**/*"])' 1385*c2e18aaaSAndroid Build Coastguard Worker ), 1386*c2e18aaaSAndroid Build Coastguard Worker ) 1387*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(',') 1388*c2e18aaaSAndroid Build Coastguard Worker 1389*c2e18aaaSAndroid Build Coastguard Worker def _write_deps_attribute(self, writer, attribute_name, module_refs): 1390*c2e18aaaSAndroid Build Coastguard Worker config_deps = filter_configs( 1391*c2e18aaaSAndroid Build Coastguard Worker group_targets_by_config(r.target() for r in module_refs), 1392*c2e18aaaSAndroid Build Coastguard Worker self.supported_configs(), 1393*c2e18aaaSAndroid Build Coastguard Worker ) 1394*c2e18aaaSAndroid Build Coastguard Worker 1395*c2e18aaaSAndroid Build Coastguard Worker if not config_deps: 1396*c2e18aaaSAndroid Build Coastguard Worker return 1397*c2e18aaaSAndroid Build Coastguard Worker 1398*c2e18aaaSAndroid Build Coastguard Worker for config in self.supported_configs(): 1399*c2e18aaaSAndroid Build Coastguard Worker config_deps.setdefault(config, []) 1400*c2e18aaaSAndroid Build Coastguard Worker 1401*c2e18aaaSAndroid Build Coastguard Worker writer.write(f'{attribute_name} = ') 1402*c2e18aaaSAndroid Build Coastguard Worker write_config_select( 1403*c2e18aaaSAndroid Build Coastguard Worker writer, 1404*c2e18aaaSAndroid Build Coastguard Worker config_deps, 1405*c2e18aaaSAndroid Build Coastguard Worker lambda _, targets: write_target_list(writer, targets), 1406*c2e18aaaSAndroid Build Coastguard Worker ) 1407*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(',') 1408*c2e18aaaSAndroid Build Coastguard Worker 1409*c2e18aaaSAndroid Build Coastguard Worker 1410*c2e18aaaSAndroid Build Coastguard Workerdef group_paths_by_config( 1411*c2e18aaaSAndroid Build Coastguard Worker configs: List[Config], paths: List[pathlib.Path] 1412*c2e18aaaSAndroid Build Coastguard Worker) -> Dict[Config, List[pathlib.Path]]: 1413*c2e18aaaSAndroid Build Coastguard Worker 1414*c2e18aaaSAndroid Build Coastguard Worker config_files = defaultdict(list) 1415*c2e18aaaSAndroid Build Coastguard Worker 1416*c2e18aaaSAndroid Build Coastguard Worker for f in paths: 1417*c2e18aaaSAndroid Build Coastguard Worker matching_configs = [c for c in configs if _is_relative_to(f, c.out_path)] 1418*c2e18aaaSAndroid Build Coastguard Worker 1419*c2e18aaaSAndroid Build Coastguard Worker if not matching_configs: 1420*c2e18aaaSAndroid Build Coastguard Worker continue 1421*c2e18aaaSAndroid Build Coastguard Worker 1422*c2e18aaaSAndroid Build Coastguard Worker # The path can only appear in ANDROID_HOST_OUT for host target or 1423*c2e18aaaSAndroid Build Coastguard Worker # ANDROID_PRODUCT_OUT, but cannot appear in both. 1424*c2e18aaaSAndroid Build Coastguard Worker if len(matching_configs) > 1: 1425*c2e18aaaSAndroid Build Coastguard Worker raise ValueError( 1426*c2e18aaaSAndroid Build Coastguard Worker f'Installed path `{f}` is not in' 1427*c2e18aaaSAndroid Build Coastguard Worker ' ANDROID_HOST_OUT or ANDROID_PRODUCT_OUT' 1428*c2e18aaaSAndroid Build Coastguard Worker ) 1429*c2e18aaaSAndroid Build Coastguard Worker 1430*c2e18aaaSAndroid Build Coastguard Worker config_files[matching_configs[0]].append(f) 1431*c2e18aaaSAndroid Build Coastguard Worker 1432*c2e18aaaSAndroid Build Coastguard Worker return config_files 1433*c2e18aaaSAndroid Build Coastguard Worker 1434*c2e18aaaSAndroid Build Coastguard Worker 1435*c2e18aaaSAndroid Build Coastguard Workerdef group_targets_by_config( 1436*c2e18aaaSAndroid Build Coastguard Worker targets: List[Target], 1437*c2e18aaaSAndroid Build Coastguard Worker) -> Dict[Config, List[Target]]: 1438*c2e18aaaSAndroid Build Coastguard Worker 1439*c2e18aaaSAndroid Build Coastguard Worker config_to_targets = defaultdict(list) 1440*c2e18aaaSAndroid Build Coastguard Worker 1441*c2e18aaaSAndroid Build Coastguard Worker for target in targets: 1442*c2e18aaaSAndroid Build Coastguard Worker for config in target.supported_configs(): 1443*c2e18aaaSAndroid Build Coastguard Worker config_to_targets[config].append(target) 1444*c2e18aaaSAndroid Build Coastguard Worker 1445*c2e18aaaSAndroid Build Coastguard Worker return config_to_targets 1446*c2e18aaaSAndroid Build Coastguard Worker 1447*c2e18aaaSAndroid Build Coastguard Worker 1448*c2e18aaaSAndroid Build Coastguard Workerdef filter_configs( 1449*c2e18aaaSAndroid Build Coastguard Worker config_dict: Dict[Config, Any], 1450*c2e18aaaSAndroid Build Coastguard Worker configs: Set[Config], 1451*c2e18aaaSAndroid Build Coastguard Worker) -> Dict[Config, Any]: 1452*c2e18aaaSAndroid Build Coastguard Worker return {k: v for (k, v) in config_dict.items() if k in configs} 1453*c2e18aaaSAndroid Build Coastguard Worker 1454*c2e18aaaSAndroid Build Coastguard Worker 1455*c2e18aaaSAndroid Build Coastguard Workerdef _is_relative_to(path1: pathlib.Path, path2: pathlib.Path) -> bool: 1456*c2e18aaaSAndroid Build Coastguard Worker """Return True if the path is relative to another path or False.""" 1457*c2e18aaaSAndroid Build Coastguard Worker # Note that this implementation is required because Path.is_relative_to only 1458*c2e18aaaSAndroid Build Coastguard Worker # exists starting with Python 3.9. 1459*c2e18aaaSAndroid Build Coastguard Worker try: 1460*c2e18aaaSAndroid Build Coastguard Worker path1.relative_to(path2) 1461*c2e18aaaSAndroid Build Coastguard Worker return True 1462*c2e18aaaSAndroid Build Coastguard Worker except ValueError: 1463*c2e18aaaSAndroid Build Coastguard Worker return False 1464*c2e18aaaSAndroid Build Coastguard Worker 1465*c2e18aaaSAndroid Build Coastguard Worker 1466*c2e18aaaSAndroid Build Coastguard Workerdef get_module_installed_paths( 1467*c2e18aaaSAndroid Build Coastguard Worker info: Dict[str, Any], src_root_path: pathlib.Path 1468*c2e18aaaSAndroid Build Coastguard Worker) -> List[pathlib.Path]: 1469*c2e18aaaSAndroid Build Coastguard Worker 1470*c2e18aaaSAndroid Build Coastguard Worker # Install paths in module-info are usually relative to the Android 1471*c2e18aaaSAndroid Build Coastguard Worker # source root ${ANDROID_BUILD_TOP}. When the output directory is 1472*c2e18aaaSAndroid Build Coastguard Worker # customized by the user however, the install paths are absolute. 1473*c2e18aaaSAndroid Build Coastguard Worker def resolve(install_path_string): 1474*c2e18aaaSAndroid Build Coastguard Worker install_path = pathlib.Path(install_path_string) 1475*c2e18aaaSAndroid Build Coastguard Worker if not install_path.expanduser().is_absolute(): 1476*c2e18aaaSAndroid Build Coastguard Worker return src_root_path.joinpath(install_path) 1477*c2e18aaaSAndroid Build Coastguard Worker return install_path 1478*c2e18aaaSAndroid Build Coastguard Worker 1479*c2e18aaaSAndroid Build Coastguard Worker return map(resolve, info.get(constants.MODULE_INSTALLED, [])) 1480*c2e18aaaSAndroid Build Coastguard Worker 1481*c2e18aaaSAndroid Build Coastguard Worker 1482*c2e18aaaSAndroid Build Coastguard Workerdef find_runtime_dep_refs( 1483*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, 1484*c2e18aaaSAndroid Build Coastguard Worker info: module_info.Module, 1485*c2e18aaaSAndroid Build Coastguard Worker configs: List[Config], 1486*c2e18aaaSAndroid Build Coastguard Worker src_root_path: pathlib.Path, 1487*c2e18aaaSAndroid Build Coastguard Worker enabled_features: List[Features], 1488*c2e18aaaSAndroid Build Coastguard Worker) -> List[ModuleRef]: 1489*c2e18aaaSAndroid Build Coastguard Worker """Return module references for runtime dependencies.""" 1490*c2e18aaaSAndroid Build Coastguard Worker 1491*c2e18aaaSAndroid Build Coastguard Worker # We don't use the `dependencies` module-info field for shared libraries 1492*c2e18aaaSAndroid Build Coastguard Worker # since it's ambiguous and could generate more targets and pull in more 1493*c2e18aaaSAndroid Build Coastguard Worker # dependencies than necessary. In particular, libraries that support both 1494*c2e18aaaSAndroid Build Coastguard Worker # static and dynamic linking could end up becoming runtime dependencies 1495*c2e18aaaSAndroid Build Coastguard Worker # even though the build specifies static linking. For example, if a target 1496*c2e18aaaSAndroid Build Coastguard Worker # 'T' is statically linked to 'U' which supports both variants, the latter 1497*c2e18aaaSAndroid Build Coastguard Worker # still appears as a dependency. Since we can't tell, this would result in 1498*c2e18aaaSAndroid Build Coastguard Worker # the shared library variant of 'U' being added on the library path. 1499*c2e18aaaSAndroid Build Coastguard Worker libs = set() 1500*c2e18aaaSAndroid Build Coastguard Worker libs.update(info.get(constants.MODULE_SHARED_LIBS, [])) 1501*c2e18aaaSAndroid Build Coastguard Worker libs.update(info.get(constants.MODULE_RUNTIME_DEPS, [])) 1502*c2e18aaaSAndroid Build Coastguard Worker 1503*c2e18aaaSAndroid Build Coastguard Worker if Features.EXPERIMENTAL_JAVA_RUNTIME_DEPENDENCIES in enabled_features: 1504*c2e18aaaSAndroid Build Coastguard Worker libs.update(info.get(constants.MODULE_LIBS, [])) 1505*c2e18aaaSAndroid Build Coastguard Worker 1506*c2e18aaaSAndroid Build Coastguard Worker runtime_dep_refs = _find_module_refs(mod_info, configs, src_root_path, libs) 1507*c2e18aaaSAndroid Build Coastguard Worker 1508*c2e18aaaSAndroid Build Coastguard Worker runtime_library_class = {'RLIB_LIBRARIES', 'DYLIB_LIBRARIES'} 1509*c2e18aaaSAndroid Build Coastguard Worker # We collect rlibs even though they are technically static libraries since 1510*c2e18aaaSAndroid Build Coastguard Worker # they could refer to dylibs which are required at runtime. Generating 1511*c2e18aaaSAndroid Build Coastguard Worker # Bazel targets for these intermediate modules keeps the generator simple 1512*c2e18aaaSAndroid Build Coastguard Worker # and preserves the shape (isomorphic) of the Soong structure making the 1513*c2e18aaaSAndroid Build Coastguard Worker # workspace easier to debug. 1514*c2e18aaaSAndroid Build Coastguard Worker for dep_name in info.get(constants.MODULE_DEPENDENCIES, []): 1515*c2e18aaaSAndroid Build Coastguard Worker dep_info = mod_info.get_module_info(dep_name) 1516*c2e18aaaSAndroid Build Coastguard Worker if not dep_info: 1517*c2e18aaaSAndroid Build Coastguard Worker continue 1518*c2e18aaaSAndroid Build Coastguard Worker if not runtime_library_class.intersection( 1519*c2e18aaaSAndroid Build Coastguard Worker dep_info.get(constants.MODULE_CLASS, []) 1520*c2e18aaaSAndroid Build Coastguard Worker ): 1521*c2e18aaaSAndroid Build Coastguard Worker continue 1522*c2e18aaaSAndroid Build Coastguard Worker runtime_dep_refs.append(ModuleRef.for_info(dep_info)) 1523*c2e18aaaSAndroid Build Coastguard Worker 1524*c2e18aaaSAndroid Build Coastguard Worker return runtime_dep_refs 1525*c2e18aaaSAndroid Build Coastguard Worker 1526*c2e18aaaSAndroid Build Coastguard Worker 1527*c2e18aaaSAndroid Build Coastguard Workerdef find_data_dep_refs( 1528*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, 1529*c2e18aaaSAndroid Build Coastguard Worker info: module_info.Module, 1530*c2e18aaaSAndroid Build Coastguard Worker configs: List[Config], 1531*c2e18aaaSAndroid Build Coastguard Worker src_root_path: pathlib.Path, 1532*c2e18aaaSAndroid Build Coastguard Worker) -> List[ModuleRef]: 1533*c2e18aaaSAndroid Build Coastguard Worker """Return module references for data dependencies.""" 1534*c2e18aaaSAndroid Build Coastguard Worker 1535*c2e18aaaSAndroid Build Coastguard Worker return _find_module_refs( 1536*c2e18aaaSAndroid Build Coastguard Worker mod_info, configs, src_root_path, info.get(constants.MODULE_DATA_DEPS, []) 1537*c2e18aaaSAndroid Build Coastguard Worker ) 1538*c2e18aaaSAndroid Build Coastguard Worker 1539*c2e18aaaSAndroid Build Coastguard Worker 1540*c2e18aaaSAndroid Build Coastguard Workerdef find_device_data_dep_refs( 1541*c2e18aaaSAndroid Build Coastguard Worker gen: WorkspaceGenerator, 1542*c2e18aaaSAndroid Build Coastguard Worker info: module_info.Module, 1543*c2e18aaaSAndroid Build Coastguard Worker) -> List[ModuleRef]: 1544*c2e18aaaSAndroid Build Coastguard Worker """Return module references for device data dependencies.""" 1545*c2e18aaaSAndroid Build Coastguard Worker 1546*c2e18aaaSAndroid Build Coastguard Worker return _find_module_refs( 1547*c2e18aaaSAndroid Build Coastguard Worker gen.mod_info, 1548*c2e18aaaSAndroid Build Coastguard Worker [Config('device', gen.resource_manager.get_product_out_file_path())], 1549*c2e18aaaSAndroid Build Coastguard Worker gen.resource_manager.get_src_file_path(), 1550*c2e18aaaSAndroid Build Coastguard Worker info.get(constants.MODULE_TARGET_DEPS, []), 1551*c2e18aaaSAndroid Build Coastguard Worker ) 1552*c2e18aaaSAndroid Build Coastguard Worker 1553*c2e18aaaSAndroid Build Coastguard Worker 1554*c2e18aaaSAndroid Build Coastguard Workerdef find_static_dep_refs( 1555*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, 1556*c2e18aaaSAndroid Build Coastguard Worker info: module_info.Module, 1557*c2e18aaaSAndroid Build Coastguard Worker configs: List[Config], 1558*c2e18aaaSAndroid Build Coastguard Worker src_root_path: pathlib.Path, 1559*c2e18aaaSAndroid Build Coastguard Worker enabled_features: List[Features], 1560*c2e18aaaSAndroid Build Coastguard Worker) -> List[ModuleRef]: 1561*c2e18aaaSAndroid Build Coastguard Worker """Return module references for static libraries.""" 1562*c2e18aaaSAndroid Build Coastguard Worker 1563*c2e18aaaSAndroid Build Coastguard Worker if Features.EXPERIMENTAL_JAVA_RUNTIME_DEPENDENCIES not in enabled_features: 1564*c2e18aaaSAndroid Build Coastguard Worker return [] 1565*c2e18aaaSAndroid Build Coastguard Worker 1566*c2e18aaaSAndroid Build Coastguard Worker static_libs = set() 1567*c2e18aaaSAndroid Build Coastguard Worker static_libs.update(info.get(constants.MODULE_STATIC_LIBS, [])) 1568*c2e18aaaSAndroid Build Coastguard Worker static_libs.update(info.get(constants.MODULE_STATIC_DEPS, [])) 1569*c2e18aaaSAndroid Build Coastguard Worker 1570*c2e18aaaSAndroid Build Coastguard Worker return _find_module_refs(mod_info, configs, src_root_path, static_libs) 1571*c2e18aaaSAndroid Build Coastguard Worker 1572*c2e18aaaSAndroid Build Coastguard Worker 1573*c2e18aaaSAndroid Build Coastguard Workerdef _find_module_refs( 1574*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, 1575*c2e18aaaSAndroid Build Coastguard Worker configs: List[Config], 1576*c2e18aaaSAndroid Build Coastguard Worker src_root_path: pathlib.Path, 1577*c2e18aaaSAndroid Build Coastguard Worker module_names: List[str], 1578*c2e18aaaSAndroid Build Coastguard Worker) -> List[ModuleRef]: 1579*c2e18aaaSAndroid Build Coastguard Worker """Return module references for modules.""" 1580*c2e18aaaSAndroid Build Coastguard Worker 1581*c2e18aaaSAndroid Build Coastguard Worker module_refs = [] 1582*c2e18aaaSAndroid Build Coastguard Worker 1583*c2e18aaaSAndroid Build Coastguard Worker for name in module_names: 1584*c2e18aaaSAndroid Build Coastguard Worker info = mod_info.get_module_info(name) 1585*c2e18aaaSAndroid Build Coastguard Worker if not info: 1586*c2e18aaaSAndroid Build Coastguard Worker continue 1587*c2e18aaaSAndroid Build Coastguard Worker 1588*c2e18aaaSAndroid Build Coastguard Worker installed_paths = get_module_installed_paths(info, src_root_path) 1589*c2e18aaaSAndroid Build Coastguard Worker config_files = group_paths_by_config(configs, installed_paths) 1590*c2e18aaaSAndroid Build Coastguard Worker if not config_files: 1591*c2e18aaaSAndroid Build Coastguard Worker continue 1592*c2e18aaaSAndroid Build Coastguard Worker 1593*c2e18aaaSAndroid Build Coastguard Worker module_refs.append(ModuleRef.for_info(info)) 1594*c2e18aaaSAndroid Build Coastguard Worker 1595*c2e18aaaSAndroid Build Coastguard Worker return module_refs 1596*c2e18aaaSAndroid Build Coastguard Worker 1597*c2e18aaaSAndroid Build Coastguard Worker 1598*c2e18aaaSAndroid Build Coastguard Workerclass IndentWriter: 1599*c2e18aaaSAndroid Build Coastguard Worker 1600*c2e18aaaSAndroid Build Coastguard Worker def __init__(self, f: IO): 1601*c2e18aaaSAndroid Build Coastguard Worker self._file = f 1602*c2e18aaaSAndroid Build Coastguard Worker self._indent_level = 0 1603*c2e18aaaSAndroid Build Coastguard Worker self._indent_string = 4 * ' ' 1604*c2e18aaaSAndroid Build Coastguard Worker self._indent_next = True 1605*c2e18aaaSAndroid Build Coastguard Worker 1606*c2e18aaaSAndroid Build Coastguard Worker def write_line(self, text: str = ''): 1607*c2e18aaaSAndroid Build Coastguard Worker if text: 1608*c2e18aaaSAndroid Build Coastguard Worker self.write(text) 1609*c2e18aaaSAndroid Build Coastguard Worker 1610*c2e18aaaSAndroid Build Coastguard Worker self._file.write('\n') 1611*c2e18aaaSAndroid Build Coastguard Worker self._indent_next = True 1612*c2e18aaaSAndroid Build Coastguard Worker 1613*c2e18aaaSAndroid Build Coastguard Worker def write(self, text): 1614*c2e18aaaSAndroid Build Coastguard Worker if self._indent_next: 1615*c2e18aaaSAndroid Build Coastguard Worker self._file.write(self._indent_string * self._indent_level) 1616*c2e18aaaSAndroid Build Coastguard Worker self._indent_next = False 1617*c2e18aaaSAndroid Build Coastguard Worker 1618*c2e18aaaSAndroid Build Coastguard Worker self._file.write(text) 1619*c2e18aaaSAndroid Build Coastguard Worker 1620*c2e18aaaSAndroid Build Coastguard Worker @contextlib.contextmanager 1621*c2e18aaaSAndroid Build Coastguard Worker def indent(self): 1622*c2e18aaaSAndroid Build Coastguard Worker self._indent_level += 1 1623*c2e18aaaSAndroid Build Coastguard Worker yield 1624*c2e18aaaSAndroid Build Coastguard Worker self._indent_level -= 1 1625*c2e18aaaSAndroid Build Coastguard Worker 1626*c2e18aaaSAndroid Build Coastguard Worker 1627*c2e18aaaSAndroid Build Coastguard Workerdef write_config_select( 1628*c2e18aaaSAndroid Build Coastguard Worker writer: IndentWriter, 1629*c2e18aaaSAndroid Build Coastguard Worker config_dict: Dict[Config, Any], 1630*c2e18aaaSAndroid Build Coastguard Worker write_value_fn: Callable, 1631*c2e18aaaSAndroid Build Coastguard Worker): 1632*c2e18aaaSAndroid Build Coastguard Worker writer.write_line('select({') 1633*c2e18aaaSAndroid Build Coastguard Worker 1634*c2e18aaaSAndroid Build Coastguard Worker with writer.indent(): 1635*c2e18aaaSAndroid Build Coastguard Worker for config, value in sorted(config_dict.items(), key=lambda c: c[0].name): 1636*c2e18aaaSAndroid Build Coastguard Worker 1637*c2e18aaaSAndroid Build Coastguard Worker writer.write(f'"//bazel/rules:{config.name}": ') 1638*c2e18aaaSAndroid Build Coastguard Worker write_value_fn(config, value) 1639*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(',') 1640*c2e18aaaSAndroid Build Coastguard Worker 1641*c2e18aaaSAndroid Build Coastguard Worker writer.write('})') 1642*c2e18aaaSAndroid Build Coastguard Worker 1643*c2e18aaaSAndroid Build Coastguard Worker 1644*c2e18aaaSAndroid Build Coastguard Workerdef write_target_list(writer: IndentWriter, targets: List[Target]): 1645*c2e18aaaSAndroid Build Coastguard Worker writer.write_line('[') 1646*c2e18aaaSAndroid Build Coastguard Worker 1647*c2e18aaaSAndroid Build Coastguard Worker with writer.indent(): 1648*c2e18aaaSAndroid Build Coastguard Worker for label in sorted(set(t.qualified_name() for t in targets)): 1649*c2e18aaaSAndroid Build Coastguard Worker writer.write_line(f'"{label}",') 1650*c2e18aaaSAndroid Build Coastguard Worker 1651*c2e18aaaSAndroid Build Coastguard Worker writer.write(']') 1652*c2e18aaaSAndroid Build Coastguard Worker 1653*c2e18aaaSAndroid Build Coastguard Worker 1654*c2e18aaaSAndroid Build Coastguard Workerdef _decorate_find_method(mod_info, finder_method_func, host, enabled_features): 1655*c2e18aaaSAndroid Build Coastguard Worker """A finder_method decorator to override TestInfo properties.""" 1656*c2e18aaaSAndroid Build Coastguard Worker 1657*c2e18aaaSAndroid Build Coastguard Worker def use_bazel_runner(finder_obj, test_id): 1658*c2e18aaaSAndroid Build Coastguard Worker test_infos = finder_method_func(finder_obj, test_id) 1659*c2e18aaaSAndroid Build Coastguard Worker if not test_infos: 1660*c2e18aaaSAndroid Build Coastguard Worker return test_infos 1661*c2e18aaaSAndroid Build Coastguard Worker for tinfo in test_infos: 1662*c2e18aaaSAndroid Build Coastguard Worker m_info = mod_info.get_module_info(tinfo.test_name) 1663*c2e18aaaSAndroid Build Coastguard Worker 1664*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/262200630): Refactor the duplicated logic in 1665*c2e18aaaSAndroid Build Coastguard Worker # _decorate_find_method() and _add_test_module_targets() to 1666*c2e18aaaSAndroid Build Coastguard Worker # determine whether a test should run with Atest Bazel Mode. 1667*c2e18aaaSAndroid Build Coastguard Worker 1668*c2e18aaaSAndroid Build Coastguard Worker # Only enable modern Robolectric tests since those are the only ones 1669*c2e18aaaSAndroid Build Coastguard Worker # TF currently supports. 1670*c2e18aaaSAndroid Build Coastguard Worker if mod_info.is_modern_robolectric_test(m_info): 1671*c2e18aaaSAndroid Build Coastguard Worker if Features.EXPERIMENTAL_ROBOLECTRIC_TEST in enabled_features: 1672*c2e18aaaSAndroid Build Coastguard Worker tinfo.test_runner = BazelTestRunner.NAME 1673*c2e18aaaSAndroid Build Coastguard Worker continue 1674*c2e18aaaSAndroid Build Coastguard Worker 1675*c2e18aaaSAndroid Build Coastguard Worker # Only run device-driven tests in Bazel mode when '--host' is not 1676*c2e18aaaSAndroid Build Coastguard Worker # specified and the feature is enabled. 1677*c2e18aaaSAndroid Build Coastguard Worker if not host and mod_info.is_device_driven_test(m_info): 1678*c2e18aaaSAndroid Build Coastguard Worker if Features.EXPERIMENTAL_DEVICE_DRIVEN_TEST in enabled_features: 1679*c2e18aaaSAndroid Build Coastguard Worker tinfo.test_runner = BazelTestRunner.NAME 1680*c2e18aaaSAndroid Build Coastguard Worker continue 1681*c2e18aaaSAndroid Build Coastguard Worker 1682*c2e18aaaSAndroid Build Coastguard Worker if mod_info.is_suite_in_compatibility_suites( 1683*c2e18aaaSAndroid Build Coastguard Worker 'host-unit-tests', m_info 1684*c2e18aaaSAndroid Build Coastguard Worker ) or ( 1685*c2e18aaaSAndroid Build Coastguard Worker Features.EXPERIMENTAL_HOST_DRIVEN_TEST in enabled_features 1686*c2e18aaaSAndroid Build Coastguard Worker and mod_info.is_host_driven_test(m_info) 1687*c2e18aaaSAndroid Build Coastguard Worker ): 1688*c2e18aaaSAndroid Build Coastguard Worker tinfo.test_runner = BazelTestRunner.NAME 1689*c2e18aaaSAndroid Build Coastguard Worker return test_infos 1690*c2e18aaaSAndroid Build Coastguard Worker 1691*c2e18aaaSAndroid Build Coastguard Worker return use_bazel_runner 1692*c2e18aaaSAndroid Build Coastguard Worker 1693*c2e18aaaSAndroid Build Coastguard Worker 1694*c2e18aaaSAndroid Build Coastguard Workerdef create_new_finder( 1695*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, 1696*c2e18aaaSAndroid Build Coastguard Worker finder: test_finder_base.TestFinderBase, 1697*c2e18aaaSAndroid Build Coastguard Worker host: bool, 1698*c2e18aaaSAndroid Build Coastguard Worker enabled_features: List[Features] = None, 1699*c2e18aaaSAndroid Build Coastguard Worker): 1700*c2e18aaaSAndroid Build Coastguard Worker """Create new test_finder_base.Finder with decorated find_method. 1701*c2e18aaaSAndroid Build Coastguard Worker 1702*c2e18aaaSAndroid Build Coastguard Worker Args: 1703*c2e18aaaSAndroid Build Coastguard Worker mod_info: ModuleInfo object. 1704*c2e18aaaSAndroid Build Coastguard Worker finder: Test Finder class. 1705*c2e18aaaSAndroid Build Coastguard Worker host: Whether to run the host variant. 1706*c2e18aaaSAndroid Build Coastguard Worker enabled_features: List of enabled features. 1707*c2e18aaaSAndroid Build Coastguard Worker 1708*c2e18aaaSAndroid Build Coastguard Worker Returns: 1709*c2e18aaaSAndroid Build Coastguard Worker List of ordered find methods. 1710*c2e18aaaSAndroid Build Coastguard Worker """ 1711*c2e18aaaSAndroid Build Coastguard Worker return test_finder_base.Finder( 1712*c2e18aaaSAndroid Build Coastguard Worker finder.test_finder_instance, 1713*c2e18aaaSAndroid Build Coastguard Worker _decorate_find_method( 1714*c2e18aaaSAndroid Build Coastguard Worker mod_info, finder.find_method, host, enabled_features or [] 1715*c2e18aaaSAndroid Build Coastguard Worker ), 1716*c2e18aaaSAndroid Build Coastguard Worker finder.finder_info, 1717*c2e18aaaSAndroid Build Coastguard Worker ) 1718*c2e18aaaSAndroid Build Coastguard Worker 1719*c2e18aaaSAndroid Build Coastguard Worker 1720*c2e18aaaSAndroid Build Coastguard Workerclass RunCommandError(subprocess.CalledProcessError): 1721*c2e18aaaSAndroid Build Coastguard Worker """CalledProcessError but including debug information when it fails.""" 1722*c2e18aaaSAndroid Build Coastguard Worker 1723*c2e18aaaSAndroid Build Coastguard Worker def __str__(self): 1724*c2e18aaaSAndroid Build Coastguard Worker return f'{super().__str__()}\nstdout={self.stdout}\n\nstderr={self.stderr}' 1725*c2e18aaaSAndroid Build Coastguard Worker 1726*c2e18aaaSAndroid Build Coastguard Worker 1727*c2e18aaaSAndroid Build Coastguard Workerdef default_run_command(args: List[str], cwd: pathlib.Path) -> str: 1728*c2e18aaaSAndroid Build Coastguard Worker result = subprocess.run( 1729*c2e18aaaSAndroid Build Coastguard Worker args=args, 1730*c2e18aaaSAndroid Build Coastguard Worker cwd=cwd, 1731*c2e18aaaSAndroid Build Coastguard Worker text=True, 1732*c2e18aaaSAndroid Build Coastguard Worker capture_output=True, 1733*c2e18aaaSAndroid Build Coastguard Worker check=False, 1734*c2e18aaaSAndroid Build Coastguard Worker ) 1735*c2e18aaaSAndroid Build Coastguard Worker if result.returncode: 1736*c2e18aaaSAndroid Build Coastguard Worker # Provide a more detailed log message including stdout and stderr. 1737*c2e18aaaSAndroid Build Coastguard Worker raise RunCommandError( 1738*c2e18aaaSAndroid Build Coastguard Worker result.returncode, result.args, result.stdout, result.stderr 1739*c2e18aaaSAndroid Build Coastguard Worker ) 1740*c2e18aaaSAndroid Build Coastguard Worker return result.stdout 1741*c2e18aaaSAndroid Build Coastguard Worker 1742*c2e18aaaSAndroid Build Coastguard Worker 1743*c2e18aaaSAndroid Build Coastguard Worker@dataclasses.dataclass 1744*c2e18aaaSAndroid Build Coastguard Workerclass BuildMetadata: 1745*c2e18aaaSAndroid Build Coastguard Worker build_branch: str 1746*c2e18aaaSAndroid Build Coastguard Worker build_target: str 1747*c2e18aaaSAndroid Build Coastguard Worker 1748*c2e18aaaSAndroid Build Coastguard Worker 1749*c2e18aaaSAndroid Build Coastguard Workerclass BazelTestRunner(trb.TestRunnerBase): 1750*c2e18aaaSAndroid Build Coastguard Worker """Bazel Test Runner class.""" 1751*c2e18aaaSAndroid Build Coastguard Worker 1752*c2e18aaaSAndroid Build Coastguard Worker NAME = 'BazelTestRunner' 1753*c2e18aaaSAndroid Build Coastguard Worker EXECUTABLE = 'none' 1754*c2e18aaaSAndroid Build Coastguard Worker 1755*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=redefined-outer-name 1756*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=too-many-arguments 1757*c2e18aaaSAndroid Build Coastguard Worker def __init__( 1758*c2e18aaaSAndroid Build Coastguard Worker self, 1759*c2e18aaaSAndroid Build Coastguard Worker results_dir, 1760*c2e18aaaSAndroid Build Coastguard Worker mod_info: module_info.ModuleInfo, 1761*c2e18aaaSAndroid Build Coastguard Worker extra_args: Dict[str, Any] = None, 1762*c2e18aaaSAndroid Build Coastguard Worker src_top: pathlib.Path = None, 1763*c2e18aaaSAndroid Build Coastguard Worker workspace_path: pathlib.Path = None, 1764*c2e18aaaSAndroid Build Coastguard Worker run_command: Callable = default_run_command, 1765*c2e18aaaSAndroid Build Coastguard Worker build_metadata: BuildMetadata = None, 1766*c2e18aaaSAndroid Build Coastguard Worker env: Dict[str, str] = None, 1767*c2e18aaaSAndroid Build Coastguard Worker generate_workspace_fn: Callable = generate_bazel_workspace, 1768*c2e18aaaSAndroid Build Coastguard Worker enabled_features: Set[str] = None, 1769*c2e18aaaSAndroid Build Coastguard Worker **kwargs, 1770*c2e18aaaSAndroid Build Coastguard Worker ): 1771*c2e18aaaSAndroid Build Coastguard Worker super().__init__(results_dir, **kwargs) 1772*c2e18aaaSAndroid Build Coastguard Worker self.mod_info = mod_info 1773*c2e18aaaSAndroid Build Coastguard Worker self.src_top = src_top or pathlib.Path( 1774*c2e18aaaSAndroid Build Coastguard Worker os.environ.get(constants.ANDROID_BUILD_TOP) 1775*c2e18aaaSAndroid Build Coastguard Worker ) 1776*c2e18aaaSAndroid Build Coastguard Worker self.starlark_file = _get_resource_root().joinpath( 1777*c2e18aaaSAndroid Build Coastguard Worker 'format_as_soong_module_name.cquery' 1778*c2e18aaaSAndroid Build Coastguard Worker ) 1779*c2e18aaaSAndroid Build Coastguard Worker 1780*c2e18aaaSAndroid Build Coastguard Worker self.bazel_workspace = workspace_path or get_bazel_workspace_dir() 1781*c2e18aaaSAndroid Build Coastguard Worker self.bazel_binary = self.bazel_workspace.joinpath('bazel.sh') 1782*c2e18aaaSAndroid Build Coastguard Worker self.run_command = run_command 1783*c2e18aaaSAndroid Build Coastguard Worker self._extra_args = extra_args or {} 1784*c2e18aaaSAndroid Build Coastguard Worker self.build_metadata = build_metadata or get_default_build_metadata() 1785*c2e18aaaSAndroid Build Coastguard Worker self.env = env or os.environ 1786*c2e18aaaSAndroid Build Coastguard Worker self._generate_workspace_fn = generate_workspace_fn 1787*c2e18aaaSAndroid Build Coastguard Worker self._enabled_features = ( 1788*c2e18aaaSAndroid Build Coastguard Worker enabled_features 1789*c2e18aaaSAndroid Build Coastguard Worker if enabled_features is not None 1790*c2e18aaaSAndroid Build Coastguard Worker else atest_configs.GLOBAL_ARGS.bazel_mode_features 1791*c2e18aaaSAndroid Build Coastguard Worker ) 1792*c2e18aaaSAndroid Build Coastguard Worker 1793*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=unused-argument 1794*c2e18aaaSAndroid Build Coastguard Worker def run_tests(self, test_infos, extra_args, reporter): 1795*c2e18aaaSAndroid Build Coastguard Worker """Run the list of test_infos. 1796*c2e18aaaSAndroid Build Coastguard Worker 1797*c2e18aaaSAndroid Build Coastguard Worker Args: 1798*c2e18aaaSAndroid Build Coastguard Worker test_infos: List of TestInfo. 1799*c2e18aaaSAndroid Build Coastguard Worker extra_args: Dict of extra args to add to test run. 1800*c2e18aaaSAndroid Build Coastguard Worker reporter: An instance of result_report.ResultReporter. 1801*c2e18aaaSAndroid Build Coastguard Worker """ 1802*c2e18aaaSAndroid Build Coastguard Worker ret_code = ExitCode.SUCCESS 1803*c2e18aaaSAndroid Build Coastguard Worker 1804*c2e18aaaSAndroid Build Coastguard Worker try: 1805*c2e18aaaSAndroid Build Coastguard Worker run_cmds = self.generate_run_commands(test_infos, extra_args) 1806*c2e18aaaSAndroid Build Coastguard Worker except AbortRunException as e: 1807*c2e18aaaSAndroid Build Coastguard Worker atest_utils.colorful_print(f'Stop running test(s): {e}', constants.RED) 1808*c2e18aaaSAndroid Build Coastguard Worker return ExitCode.ERROR 1809*c2e18aaaSAndroid Build Coastguard Worker 1810*c2e18aaaSAndroid Build Coastguard Worker for run_cmd in run_cmds: 1811*c2e18aaaSAndroid Build Coastguard Worker subproc = self.run(run_cmd, output_to_stdout=True) 1812*c2e18aaaSAndroid Build Coastguard Worker ret_code |= self.wait_for_subprocess(subproc) 1813*c2e18aaaSAndroid Build Coastguard Worker 1814*c2e18aaaSAndroid Build Coastguard Worker self.organize_test_logs(test_infos) 1815*c2e18aaaSAndroid Build Coastguard Worker 1816*c2e18aaaSAndroid Build Coastguard Worker return ret_code 1817*c2e18aaaSAndroid Build Coastguard Worker 1818*c2e18aaaSAndroid Build Coastguard Worker def organize_test_logs(self, test_infos: List[test_info.TestInfo]): 1819*c2e18aaaSAndroid Build Coastguard Worker for t_info in test_infos: 1820*c2e18aaaSAndroid Build Coastguard Worker test_output_dir, package_name, target_suffix = ( 1821*c2e18aaaSAndroid Build Coastguard Worker self.retrieve_test_output_info(t_info) 1822*c2e18aaaSAndroid Build Coastguard Worker ) 1823*c2e18aaaSAndroid Build Coastguard Worker if test_output_dir.joinpath(TEST_OUTPUT_ZIP_NAME).exists(): 1824*c2e18aaaSAndroid Build Coastguard Worker # TEST_OUTPUT_ZIP file exist when BES uploading is enabled. 1825*c2e18aaaSAndroid Build Coastguard Worker # Showing the BES link to users instead of the local log. 1826*c2e18aaaSAndroid Build Coastguard Worker continue 1827*c2e18aaaSAndroid Build Coastguard Worker 1828*c2e18aaaSAndroid Build Coastguard Worker # AtestExecutionInfo will find all log files in 'results_dir/log' 1829*c2e18aaaSAndroid Build Coastguard Worker # directory and generate an HTML file to display to users when 1830*c2e18aaaSAndroid Build Coastguard Worker # 'results_dir/log' directory exist. 1831*c2e18aaaSAndroid Build Coastguard Worker log_path = pathlib.Path(self.results_dir).joinpath( 1832*c2e18aaaSAndroid Build Coastguard Worker 'log', f'{package_name}', f'{t_info.test_name}_{target_suffix}' 1833*c2e18aaaSAndroid Build Coastguard Worker ) 1834*c2e18aaaSAndroid Build Coastguard Worker log_path.parent.mkdir(parents=True, exist_ok=True) 1835*c2e18aaaSAndroid Build Coastguard Worker if not log_path.is_symlink(): 1836*c2e18aaaSAndroid Build Coastguard Worker log_path.symlink_to(test_output_dir) 1837*c2e18aaaSAndroid Build Coastguard Worker 1838*c2e18aaaSAndroid Build Coastguard Worker def _get_feature_config_or_warn(self, feature, env_var_name): 1839*c2e18aaaSAndroid Build Coastguard Worker feature_config = self.env.get(env_var_name) 1840*c2e18aaaSAndroid Build Coastguard Worker if not feature_config: 1841*c2e18aaaSAndroid Build Coastguard Worker atest_utils.print_and_log_warning( 1842*c2e18aaaSAndroid Build Coastguard Worker 'Ignoring `%s` because the `%s` environment variable is not set.', 1843*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=no-member 1844*c2e18aaaSAndroid Build Coastguard Worker feature, 1845*c2e18aaaSAndroid Build Coastguard Worker env_var_name, 1846*c2e18aaaSAndroid Build Coastguard Worker ) 1847*c2e18aaaSAndroid Build Coastguard Worker return feature_config 1848*c2e18aaaSAndroid Build Coastguard Worker 1849*c2e18aaaSAndroid Build Coastguard Worker def _get_bes_publish_args(self, feature: Features) -> List[str]: 1850*c2e18aaaSAndroid Build Coastguard Worker bes_publish_config = self._get_feature_config_or_warn( 1851*c2e18aaaSAndroid Build Coastguard Worker feature, 'ATEST_BAZEL_BES_PUBLISH_CONFIG' 1852*c2e18aaaSAndroid Build Coastguard Worker ) 1853*c2e18aaaSAndroid Build Coastguard Worker 1854*c2e18aaaSAndroid Build Coastguard Worker if not bes_publish_config: 1855*c2e18aaaSAndroid Build Coastguard Worker return [] 1856*c2e18aaaSAndroid Build Coastguard Worker 1857*c2e18aaaSAndroid Build Coastguard Worker branch = self.build_metadata.build_branch 1858*c2e18aaaSAndroid Build Coastguard Worker target = self.build_metadata.build_target 1859*c2e18aaaSAndroid Build Coastguard Worker 1860*c2e18aaaSAndroid Build Coastguard Worker return [ 1861*c2e18aaaSAndroid Build Coastguard Worker f'--config={bes_publish_config}', 1862*c2e18aaaSAndroid Build Coastguard Worker f'--build_metadata=ab_branch={branch}', 1863*c2e18aaaSAndroid Build Coastguard Worker f'--build_metadata=ab_target={target}', 1864*c2e18aaaSAndroid Build Coastguard Worker ] 1865*c2e18aaaSAndroid Build Coastguard Worker 1866*c2e18aaaSAndroid Build Coastguard Worker def _get_remote_args(self, feature): 1867*c2e18aaaSAndroid Build Coastguard Worker remote_config = self._get_feature_config_or_warn( 1868*c2e18aaaSAndroid Build Coastguard Worker feature, 'ATEST_BAZEL_REMOTE_CONFIG' 1869*c2e18aaaSAndroid Build Coastguard Worker ) 1870*c2e18aaaSAndroid Build Coastguard Worker if not remote_config: 1871*c2e18aaaSAndroid Build Coastguard Worker return [] 1872*c2e18aaaSAndroid Build Coastguard Worker return [f'--config={remote_config}'] 1873*c2e18aaaSAndroid Build Coastguard Worker 1874*c2e18aaaSAndroid Build Coastguard Worker def _get_remote_avd_args(self, feature): 1875*c2e18aaaSAndroid Build Coastguard Worker remote_avd_config = self._get_feature_config_or_warn( 1876*c2e18aaaSAndroid Build Coastguard Worker feature, 'ATEST_BAZEL_REMOTE_AVD_CONFIG' 1877*c2e18aaaSAndroid Build Coastguard Worker ) 1878*c2e18aaaSAndroid Build Coastguard Worker if not remote_avd_config: 1879*c2e18aaaSAndroid Build Coastguard Worker raise ValueError( 1880*c2e18aaaSAndroid Build Coastguard Worker 'Cannot run remote device test because ' 1881*c2e18aaaSAndroid Build Coastguard Worker 'ATEST_BAZEL_REMOTE_AVD_CONFIG ' 1882*c2e18aaaSAndroid Build Coastguard Worker 'environment variable is not set.' 1883*c2e18aaaSAndroid Build Coastguard Worker ) 1884*c2e18aaaSAndroid Build Coastguard Worker return [f'--config={remote_avd_config}'] 1885*c2e18aaaSAndroid Build Coastguard Worker 1886*c2e18aaaSAndroid Build Coastguard Worker def host_env_check(self): 1887*c2e18aaaSAndroid Build Coastguard Worker """Check that host env has everything we need. 1888*c2e18aaaSAndroid Build Coastguard Worker 1889*c2e18aaaSAndroid Build Coastguard Worker We actually can assume the host env is fine because we have the same 1890*c2e18aaaSAndroid Build Coastguard Worker requirements that atest has. Update this to check for android env vars 1891*c2e18aaaSAndroid Build Coastguard Worker if that changes. 1892*c2e18aaaSAndroid Build Coastguard Worker """ 1893*c2e18aaaSAndroid Build Coastguard Worker 1894*c2e18aaaSAndroid Build Coastguard Worker def get_test_runner_build_reqs(self, test_infos) -> Set[str]: 1895*c2e18aaaSAndroid Build Coastguard Worker if not test_infos: 1896*c2e18aaaSAndroid Build Coastguard Worker return set() 1897*c2e18aaaSAndroid Build Coastguard Worker 1898*c2e18aaaSAndroid Build Coastguard Worker self._generate_workspace_fn( 1899*c2e18aaaSAndroid Build Coastguard Worker self.mod_info, 1900*c2e18aaaSAndroid Build Coastguard Worker self._enabled_features, 1901*c2e18aaaSAndroid Build Coastguard Worker ) 1902*c2e18aaaSAndroid Build Coastguard Worker 1903*c2e18aaaSAndroid Build Coastguard Worker deps_expression = ' + '.join( 1904*c2e18aaaSAndroid Build Coastguard Worker sorted(self.test_info_target_label(i) for i in test_infos) 1905*c2e18aaaSAndroid Build Coastguard Worker ) 1906*c2e18aaaSAndroid Build Coastguard Worker 1907*c2e18aaaSAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile() as query_file: 1908*c2e18aaaSAndroid Build Coastguard Worker with open(query_file.name, 'w', encoding='utf-8') as _query_file: 1909*c2e18aaaSAndroid Build Coastguard Worker _query_file.write(f'deps(tests({deps_expression}))') 1910*c2e18aaaSAndroid Build Coastguard Worker 1911*c2e18aaaSAndroid Build Coastguard Worker query_args = [ 1912*c2e18aaaSAndroid Build Coastguard Worker str(self.bazel_binary), 1913*c2e18aaaSAndroid Build Coastguard Worker 'cquery', 1914*c2e18aaaSAndroid Build Coastguard Worker f'--query_file={query_file.name}', 1915*c2e18aaaSAndroid Build Coastguard Worker '--output=starlark', 1916*c2e18aaaSAndroid Build Coastguard Worker f'--starlark:file={self.starlark_file}', 1917*c2e18aaaSAndroid Build Coastguard Worker ] 1918*c2e18aaaSAndroid Build Coastguard Worker 1919*c2e18aaaSAndroid Build Coastguard Worker output = self.run_command(query_args, self.bazel_workspace) 1920*c2e18aaaSAndroid Build Coastguard Worker 1921*c2e18aaaSAndroid Build Coastguard Worker targets = set() 1922*c2e18aaaSAndroid Build Coastguard Worker robolectric_tests = set( 1923*c2e18aaaSAndroid Build Coastguard Worker filter( 1924*c2e18aaaSAndroid Build Coastguard Worker self._is_robolectric_test_suite, 1925*c2e18aaaSAndroid Build Coastguard Worker [test.test_name for test in test_infos], 1926*c2e18aaaSAndroid Build Coastguard Worker ) 1927*c2e18aaaSAndroid Build Coastguard Worker ) 1928*c2e18aaaSAndroid Build Coastguard Worker 1929*c2e18aaaSAndroid Build Coastguard Worker modules_to_variant = _parse_cquery_output(output) 1930*c2e18aaaSAndroid Build Coastguard Worker 1931*c2e18aaaSAndroid Build Coastguard Worker for module, variants in modules_to_variant.items(): 1932*c2e18aaaSAndroid Build Coastguard Worker 1933*c2e18aaaSAndroid Build Coastguard Worker # Skip specifying the build variant for Robolectric test modules 1934*c2e18aaaSAndroid Build Coastguard Worker # since they are special. Soong builds them with the `target` 1935*c2e18aaaSAndroid Build Coastguard Worker # variant although are installed as 'host' modules. 1936*c2e18aaaSAndroid Build Coastguard Worker if module in robolectric_tests: 1937*c2e18aaaSAndroid Build Coastguard Worker targets.add(module) 1938*c2e18aaaSAndroid Build Coastguard Worker continue 1939*c2e18aaaSAndroid Build Coastguard Worker 1940*c2e18aaaSAndroid Build Coastguard Worker targets.add(_soong_target_for_variants(module, variants)) 1941*c2e18aaaSAndroid Build Coastguard Worker 1942*c2e18aaaSAndroid Build Coastguard Worker return targets 1943*c2e18aaaSAndroid Build Coastguard Worker 1944*c2e18aaaSAndroid Build Coastguard Worker def _is_robolectric_test_suite(self, module_name: str) -> bool: 1945*c2e18aaaSAndroid Build Coastguard Worker return self.mod_info.is_robolectric_test_suite( 1946*c2e18aaaSAndroid Build Coastguard Worker self.mod_info.get_module_info(module_name) 1947*c2e18aaaSAndroid Build Coastguard Worker ) 1948*c2e18aaaSAndroid Build Coastguard Worker 1949*c2e18aaaSAndroid Build Coastguard Worker def test_info_target_label(self, test: test_info.TestInfo) -> str: 1950*c2e18aaaSAndroid Build Coastguard Worker module_name = test.test_name 1951*c2e18aaaSAndroid Build Coastguard Worker info = self.mod_info.get_module_info(module_name) 1952*c2e18aaaSAndroid Build Coastguard Worker package_name = info.get(constants.MODULE_PATH)[0] 1953*c2e18aaaSAndroid Build Coastguard Worker target_suffix = self.get_target_suffix(info) 1954*c2e18aaaSAndroid Build Coastguard Worker 1955*c2e18aaaSAndroid Build Coastguard Worker return f'//{package_name}:{module_name}_{target_suffix}' 1956*c2e18aaaSAndroid Build Coastguard Worker 1957*c2e18aaaSAndroid Build Coastguard Worker def retrieve_test_output_info( 1958*c2e18aaaSAndroid Build Coastguard Worker self, test_info: test_info.TestInfo 1959*c2e18aaaSAndroid Build Coastguard Worker ) -> Tuple[pathlib.Path, str, str]: 1960*c2e18aaaSAndroid Build Coastguard Worker """Return test output information. 1961*c2e18aaaSAndroid Build Coastguard Worker 1962*c2e18aaaSAndroid Build Coastguard Worker Args: 1963*c2e18aaaSAndroid Build Coastguard Worker test_info (test_info.TestInfo): Information about the test. 1964*c2e18aaaSAndroid Build Coastguard Worker 1965*c2e18aaaSAndroid Build Coastguard Worker Returns: 1966*c2e18aaaSAndroid Build Coastguard Worker Tuple[pathlib.Path, str, str]: A tuple containing the following 1967*c2e18aaaSAndroid Build Coastguard Worker elements: 1968*c2e18aaaSAndroid Build Coastguard Worker - test_output_dir (pathlib.Path): Absolute path of the test output 1969*c2e18aaaSAndroid Build Coastguard Worker folder. 1970*c2e18aaaSAndroid Build Coastguard Worker - package_name (str): Name of the package. 1971*c2e18aaaSAndroid Build Coastguard Worker - target_suffix (str): Target suffix. 1972*c2e18aaaSAndroid Build Coastguard Worker """ 1973*c2e18aaaSAndroid Build Coastguard Worker module_name = test_info.test_name 1974*c2e18aaaSAndroid Build Coastguard Worker info = self.mod_info.get_module_info(module_name) 1975*c2e18aaaSAndroid Build Coastguard Worker package_name = info.get(constants.MODULE_PATH)[0] 1976*c2e18aaaSAndroid Build Coastguard Worker target_suffix = self.get_target_suffix(info) 1977*c2e18aaaSAndroid Build Coastguard Worker 1978*c2e18aaaSAndroid Build Coastguard Worker test_output_dir = pathlib.Path( 1979*c2e18aaaSAndroid Build Coastguard Worker self.bazel_workspace, 1980*c2e18aaaSAndroid Build Coastguard Worker BAZEL_TEST_LOGS_DIR_NAME, 1981*c2e18aaaSAndroid Build Coastguard Worker package_name, 1982*c2e18aaaSAndroid Build Coastguard Worker f'{module_name}_{target_suffix}', 1983*c2e18aaaSAndroid Build Coastguard Worker TEST_OUTPUT_DIR_NAME, 1984*c2e18aaaSAndroid Build Coastguard Worker ) 1985*c2e18aaaSAndroid Build Coastguard Worker 1986*c2e18aaaSAndroid Build Coastguard Worker return test_output_dir, package_name, target_suffix 1987*c2e18aaaSAndroid Build Coastguard Worker 1988*c2e18aaaSAndroid Build Coastguard Worker def get_target_suffix(self, info: Dict[str, Any]) -> str: 1989*c2e18aaaSAndroid Build Coastguard Worker """Return 'host' or 'device' accordingly to the variant of the test.""" 1990*c2e18aaaSAndroid Build Coastguard Worker if not self._extra_args.get( 1991*c2e18aaaSAndroid Build Coastguard Worker constants.HOST, False 1992*c2e18aaaSAndroid Build Coastguard Worker ) and self.mod_info.is_device_driven_test(info): 1993*c2e18aaaSAndroid Build Coastguard Worker return 'device' 1994*c2e18aaaSAndroid Build Coastguard Worker return 'host' 1995*c2e18aaaSAndroid Build Coastguard Worker 1996*c2e18aaaSAndroid Build Coastguard Worker @staticmethod 1997*c2e18aaaSAndroid Build Coastguard Worker def _get_bazel_feature_args( 1998*c2e18aaaSAndroid Build Coastguard Worker feature: Features, extra_args: Dict[str, Any], generator: Callable 1999*c2e18aaaSAndroid Build Coastguard Worker ) -> List[str]: 2000*c2e18aaaSAndroid Build Coastguard Worker if feature not in extra_args.get('BAZEL_MODE_FEATURES', []): 2001*c2e18aaaSAndroid Build Coastguard Worker return [] 2002*c2e18aaaSAndroid Build Coastguard Worker return generator(feature) 2003*c2e18aaaSAndroid Build Coastguard Worker 2004*c2e18aaaSAndroid Build Coastguard Worker # pylint: disable=unused-argument 2005*c2e18aaaSAndroid Build Coastguard Worker def generate_run_commands(self, test_infos, extra_args, port=None): 2006*c2e18aaaSAndroid Build Coastguard Worker """Generate a list of run commands from TestInfos. 2007*c2e18aaaSAndroid Build Coastguard Worker 2008*c2e18aaaSAndroid Build Coastguard Worker Args: 2009*c2e18aaaSAndroid Build Coastguard Worker test_infos: A set of TestInfo instances. 2010*c2e18aaaSAndroid Build Coastguard Worker extra_args: A Dict of extra args to append. 2011*c2e18aaaSAndroid Build Coastguard Worker port: Optional. An int of the port number to send events to. 2012*c2e18aaaSAndroid Build Coastguard Worker 2013*c2e18aaaSAndroid Build Coastguard Worker Returns: 2014*c2e18aaaSAndroid Build Coastguard Worker A list of run commands to run the tests. 2015*c2e18aaaSAndroid Build Coastguard Worker """ 2016*c2e18aaaSAndroid Build Coastguard Worker startup_options = '' 2017*c2e18aaaSAndroid Build Coastguard Worker bazelrc = self.env.get('ATEST_BAZELRC') 2018*c2e18aaaSAndroid Build Coastguard Worker 2019*c2e18aaaSAndroid Build Coastguard Worker if bazelrc: 2020*c2e18aaaSAndroid Build Coastguard Worker startup_options = f'--bazelrc={bazelrc}' 2021*c2e18aaaSAndroid Build Coastguard Worker 2022*c2e18aaaSAndroid Build Coastguard Worker target_patterns = ' '.join( 2023*c2e18aaaSAndroid Build Coastguard Worker self.test_info_target_label(i) for i in test_infos 2024*c2e18aaaSAndroid Build Coastguard Worker ) 2025*c2e18aaaSAndroid Build Coastguard Worker 2026*c2e18aaaSAndroid Build Coastguard Worker bazel_args = parse_args(test_infos, extra_args) 2027*c2e18aaaSAndroid Build Coastguard Worker 2028*c2e18aaaSAndroid Build Coastguard Worker # If BES is not enabled, use the option of 2029*c2e18aaaSAndroid Build Coastguard Worker # '--nozip_undeclared_test_outputs' to not compress the test outputs. 2030*c2e18aaaSAndroid Build Coastguard Worker # And the URL of test outputs will be printed in terminal. 2031*c2e18aaaSAndroid Build Coastguard Worker bazel_args.extend( 2032*c2e18aaaSAndroid Build Coastguard Worker self._get_bazel_feature_args( 2033*c2e18aaaSAndroid Build Coastguard Worker Features.EXPERIMENTAL_BES_PUBLISH, 2034*c2e18aaaSAndroid Build Coastguard Worker extra_args, 2035*c2e18aaaSAndroid Build Coastguard Worker self._get_bes_publish_args, 2036*c2e18aaaSAndroid Build Coastguard Worker ) 2037*c2e18aaaSAndroid Build Coastguard Worker or ['--nozip_undeclared_test_outputs'] 2038*c2e18aaaSAndroid Build Coastguard Worker ) 2039*c2e18aaaSAndroid Build Coastguard Worker bazel_args.extend( 2040*c2e18aaaSAndroid Build Coastguard Worker self._get_bazel_feature_args( 2041*c2e18aaaSAndroid Build Coastguard Worker Features.EXPERIMENTAL_REMOTE, extra_args, self._get_remote_args 2042*c2e18aaaSAndroid Build Coastguard Worker ) 2043*c2e18aaaSAndroid Build Coastguard Worker ) 2044*c2e18aaaSAndroid Build Coastguard Worker bazel_args.extend( 2045*c2e18aaaSAndroid Build Coastguard Worker self._get_bazel_feature_args( 2046*c2e18aaaSAndroid Build Coastguard Worker Features.EXPERIMENTAL_REMOTE_AVD, 2047*c2e18aaaSAndroid Build Coastguard Worker extra_args, 2048*c2e18aaaSAndroid Build Coastguard Worker self._get_remote_avd_args, 2049*c2e18aaaSAndroid Build Coastguard Worker ) 2050*c2e18aaaSAndroid Build Coastguard Worker ) 2051*c2e18aaaSAndroid Build Coastguard Worker 2052*c2e18aaaSAndroid Build Coastguard Worker # This is an alternative to shlex.join that doesn't exist in Python 2053*c2e18aaaSAndroid Build Coastguard Worker # versions < 3.8. 2054*c2e18aaaSAndroid Build Coastguard Worker bazel_args_str = ' '.join(shlex.quote(arg) for arg in bazel_args) 2055*c2e18aaaSAndroid Build Coastguard Worker 2056*c2e18aaaSAndroid Build Coastguard Worker # Use 'cd' instead of setting the working directory in the subprocess 2057*c2e18aaaSAndroid Build Coastguard Worker # call for a working --dry-run command that users can run. 2058*c2e18aaaSAndroid Build Coastguard Worker return [ 2059*c2e18aaaSAndroid Build Coastguard Worker f'cd {self.bazel_workspace} && ' 2060*c2e18aaaSAndroid Build Coastguard Worker f'{self.bazel_binary} {startup_options} ' 2061*c2e18aaaSAndroid Build Coastguard Worker f'test {target_patterns} {bazel_args_str}' 2062*c2e18aaaSAndroid Build Coastguard Worker ] 2063*c2e18aaaSAndroid Build Coastguard Worker 2064*c2e18aaaSAndroid Build Coastguard Worker 2065*c2e18aaaSAndroid Build Coastguard Workerdef parse_args( 2066*c2e18aaaSAndroid Build Coastguard Worker test_infos: List[test_info.TestInfo], extra_args: Dict[str, Any] 2067*c2e18aaaSAndroid Build Coastguard Worker) -> Dict[str, Any]: 2068*c2e18aaaSAndroid Build Coastguard Worker """Parse commandline args and passes supported args to bazel. 2069*c2e18aaaSAndroid Build Coastguard Worker 2070*c2e18aaaSAndroid Build Coastguard Worker Args: 2071*c2e18aaaSAndroid Build Coastguard Worker test_infos: A set of TestInfo instances. 2072*c2e18aaaSAndroid Build Coastguard Worker extra_args: A Dict of extra args to append. 2073*c2e18aaaSAndroid Build Coastguard Worker 2074*c2e18aaaSAndroid Build Coastguard Worker Returns: 2075*c2e18aaaSAndroid Build Coastguard Worker A list of args to append to the run command. 2076*c2e18aaaSAndroid Build Coastguard Worker """ 2077*c2e18aaaSAndroid Build Coastguard Worker 2078*c2e18aaaSAndroid Build Coastguard Worker args_to_append = [] 2079*c2e18aaaSAndroid Build Coastguard Worker # Make a copy of the `extra_args` dict to avoid modifying it for other 2080*c2e18aaaSAndroid Build Coastguard Worker # Atest runners. 2081*c2e18aaaSAndroid Build Coastguard Worker extra_args_copy = extra_args.copy() 2082*c2e18aaaSAndroid Build Coastguard Worker 2083*c2e18aaaSAndroid Build Coastguard Worker # Remove the `--host` flag since we already pass that in the rule's 2084*c2e18aaaSAndroid Build Coastguard Worker # implementation. 2085*c2e18aaaSAndroid Build Coastguard Worker extra_args_copy.pop(constants.HOST, None) 2086*c2e18aaaSAndroid Build Coastguard Worker 2087*c2e18aaaSAndroid Build Coastguard Worker # Remove the serial arg since Bazel mode does not support device tests and 2088*c2e18aaaSAndroid Build Coastguard Worker # the serial / -s arg conflicts with the TF null device option specified in 2089*c2e18aaaSAndroid Build Coastguard Worker # the rule implementation (-n). 2090*c2e18aaaSAndroid Build Coastguard Worker extra_args_copy.pop(constants.SERIAL, None) 2091*c2e18aaaSAndroid Build Coastguard Worker 2092*c2e18aaaSAndroid Build Coastguard Worker # Map args to their native Bazel counterparts. 2093*c2e18aaaSAndroid Build Coastguard Worker for arg in _SUPPORTED_BAZEL_ARGS: 2094*c2e18aaaSAndroid Build Coastguard Worker if arg not in extra_args_copy: 2095*c2e18aaaSAndroid Build Coastguard Worker continue 2096*c2e18aaaSAndroid Build Coastguard Worker args_to_append.extend(_map_to_bazel_args(arg, extra_args_copy[arg])) 2097*c2e18aaaSAndroid Build Coastguard Worker # Remove the argument since we already mapped it to a Bazel option 2098*c2e18aaaSAndroid Build Coastguard Worker # and no longer need it mapped to a Tradefed argument below. 2099*c2e18aaaSAndroid Build Coastguard Worker del extra_args_copy[arg] 2100*c2e18aaaSAndroid Build Coastguard Worker 2101*c2e18aaaSAndroid Build Coastguard Worker # TODO(b/215461642): Store the extra_args in the top-level object so 2102*c2e18aaaSAndroid Build Coastguard Worker # that we don't have to re-parse the extra args to get BAZEL_ARG again. 2103*c2e18aaaSAndroid Build Coastguard Worker tf_args, _ = tfr.extra_args_to_tf_args(extra_args_copy) 2104*c2e18aaaSAndroid Build Coastguard Worker 2105*c2e18aaaSAndroid Build Coastguard Worker # Add ATest include filter argument to allow testcase filtering. 2106*c2e18aaaSAndroid Build Coastguard Worker tf_args.extend(tfr.get_include_filter(test_infos)) 2107*c2e18aaaSAndroid Build Coastguard Worker 2108*c2e18aaaSAndroid Build Coastguard Worker args_to_append.extend([f'--test_arg={i}' for i in tf_args]) 2109*c2e18aaaSAndroid Build Coastguard Worker 2110*c2e18aaaSAndroid Build Coastguard Worker # Disable test result caching when wait-for-debugger flag is set. 2111*c2e18aaaSAndroid Build Coastguard Worker if '--wait-for-debugger' in tf_args: 2112*c2e18aaaSAndroid Build Coastguard Worker # Remove the --cache_test_results flag if it's already set. 2113*c2e18aaaSAndroid Build Coastguard Worker args_to_append = [ 2114*c2e18aaaSAndroid Build Coastguard Worker arg 2115*c2e18aaaSAndroid Build Coastguard Worker for arg in args_to_append 2116*c2e18aaaSAndroid Build Coastguard Worker if not arg.startswith('--cache_test_results') 2117*c2e18aaaSAndroid Build Coastguard Worker ] 2118*c2e18aaaSAndroid Build Coastguard Worker args_to_append.append('--cache_test_results=no') 2119*c2e18aaaSAndroid Build Coastguard Worker 2120*c2e18aaaSAndroid Build Coastguard Worker # Default to --test_output=errors unless specified otherwise 2121*c2e18aaaSAndroid Build Coastguard Worker if not any(arg.startswith('--test_output=') for arg in args_to_append): 2122*c2e18aaaSAndroid Build Coastguard Worker args_to_append.append('--test_output=errors') 2123*c2e18aaaSAndroid Build Coastguard Worker 2124*c2e18aaaSAndroid Build Coastguard Worker # Default to --test_summary=detailed unless specified otherwise, or if the 2125*c2e18aaaSAndroid Build Coastguard Worker # feature is disabled 2126*c2e18aaaSAndroid Build Coastguard Worker if not any(arg.startswith('--test_summary=') for arg in args_to_append) and ( 2127*c2e18aaaSAndroid Build Coastguard Worker Features.NO_BAZEL_DETAILED_SUMMARY 2128*c2e18aaaSAndroid Build Coastguard Worker not in extra_args.get('BAZEL_MODE_FEATURES', []) 2129*c2e18aaaSAndroid Build Coastguard Worker ): 2130*c2e18aaaSAndroid Build Coastguard Worker args_to_append.append('--test_summary=detailed') 2131*c2e18aaaSAndroid Build Coastguard Worker 2132*c2e18aaaSAndroid Build Coastguard Worker return args_to_append 2133*c2e18aaaSAndroid Build Coastguard Worker 2134*c2e18aaaSAndroid Build Coastguard Worker 2135*c2e18aaaSAndroid Build Coastguard Workerdef _map_to_bazel_args(arg: str, arg_value: Any) -> List[str]: 2136*c2e18aaaSAndroid Build Coastguard Worker return ( 2137*c2e18aaaSAndroid Build Coastguard Worker _SUPPORTED_BAZEL_ARGS[arg](arg_value) 2138*c2e18aaaSAndroid Build Coastguard Worker if arg in _SUPPORTED_BAZEL_ARGS 2139*c2e18aaaSAndroid Build Coastguard Worker else [] 2140*c2e18aaaSAndroid Build Coastguard Worker ) 2141*c2e18aaaSAndroid Build Coastguard Worker 2142*c2e18aaaSAndroid Build Coastguard Worker 2143*c2e18aaaSAndroid Build Coastguard Workerdef _parse_cquery_output(output: str) -> Dict[str, Set[str]]: 2144*c2e18aaaSAndroid Build Coastguard Worker module_to_build_variants = defaultdict(set) 2145*c2e18aaaSAndroid Build Coastguard Worker 2146*c2e18aaaSAndroid Build Coastguard Worker for line in filter(bool, map(str.strip, output.splitlines())): 2147*c2e18aaaSAndroid Build Coastguard Worker module_name, build_variant = line.split(':') 2148*c2e18aaaSAndroid Build Coastguard Worker module_to_build_variants[module_name].add(build_variant) 2149*c2e18aaaSAndroid Build Coastguard Worker 2150*c2e18aaaSAndroid Build Coastguard Worker return module_to_build_variants 2151*c2e18aaaSAndroid Build Coastguard Worker 2152*c2e18aaaSAndroid Build Coastguard Worker 2153*c2e18aaaSAndroid Build Coastguard Workerdef _soong_target_for_variants( 2154*c2e18aaaSAndroid Build Coastguard Worker module_name: str, build_variants: Set[str] 2155*c2e18aaaSAndroid Build Coastguard Worker) -> str: 2156*c2e18aaaSAndroid Build Coastguard Worker 2157*c2e18aaaSAndroid Build Coastguard Worker if not build_variants: 2158*c2e18aaaSAndroid Build Coastguard Worker raise ValueError( 2159*c2e18aaaSAndroid Build Coastguard Worker f'Missing the build variants for module {module_name} in cquery output!' 2160*c2e18aaaSAndroid Build Coastguard Worker ) 2161*c2e18aaaSAndroid Build Coastguard Worker 2162*c2e18aaaSAndroid Build Coastguard Worker if len(build_variants) > 1: 2163*c2e18aaaSAndroid Build Coastguard Worker return module_name 2164*c2e18aaaSAndroid Build Coastguard Worker 2165*c2e18aaaSAndroid Build Coastguard Worker return f'{module_name}-{_CONFIG_TO_VARIANT[list(build_variants)[0]]}' 2166