xref: /aosp_15_r20/build/make/ci/optimized_targets.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker#
2*9e94795aSAndroid Build Coastguard Worker# Copyright 2024, The Android Open Source Project
3*9e94795aSAndroid Build Coastguard Worker#
4*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*9e94795aSAndroid Build Coastguard Worker#
8*9e94795aSAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
9*9e94795aSAndroid Build Coastguard Worker#
10*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
15*9e94795aSAndroid Build Coastguard Worker
16*9e94795aSAndroid Build Coastguard Workerfrom abc import ABC
17*9e94795aSAndroid Build Coastguard Workerimport argparse
18*9e94795aSAndroid Build Coastguard Workerimport functools
19*9e94795aSAndroid Build Coastguard Workerimport json
20*9e94795aSAndroid Build Coastguard Workerimport logging
21*9e94795aSAndroid Build Coastguard Workerimport os
22*9e94795aSAndroid Build Coastguard Workerimport pathlib
23*9e94795aSAndroid Build Coastguard Workerimport subprocess
24*9e94795aSAndroid Build Coastguard Worker
25*9e94795aSAndroid Build Coastguard Workerfrom build_context import BuildContext
26*9e94795aSAndroid Build Coastguard Workerimport test_mapping_module_retriever
27*9e94795aSAndroid Build Coastguard Worker
28*9e94795aSAndroid Build Coastguard Worker
29*9e94795aSAndroid Build Coastguard Workerclass OptimizedBuildTarget(ABC):
30*9e94795aSAndroid Build Coastguard Worker  """A representation of an optimized build target.
31*9e94795aSAndroid Build Coastguard Worker
32*9e94795aSAndroid Build Coastguard Worker  This class will determine what targets to build given a given build_cotext and
33*9e94795aSAndroid Build Coastguard Worker  will have a packaging function to generate any necessary output zips for the
34*9e94795aSAndroid Build Coastguard Worker  build.
35*9e94795aSAndroid Build Coastguard Worker  """
36*9e94795aSAndroid Build Coastguard Worker
37*9e94795aSAndroid Build Coastguard Worker  _SOONG_UI_BASH_PATH = 'build/soong/soong_ui.bash'
38*9e94795aSAndroid Build Coastguard Worker  _PREBUILT_SOONG_ZIP_PATH = 'prebuilts/build-tools/linux-x86/bin/soong_zip'
39*9e94795aSAndroid Build Coastguard Worker
40*9e94795aSAndroid Build Coastguard Worker  def __init__(
41*9e94795aSAndroid Build Coastguard Worker      self,
42*9e94795aSAndroid Build Coastguard Worker      target: str,
43*9e94795aSAndroid Build Coastguard Worker      build_context: BuildContext,
44*9e94795aSAndroid Build Coastguard Worker      args: argparse.Namespace,
45*9e94795aSAndroid Build Coastguard Worker  ):
46*9e94795aSAndroid Build Coastguard Worker    self.target = target
47*9e94795aSAndroid Build Coastguard Worker    self.build_context = build_context
48*9e94795aSAndroid Build Coastguard Worker    self.args = args
49*9e94795aSAndroid Build Coastguard Worker
50*9e94795aSAndroid Build Coastguard Worker  def get_build_targets(self) -> set[str]:
51*9e94795aSAndroid Build Coastguard Worker    features = self.build_context.enabled_build_features
52*9e94795aSAndroid Build Coastguard Worker    if self.get_enabled_flag() in features:
53*9e94795aSAndroid Build Coastguard Worker      self.modules_to_build = self.get_build_targets_impl()
54*9e94795aSAndroid Build Coastguard Worker      return self.modules_to_build
55*9e94795aSAndroid Build Coastguard Worker
56*9e94795aSAndroid Build Coastguard Worker    self.modules_to_build = {self.target}
57*9e94795aSAndroid Build Coastguard Worker    return {self.target}
58*9e94795aSAndroid Build Coastguard Worker
59*9e94795aSAndroid Build Coastguard Worker  def get_package_outputs_commands(self) -> list[list[str]]:
60*9e94795aSAndroid Build Coastguard Worker    features = self.build_context.enabled_build_features
61*9e94795aSAndroid Build Coastguard Worker    if self.get_enabled_flag() in features:
62*9e94795aSAndroid Build Coastguard Worker      return self.get_package_outputs_commands_impl()
63*9e94795aSAndroid Build Coastguard Worker
64*9e94795aSAndroid Build Coastguard Worker    return []
65*9e94795aSAndroid Build Coastguard Worker
66*9e94795aSAndroid Build Coastguard Worker  def get_package_outputs_commands_impl(self) -> list[list[str]]:
67*9e94795aSAndroid Build Coastguard Worker    raise NotImplementedError(
68*9e94795aSAndroid Build Coastguard Worker        'get_package_outputs_commands_impl not implemented in'
69*9e94795aSAndroid Build Coastguard Worker        f' {type(self).__name__}'
70*9e94795aSAndroid Build Coastguard Worker    )
71*9e94795aSAndroid Build Coastguard Worker
72*9e94795aSAndroid Build Coastguard Worker  def get_enabled_flag(self):
73*9e94795aSAndroid Build Coastguard Worker    raise NotImplementedError(
74*9e94795aSAndroid Build Coastguard Worker        f'get_enabled_flag not implemented in {type(self).__name__}'
75*9e94795aSAndroid Build Coastguard Worker    )
76*9e94795aSAndroid Build Coastguard Worker
77*9e94795aSAndroid Build Coastguard Worker  def get_build_targets_impl(self) -> set[str]:
78*9e94795aSAndroid Build Coastguard Worker    raise NotImplementedError(
79*9e94795aSAndroid Build Coastguard Worker        f'get_build_targets_impl not implemented in {type(self).__name__}'
80*9e94795aSAndroid Build Coastguard Worker    )
81*9e94795aSAndroid Build Coastguard Worker
82*9e94795aSAndroid Build Coastguard Worker  def _generate_zip_options_for_items(
83*9e94795aSAndroid Build Coastguard Worker      self,
84*9e94795aSAndroid Build Coastguard Worker      prefix: str = '',
85*9e94795aSAndroid Build Coastguard Worker      relative_root: str = '',
86*9e94795aSAndroid Build Coastguard Worker      list_files: list[str] | None = None,
87*9e94795aSAndroid Build Coastguard Worker      files: list[str] | None = None,
88*9e94795aSAndroid Build Coastguard Worker      directories: list[str] | None = None,
89*9e94795aSAndroid Build Coastguard Worker  ) -> list[str]:
90*9e94795aSAndroid Build Coastguard Worker    if not list_files and not files and not directories:
91*9e94795aSAndroid Build Coastguard Worker      raise RuntimeError(
92*9e94795aSAndroid Build Coastguard Worker          f'No items specified to be added to zip! Prefix: {prefix}, Relative'
93*9e94795aSAndroid Build Coastguard Worker          f' root: {relative_root}'
94*9e94795aSAndroid Build Coastguard Worker      )
95*9e94795aSAndroid Build Coastguard Worker    command_segment = []
96*9e94795aSAndroid Build Coastguard Worker    # These are all soong_zip options so consult soong_zip --help for specifics.
97*9e94795aSAndroid Build Coastguard Worker    if prefix:
98*9e94795aSAndroid Build Coastguard Worker      command_segment.append('-P')
99*9e94795aSAndroid Build Coastguard Worker      command_segment.append(prefix)
100*9e94795aSAndroid Build Coastguard Worker    if relative_root:
101*9e94795aSAndroid Build Coastguard Worker      command_segment.append('-C')
102*9e94795aSAndroid Build Coastguard Worker      command_segment.append(relative_root)
103*9e94795aSAndroid Build Coastguard Worker    if list_files:
104*9e94795aSAndroid Build Coastguard Worker      for list_file in list_files:
105*9e94795aSAndroid Build Coastguard Worker        command_segment.append('-l')
106*9e94795aSAndroid Build Coastguard Worker        command_segment.append(list_file)
107*9e94795aSAndroid Build Coastguard Worker    if files:
108*9e94795aSAndroid Build Coastguard Worker      for file in files:
109*9e94795aSAndroid Build Coastguard Worker        command_segment.append('-f')
110*9e94795aSAndroid Build Coastguard Worker        command_segment.append(file)
111*9e94795aSAndroid Build Coastguard Worker    if directories:
112*9e94795aSAndroid Build Coastguard Worker      for directory in directories:
113*9e94795aSAndroid Build Coastguard Worker        command_segment.append('-D')
114*9e94795aSAndroid Build Coastguard Worker        command_segment.append(directory)
115*9e94795aSAndroid Build Coastguard Worker
116*9e94795aSAndroid Build Coastguard Worker    return command_segment
117*9e94795aSAndroid Build Coastguard Worker
118*9e94795aSAndroid Build Coastguard Worker  def _query_soong_vars(
119*9e94795aSAndroid Build Coastguard Worker      self, src_top: pathlib.Path, soong_vars: list[str]
120*9e94795aSAndroid Build Coastguard Worker  ) -> dict[str, str]:
121*9e94795aSAndroid Build Coastguard Worker    process_result = subprocess.run(
122*9e94795aSAndroid Build Coastguard Worker        args=[
123*9e94795aSAndroid Build Coastguard Worker            f'{src_top / self._SOONG_UI_BASH_PATH}',
124*9e94795aSAndroid Build Coastguard Worker            '--dumpvars-mode',
125*9e94795aSAndroid Build Coastguard Worker            f'--abs-vars={" ".join(soong_vars)}',
126*9e94795aSAndroid Build Coastguard Worker        ],
127*9e94795aSAndroid Build Coastguard Worker        env=os.environ,
128*9e94795aSAndroid Build Coastguard Worker        check=False,
129*9e94795aSAndroid Build Coastguard Worker        capture_output=True,
130*9e94795aSAndroid Build Coastguard Worker        text=True,
131*9e94795aSAndroid Build Coastguard Worker    )
132*9e94795aSAndroid Build Coastguard Worker    if not process_result.returncode == 0:
133*9e94795aSAndroid Build Coastguard Worker      logging.error('soong dumpvars command failed! stderr:')
134*9e94795aSAndroid Build Coastguard Worker      logging.error(process_result.stderr)
135*9e94795aSAndroid Build Coastguard Worker      raise RuntimeError('Soong dumpvars failed! See log for stderr.')
136*9e94795aSAndroid Build Coastguard Worker
137*9e94795aSAndroid Build Coastguard Worker    if not process_result.stdout:
138*9e94795aSAndroid Build Coastguard Worker      raise RuntimeError(
139*9e94795aSAndroid Build Coastguard Worker          'Necessary soong variables ' + soong_vars + ' not found.'
140*9e94795aSAndroid Build Coastguard Worker      )
141*9e94795aSAndroid Build Coastguard Worker
142*9e94795aSAndroid Build Coastguard Worker    try:
143*9e94795aSAndroid Build Coastguard Worker      return {
144*9e94795aSAndroid Build Coastguard Worker          line.split('=')[0]: line.split('=')[1].strip("'")
145*9e94795aSAndroid Build Coastguard Worker          for line in process_result.stdout.strip().split('\n')
146*9e94795aSAndroid Build Coastguard Worker      }
147*9e94795aSAndroid Build Coastguard Worker    except IndexError as e:
148*9e94795aSAndroid Build Coastguard Worker      raise RuntimeError(
149*9e94795aSAndroid Build Coastguard Worker          'Error parsing soong dumpvars output! See output here:'
150*9e94795aSAndroid Build Coastguard Worker          f' {process_result.stdout}',
151*9e94795aSAndroid Build Coastguard Worker          e,
152*9e94795aSAndroid Build Coastguard Worker      )
153*9e94795aSAndroid Build Coastguard Worker
154*9e94795aSAndroid Build Coastguard Worker  def _base_zip_command(
155*9e94795aSAndroid Build Coastguard Worker      self, src_top: pathlib.Path, dist_dir: pathlib.Path, name: str
156*9e94795aSAndroid Build Coastguard Worker  ) -> list[str]:
157*9e94795aSAndroid Build Coastguard Worker    return [
158*9e94795aSAndroid Build Coastguard Worker        f'{src_top / self._PREBUILT_SOONG_ZIP_PATH }',
159*9e94795aSAndroid Build Coastguard Worker        '-d',
160*9e94795aSAndroid Build Coastguard Worker        '-o',
161*9e94795aSAndroid Build Coastguard Worker        f'{dist_dir / name}',
162*9e94795aSAndroid Build Coastguard Worker    ]
163*9e94795aSAndroid Build Coastguard Worker
164*9e94795aSAndroid Build Coastguard Worker
165*9e94795aSAndroid Build Coastguard Workerclass NullOptimizer(OptimizedBuildTarget):
166*9e94795aSAndroid Build Coastguard Worker  """No-op target optimizer.
167*9e94795aSAndroid Build Coastguard Worker
168*9e94795aSAndroid Build Coastguard Worker  This will simply build the same target it was given and do nothing for the
169*9e94795aSAndroid Build Coastguard Worker  packaging step.
170*9e94795aSAndroid Build Coastguard Worker  """
171*9e94795aSAndroid Build Coastguard Worker
172*9e94795aSAndroid Build Coastguard Worker  def __init__(self, target):
173*9e94795aSAndroid Build Coastguard Worker    self.target = target
174*9e94795aSAndroid Build Coastguard Worker
175*9e94795aSAndroid Build Coastguard Worker  def get_build_targets(self):
176*9e94795aSAndroid Build Coastguard Worker    return {self.target}
177*9e94795aSAndroid Build Coastguard Worker
178*9e94795aSAndroid Build Coastguard Worker  def get_package_outputs_commands(self):
179*9e94795aSAndroid Build Coastguard Worker    return []
180*9e94795aSAndroid Build Coastguard Worker
181*9e94795aSAndroid Build Coastguard Worker
182*9e94795aSAndroid Build Coastguard Workerclass ChangeInfo:
183*9e94795aSAndroid Build Coastguard Worker
184*9e94795aSAndroid Build Coastguard Worker  def __init__(self, change_info_file_path):
185*9e94795aSAndroid Build Coastguard Worker    try:
186*9e94795aSAndroid Build Coastguard Worker      with open(change_info_file_path) as change_info_file:
187*9e94795aSAndroid Build Coastguard Worker        change_info_contents = json.load(change_info_file)
188*9e94795aSAndroid Build Coastguard Worker    except json.decoder.JSONDecodeError:
189*9e94795aSAndroid Build Coastguard Worker      logging.error(f'Failed to load CHANGE_INFO: {change_info_file_path}')
190*9e94795aSAndroid Build Coastguard Worker      raise
191*9e94795aSAndroid Build Coastguard Worker
192*9e94795aSAndroid Build Coastguard Worker    self._change_info_contents = change_info_contents
193*9e94795aSAndroid Build Coastguard Worker
194*9e94795aSAndroid Build Coastguard Worker  def find_changed_files(self) -> set[str]:
195*9e94795aSAndroid Build Coastguard Worker    changed_files = set()
196*9e94795aSAndroid Build Coastguard Worker
197*9e94795aSAndroid Build Coastguard Worker    for change in self._change_info_contents['changes']:
198*9e94795aSAndroid Build Coastguard Worker      project_path = change.get('projectPath') + '/'
199*9e94795aSAndroid Build Coastguard Worker
200*9e94795aSAndroid Build Coastguard Worker      for revision in change.get('revisions'):
201*9e94795aSAndroid Build Coastguard Worker        for file_info in revision.get('fileInfos'):
202*9e94795aSAndroid Build Coastguard Worker          changed_files.add(project_path + file_info.get('path'))
203*9e94795aSAndroid Build Coastguard Worker
204*9e94795aSAndroid Build Coastguard Worker    return changed_files
205*9e94795aSAndroid Build Coastguard Worker
206*9e94795aSAndroid Build Coastguard Worker
207*9e94795aSAndroid Build Coastguard Workerclass GeneralTestsOptimizer(OptimizedBuildTarget):
208*9e94795aSAndroid Build Coastguard Worker  """general-tests optimizer
209*9e94795aSAndroid Build Coastguard Worker
210*9e94795aSAndroid Build Coastguard Worker  This optimizer reads in the list of changed files from the file located in
211*9e94795aSAndroid Build Coastguard Worker  env[CHANGE_INFO] and uses this list alongside the normal TEST MAPPING logic to
212*9e94795aSAndroid Build Coastguard Worker  determine what test mapping modules will run for the given changes. It then
213*9e94795aSAndroid Build Coastguard Worker  builds those modules and packages them in the same way general-tests.zip is
214*9e94795aSAndroid Build Coastguard Worker  normally built.
215*9e94795aSAndroid Build Coastguard Worker  """
216*9e94795aSAndroid Build Coastguard Worker
217*9e94795aSAndroid Build Coastguard Worker  # List of modules that are built alongside general-tests as dependencies.
218*9e94795aSAndroid Build Coastguard Worker  _REQUIRED_MODULES = frozenset([
219*9e94795aSAndroid Build Coastguard Worker      'cts-tradefed',
220*9e94795aSAndroid Build Coastguard Worker      'vts-tradefed',
221*9e94795aSAndroid Build Coastguard Worker      'compatibility-host-util',
222*9e94795aSAndroid Build Coastguard Worker      'general-tests-shared-libs',
223*9e94795aSAndroid Build Coastguard Worker  ])
224*9e94795aSAndroid Build Coastguard Worker
225*9e94795aSAndroid Build Coastguard Worker  def get_build_targets_impl(self) -> set[str]:
226*9e94795aSAndroid Build Coastguard Worker    change_info_file_path = os.environ.get('CHANGE_INFO')
227*9e94795aSAndroid Build Coastguard Worker    if not change_info_file_path:
228*9e94795aSAndroid Build Coastguard Worker      logging.info(
229*9e94795aSAndroid Build Coastguard Worker          'No CHANGE_INFO env var found, general-tests optimization disabled.'
230*9e94795aSAndroid Build Coastguard Worker      )
231*9e94795aSAndroid Build Coastguard Worker      return {'general-tests'}
232*9e94795aSAndroid Build Coastguard Worker
233*9e94795aSAndroid Build Coastguard Worker    test_infos = self.build_context.test_infos
234*9e94795aSAndroid Build Coastguard Worker    test_mapping_test_groups = set()
235*9e94795aSAndroid Build Coastguard Worker    for test_info in test_infos:
236*9e94795aSAndroid Build Coastguard Worker      is_test_mapping = test_info.is_test_mapping
237*9e94795aSAndroid Build Coastguard Worker      current_test_mapping_test_groups = test_info.test_mapping_test_groups
238*9e94795aSAndroid Build Coastguard Worker      uses_general_tests = test_info.build_target_used('general-tests')
239*9e94795aSAndroid Build Coastguard Worker
240*9e94795aSAndroid Build Coastguard Worker      if uses_general_tests and not is_test_mapping:
241*9e94795aSAndroid Build Coastguard Worker        logging.info(
242*9e94795aSAndroid Build Coastguard Worker            'Test uses general-tests.zip but is not test-mapping, general-tests'
243*9e94795aSAndroid Build Coastguard Worker            ' optimization disabled.'
244*9e94795aSAndroid Build Coastguard Worker        )
245*9e94795aSAndroid Build Coastguard Worker        return {'general-tests'}
246*9e94795aSAndroid Build Coastguard Worker
247*9e94795aSAndroid Build Coastguard Worker      if is_test_mapping:
248*9e94795aSAndroid Build Coastguard Worker        test_mapping_test_groups.update(current_test_mapping_test_groups)
249*9e94795aSAndroid Build Coastguard Worker
250*9e94795aSAndroid Build Coastguard Worker    change_info = ChangeInfo(change_info_file_path)
251*9e94795aSAndroid Build Coastguard Worker    changed_files = change_info.find_changed_files()
252*9e94795aSAndroid Build Coastguard Worker
253*9e94795aSAndroid Build Coastguard Worker    test_mappings = test_mapping_module_retriever.GetTestMappings(
254*9e94795aSAndroid Build Coastguard Worker        changed_files, set()
255*9e94795aSAndroid Build Coastguard Worker    )
256*9e94795aSAndroid Build Coastguard Worker
257*9e94795aSAndroid Build Coastguard Worker    modules_to_build = set(self._REQUIRED_MODULES)
258*9e94795aSAndroid Build Coastguard Worker
259*9e94795aSAndroid Build Coastguard Worker    modules_to_build.update(
260*9e94795aSAndroid Build Coastguard Worker        test_mapping_module_retriever.FindAffectedModules(
261*9e94795aSAndroid Build Coastguard Worker            test_mappings, changed_files, test_mapping_test_groups
262*9e94795aSAndroid Build Coastguard Worker        )
263*9e94795aSAndroid Build Coastguard Worker    )
264*9e94795aSAndroid Build Coastguard Worker
265*9e94795aSAndroid Build Coastguard Worker    return modules_to_build
266*9e94795aSAndroid Build Coastguard Worker
267*9e94795aSAndroid Build Coastguard Worker  def get_package_outputs_commands_impl(self):
268*9e94795aSAndroid Build Coastguard Worker    src_top = pathlib.Path(os.environ.get('TOP', os.getcwd()))
269*9e94795aSAndroid Build Coastguard Worker    dist_dir = pathlib.Path(os.environ.get('DIST_DIR'))
270*9e94795aSAndroid Build Coastguard Worker
271*9e94795aSAndroid Build Coastguard Worker    soong_vars = self._query_soong_vars(
272*9e94795aSAndroid Build Coastguard Worker        src_top,
273*9e94795aSAndroid Build Coastguard Worker        [
274*9e94795aSAndroid Build Coastguard Worker            'HOST_OUT_TESTCASES',
275*9e94795aSAndroid Build Coastguard Worker            'TARGET_OUT_TESTCASES',
276*9e94795aSAndroid Build Coastguard Worker            'PRODUCT_OUT',
277*9e94795aSAndroid Build Coastguard Worker            'SOONG_HOST_OUT',
278*9e94795aSAndroid Build Coastguard Worker            'HOST_OUT',
279*9e94795aSAndroid Build Coastguard Worker        ],
280*9e94795aSAndroid Build Coastguard Worker    )
281*9e94795aSAndroid Build Coastguard Worker    host_out_testcases = pathlib.Path(soong_vars.get('HOST_OUT_TESTCASES'))
282*9e94795aSAndroid Build Coastguard Worker    target_out_testcases = pathlib.Path(soong_vars.get('TARGET_OUT_TESTCASES'))
283*9e94795aSAndroid Build Coastguard Worker    product_out = pathlib.Path(soong_vars.get('PRODUCT_OUT'))
284*9e94795aSAndroid Build Coastguard Worker    soong_host_out = pathlib.Path(soong_vars.get('SOONG_HOST_OUT'))
285*9e94795aSAndroid Build Coastguard Worker    host_out = pathlib.Path(soong_vars.get('HOST_OUT'))
286*9e94795aSAndroid Build Coastguard Worker
287*9e94795aSAndroid Build Coastguard Worker    host_paths = []
288*9e94795aSAndroid Build Coastguard Worker    target_paths = []
289*9e94795aSAndroid Build Coastguard Worker    host_config_files = []
290*9e94795aSAndroid Build Coastguard Worker    target_config_files = []
291*9e94795aSAndroid Build Coastguard Worker    for module in self.modules_to_build:
292*9e94795aSAndroid Build Coastguard Worker      # The required modules are handled separately, no need to package.
293*9e94795aSAndroid Build Coastguard Worker      if module in self._REQUIRED_MODULES:
294*9e94795aSAndroid Build Coastguard Worker        continue
295*9e94795aSAndroid Build Coastguard Worker
296*9e94795aSAndroid Build Coastguard Worker      host_path = host_out_testcases / module
297*9e94795aSAndroid Build Coastguard Worker      if os.path.exists(host_path):
298*9e94795aSAndroid Build Coastguard Worker        host_paths.append(host_path)
299*9e94795aSAndroid Build Coastguard Worker        self._collect_config_files(src_top, host_path, host_config_files)
300*9e94795aSAndroid Build Coastguard Worker
301*9e94795aSAndroid Build Coastguard Worker      target_path = target_out_testcases / module
302*9e94795aSAndroid Build Coastguard Worker      if os.path.exists(target_path):
303*9e94795aSAndroid Build Coastguard Worker        target_paths.append(target_path)
304*9e94795aSAndroid Build Coastguard Worker        self._collect_config_files(src_top, target_path, target_config_files)
305*9e94795aSAndroid Build Coastguard Worker
306*9e94795aSAndroid Build Coastguard Worker      if not os.path.exists(host_path) and not os.path.exists(target_path):
307*9e94795aSAndroid Build Coastguard Worker        logging.info(f'No host or target build outputs found for {module}.')
308*9e94795aSAndroid Build Coastguard Worker
309*9e94795aSAndroid Build Coastguard Worker    zip_commands = []
310*9e94795aSAndroid Build Coastguard Worker
311*9e94795aSAndroid Build Coastguard Worker    zip_commands.extend(
312*9e94795aSAndroid Build Coastguard Worker        self._get_zip_test_configs_zips_commands(
313*9e94795aSAndroid Build Coastguard Worker            src_top,
314*9e94795aSAndroid Build Coastguard Worker            dist_dir,
315*9e94795aSAndroid Build Coastguard Worker            host_out,
316*9e94795aSAndroid Build Coastguard Worker            product_out,
317*9e94795aSAndroid Build Coastguard Worker            host_config_files,
318*9e94795aSAndroid Build Coastguard Worker            target_config_files,
319*9e94795aSAndroid Build Coastguard Worker        )
320*9e94795aSAndroid Build Coastguard Worker    )
321*9e94795aSAndroid Build Coastguard Worker
322*9e94795aSAndroid Build Coastguard Worker    zip_command = self._base_zip_command(src_top, dist_dir, 'general-tests.zip')
323*9e94795aSAndroid Build Coastguard Worker
324*9e94795aSAndroid Build Coastguard Worker    # Add host testcases.
325*9e94795aSAndroid Build Coastguard Worker    if host_paths:
326*9e94795aSAndroid Build Coastguard Worker      zip_command.extend(
327*9e94795aSAndroid Build Coastguard Worker          self._generate_zip_options_for_items(
328*9e94795aSAndroid Build Coastguard Worker              prefix='host',
329*9e94795aSAndroid Build Coastguard Worker              relative_root=f'{src_top / soong_host_out}',
330*9e94795aSAndroid Build Coastguard Worker              directories=host_paths,
331*9e94795aSAndroid Build Coastguard Worker          )
332*9e94795aSAndroid Build Coastguard Worker      )
333*9e94795aSAndroid Build Coastguard Worker
334*9e94795aSAndroid Build Coastguard Worker    # Add target testcases.
335*9e94795aSAndroid Build Coastguard Worker    if target_paths:
336*9e94795aSAndroid Build Coastguard Worker      zip_command.extend(
337*9e94795aSAndroid Build Coastguard Worker          self._generate_zip_options_for_items(
338*9e94795aSAndroid Build Coastguard Worker              prefix='target',
339*9e94795aSAndroid Build Coastguard Worker              relative_root=f'{src_top / product_out}',
340*9e94795aSAndroid Build Coastguard Worker              directories=target_paths,
341*9e94795aSAndroid Build Coastguard Worker          )
342*9e94795aSAndroid Build Coastguard Worker      )
343*9e94795aSAndroid Build Coastguard Worker
344*9e94795aSAndroid Build Coastguard Worker    # TODO(lucafarsi): Push this logic into a general-tests-minimal build command
345*9e94795aSAndroid Build Coastguard Worker    # Add necessary tools. These are also hardcoded in general-tests.mk.
346*9e94795aSAndroid Build Coastguard Worker    framework_path = soong_host_out / 'framework'
347*9e94795aSAndroid Build Coastguard Worker
348*9e94795aSAndroid Build Coastguard Worker    zip_command.extend(
349*9e94795aSAndroid Build Coastguard Worker        self._generate_zip_options_for_items(
350*9e94795aSAndroid Build Coastguard Worker            prefix='host/tools',
351*9e94795aSAndroid Build Coastguard Worker            relative_root=str(framework_path),
352*9e94795aSAndroid Build Coastguard Worker            files=[
353*9e94795aSAndroid Build Coastguard Worker                f"{framework_path / 'cts-tradefed.jar'}",
354*9e94795aSAndroid Build Coastguard Worker                f"{framework_path / 'compatibility-host-util.jar'}",
355*9e94795aSAndroid Build Coastguard Worker                f"{framework_path / 'vts-tradefed.jar'}",
356*9e94795aSAndroid Build Coastguard Worker            ],
357*9e94795aSAndroid Build Coastguard Worker        )
358*9e94795aSAndroid Build Coastguard Worker    )
359*9e94795aSAndroid Build Coastguard Worker
360*9e94795aSAndroid Build Coastguard Worker    zip_commands.append(zip_command)
361*9e94795aSAndroid Build Coastguard Worker    return zip_commands
362*9e94795aSAndroid Build Coastguard Worker
363*9e94795aSAndroid Build Coastguard Worker  def _collect_config_files(
364*9e94795aSAndroid Build Coastguard Worker      self,
365*9e94795aSAndroid Build Coastguard Worker      src_top: pathlib.Path,
366*9e94795aSAndroid Build Coastguard Worker      root_dir: pathlib.Path,
367*9e94795aSAndroid Build Coastguard Worker      config_files: list[str],
368*9e94795aSAndroid Build Coastguard Worker  ):
369*9e94795aSAndroid Build Coastguard Worker    for root, dirs, files in os.walk(src_top / root_dir):
370*9e94795aSAndroid Build Coastguard Worker      for file in files:
371*9e94795aSAndroid Build Coastguard Worker        if file.endswith('.config'):
372*9e94795aSAndroid Build Coastguard Worker          config_files.append(root_dir / file)
373*9e94795aSAndroid Build Coastguard Worker
374*9e94795aSAndroid Build Coastguard Worker  def _get_zip_test_configs_zips_commands(
375*9e94795aSAndroid Build Coastguard Worker      self,
376*9e94795aSAndroid Build Coastguard Worker      src_top: pathlib.Path,
377*9e94795aSAndroid Build Coastguard Worker      dist_dir: pathlib.Path,
378*9e94795aSAndroid Build Coastguard Worker      host_out: pathlib.Path,
379*9e94795aSAndroid Build Coastguard Worker      product_out: pathlib.Path,
380*9e94795aSAndroid Build Coastguard Worker      host_config_files: list[str],
381*9e94795aSAndroid Build Coastguard Worker      target_config_files: list[str],
382*9e94795aSAndroid Build Coastguard Worker  ) -> tuple[list[str], list[str]]:
383*9e94795aSAndroid Build Coastguard Worker    """Generate general-tests_configs.zip and general-tests_list.zip.
384*9e94795aSAndroid Build Coastguard Worker
385*9e94795aSAndroid Build Coastguard Worker    general-tests_configs.zip contains all of the .config files that were
386*9e94795aSAndroid Build Coastguard Worker    built and general-tests_list.zip contains a text file which lists
387*9e94795aSAndroid Build Coastguard Worker    all of the .config files that are in general-tests_configs.zip.
388*9e94795aSAndroid Build Coastguard Worker
389*9e94795aSAndroid Build Coastguard Worker    general-tests_configs.zip is organized as follows:
390*9e94795aSAndroid Build Coastguard Worker    /
391*9e94795aSAndroid Build Coastguard Worker      host/
392*9e94795aSAndroid Build Coastguard Worker        testcases/
393*9e94795aSAndroid Build Coastguard Worker          test_1.config
394*9e94795aSAndroid Build Coastguard Worker          test_2.config
395*9e94795aSAndroid Build Coastguard Worker          ...
396*9e94795aSAndroid Build Coastguard Worker      target/
397*9e94795aSAndroid Build Coastguard Worker        testcases/
398*9e94795aSAndroid Build Coastguard Worker          test_1.config
399*9e94795aSAndroid Build Coastguard Worker          test_2.config
400*9e94795aSAndroid Build Coastguard Worker          ...
401*9e94795aSAndroid Build Coastguard Worker
402*9e94795aSAndroid Build Coastguard Worker    So the process is we write out the paths to all the host config files into
403*9e94795aSAndroid Build Coastguard Worker    one
404*9e94795aSAndroid Build Coastguard Worker    file and all the paths to the target config files in another. We also write
405*9e94795aSAndroid Build Coastguard Worker    the paths to all the config files into a third file to use for
406*9e94795aSAndroid Build Coastguard Worker    general-tests_list.zip.
407*9e94795aSAndroid Build Coastguard Worker
408*9e94795aSAndroid Build Coastguard Worker    Args:
409*9e94795aSAndroid Build Coastguard Worker      dist_dir: dist directory.
410*9e94795aSAndroid Build Coastguard Worker      host_out: host out directory.
411*9e94795aSAndroid Build Coastguard Worker      product_out: product out directory.
412*9e94795aSAndroid Build Coastguard Worker      host_config_files: list of all host config files.
413*9e94795aSAndroid Build Coastguard Worker      target_config_files: list of all target config files.
414*9e94795aSAndroid Build Coastguard Worker
415*9e94795aSAndroid Build Coastguard Worker    Returns:
416*9e94795aSAndroid Build Coastguard Worker      The commands to generate general-tests_configs.zip and
417*9e94795aSAndroid Build Coastguard Worker      general-tests_list.zip
418*9e94795aSAndroid Build Coastguard Worker    """
419*9e94795aSAndroid Build Coastguard Worker    with open(
420*9e94795aSAndroid Build Coastguard Worker        f"{host_out / 'host_general-tests_list'}", 'w'
421*9e94795aSAndroid Build Coastguard Worker    ) as host_list_file, open(
422*9e94795aSAndroid Build Coastguard Worker        f"{product_out / 'target_general-tests_list'}", 'w'
423*9e94795aSAndroid Build Coastguard Worker    ) as target_list_file, open(
424*9e94795aSAndroid Build Coastguard Worker        f"{host_out / 'general-tests_list'}", 'w'
425*9e94795aSAndroid Build Coastguard Worker    ) as list_file:
426*9e94795aSAndroid Build Coastguard Worker
427*9e94795aSAndroid Build Coastguard Worker      for config_file in host_config_files:
428*9e94795aSAndroid Build Coastguard Worker        host_list_file.write(f'{config_file}' + '\n')
429*9e94795aSAndroid Build Coastguard Worker        list_file.write('host/' + os.path.relpath(config_file, host_out) + '\n')
430*9e94795aSAndroid Build Coastguard Worker
431*9e94795aSAndroid Build Coastguard Worker      for config_file in target_config_files:
432*9e94795aSAndroid Build Coastguard Worker        target_list_file.write(f'{config_file}' + '\n')
433*9e94795aSAndroid Build Coastguard Worker        list_file.write(
434*9e94795aSAndroid Build Coastguard Worker            'target/' + os.path.relpath(config_file, product_out) + '\n'
435*9e94795aSAndroid Build Coastguard Worker        )
436*9e94795aSAndroid Build Coastguard Worker
437*9e94795aSAndroid Build Coastguard Worker    zip_commands = []
438*9e94795aSAndroid Build Coastguard Worker
439*9e94795aSAndroid Build Coastguard Worker    tests_config_zip_command = self._base_zip_command(
440*9e94795aSAndroid Build Coastguard Worker        src_top, dist_dir, 'general-tests_configs.zip'
441*9e94795aSAndroid Build Coastguard Worker    )
442*9e94795aSAndroid Build Coastguard Worker    tests_config_zip_command.extend(
443*9e94795aSAndroid Build Coastguard Worker        self._generate_zip_options_for_items(
444*9e94795aSAndroid Build Coastguard Worker            prefix='host',
445*9e94795aSAndroid Build Coastguard Worker            relative_root=str(host_out),
446*9e94795aSAndroid Build Coastguard Worker            list_files=[f"{host_out / 'host_general-tests_list'}"],
447*9e94795aSAndroid Build Coastguard Worker        )
448*9e94795aSAndroid Build Coastguard Worker    )
449*9e94795aSAndroid Build Coastguard Worker
450*9e94795aSAndroid Build Coastguard Worker    tests_config_zip_command.extend(
451*9e94795aSAndroid Build Coastguard Worker        self._generate_zip_options_for_items(
452*9e94795aSAndroid Build Coastguard Worker            prefix='target',
453*9e94795aSAndroid Build Coastguard Worker            relative_root=str(product_out),
454*9e94795aSAndroid Build Coastguard Worker            list_files=[f"{product_out / 'target_general-tests_list'}"],
455*9e94795aSAndroid Build Coastguard Worker        ),
456*9e94795aSAndroid Build Coastguard Worker    )
457*9e94795aSAndroid Build Coastguard Worker
458*9e94795aSAndroid Build Coastguard Worker    zip_commands.append(tests_config_zip_command)
459*9e94795aSAndroid Build Coastguard Worker
460*9e94795aSAndroid Build Coastguard Worker    tests_list_zip_command = self._base_zip_command(
461*9e94795aSAndroid Build Coastguard Worker        src_top, dist_dir, 'general-tests_list.zip'
462*9e94795aSAndroid Build Coastguard Worker    )
463*9e94795aSAndroid Build Coastguard Worker    tests_list_zip_command.extend(
464*9e94795aSAndroid Build Coastguard Worker        self._generate_zip_options_for_items(
465*9e94795aSAndroid Build Coastguard Worker            relative_root=str(host_out),
466*9e94795aSAndroid Build Coastguard Worker            files=[f"{host_out / 'general-tests_list'}"],
467*9e94795aSAndroid Build Coastguard Worker        )
468*9e94795aSAndroid Build Coastguard Worker    )
469*9e94795aSAndroid Build Coastguard Worker    zip_commands.append(tests_list_zip_command)
470*9e94795aSAndroid Build Coastguard Worker
471*9e94795aSAndroid Build Coastguard Worker    return zip_commands
472*9e94795aSAndroid Build Coastguard Worker
473*9e94795aSAndroid Build Coastguard Worker  def get_enabled_flag(self):
474*9e94795aSAndroid Build Coastguard Worker    return 'general_tests_optimized'
475*9e94795aSAndroid Build Coastguard Worker
476*9e94795aSAndroid Build Coastguard Worker  @classmethod
477*9e94795aSAndroid Build Coastguard Worker  def get_optimized_targets(cls) -> dict[str, OptimizedBuildTarget]:
478*9e94795aSAndroid Build Coastguard Worker    return {'general-tests': functools.partial(cls)}
479*9e94795aSAndroid Build Coastguard Worker
480*9e94795aSAndroid Build Coastguard Worker
481*9e94795aSAndroid Build Coastguard WorkerOPTIMIZED_BUILD_TARGETS = {}
482*9e94795aSAndroid Build Coastguard WorkerOPTIMIZED_BUILD_TARGETS.update(GeneralTestsOptimizer.get_optimized_targets())
483