1b8021494Sopenharmony_ci#!/usr/bin/env python3
2b8021494Sopenharmony_ci
3b8021494Sopenharmony_ci# Copyright 2015, VIXL authors
4b8021494Sopenharmony_ci# All rights reserved.
5b8021494Sopenharmony_ci#
6b8021494Sopenharmony_ci# Redistribution and use in source and binary forms, with or without
7b8021494Sopenharmony_ci# modification, are permitted provided that the following conditions are met:
8b8021494Sopenharmony_ci#
9b8021494Sopenharmony_ci#   * Redistributions of source code must retain the above copyright notice,
10b8021494Sopenharmony_ci#     this list of conditions and the following disclaimer.
11b8021494Sopenharmony_ci#   * Redistributions in binary form must reproduce the above copyright notice,
12b8021494Sopenharmony_ci#     this list of conditions and the following disclaimer in the documentation
13b8021494Sopenharmony_ci#     and/or other materials provided with the distribution.
14b8021494Sopenharmony_ci#   * Neither the name of ARM Limited nor the names of its contributors may be
15b8021494Sopenharmony_ci#     used to endorse or promote products derived from this software without
16b8021494Sopenharmony_ci#     specific prior written permission.
17b8021494Sopenharmony_ci#
18b8021494Sopenharmony_ci# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
19b8021494Sopenharmony_ci# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20b8021494Sopenharmony_ci# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21b8021494Sopenharmony_ci# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22b8021494Sopenharmony_ci# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b8021494Sopenharmony_ci# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24b8021494Sopenharmony_ci# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25b8021494Sopenharmony_ci# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26b8021494Sopenharmony_ci# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27b8021494Sopenharmony_ci# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28b8021494Sopenharmony_ci
29b8021494Sopenharmony_ciimport argparse
30b8021494Sopenharmony_ciimport fcntl
31b8021494Sopenharmony_ciimport itertools
32b8021494Sopenharmony_ciimport multiprocessing
33b8021494Sopenharmony_ciimport os
34b8021494Sopenharmony_cifrom os.path import join
35b8021494Sopenharmony_ciimport platform
36b8021494Sopenharmony_ciimport subprocess
37b8021494Sopenharmony_ciimport sys
38b8021494Sopenharmony_ciimport time
39b8021494Sopenharmony_ci
40b8021494Sopenharmony_ciimport config
41b8021494Sopenharmony_ciimport clang_format
42b8021494Sopenharmony_ciimport clang_tidy
43b8021494Sopenharmony_ciimport lint
44b8021494Sopenharmony_ciimport printer
45b8021494Sopenharmony_ciimport test
46b8021494Sopenharmony_ciimport test_runner
47b8021494Sopenharmony_ciimport util
48b8021494Sopenharmony_ci
49b8021494Sopenharmony_ci
50b8021494Sopenharmony_cidir_root = config.dir_root
51b8021494Sopenharmony_ci
52b8021494Sopenharmony_ci
53b8021494Sopenharmony_ci# Remove duplicates from a list
54b8021494Sopenharmony_cidef RemoveDuplicates(values):
55b8021494Sopenharmony_ci  # Convert the list into a set and back to list
56b8021494Sopenharmony_ci  # as sets guarantee items are unique.
57b8021494Sopenharmony_ci  return list(set(values))
58b8021494Sopenharmony_ci
59b8021494Sopenharmony_ci
60b8021494Sopenharmony_ci# Custom argparse.Action to automatically add and handle an 'all' option.
61b8021494Sopenharmony_ci# If no 'default' value is set, it will default to 'all.
62b8021494Sopenharmony_ci# If accepted options are set using 'choices' then only these values will be
63b8021494Sopenharmony_ci# allowed.
64b8021494Sopenharmony_ci# If they're set using 'soft_choices' then 'all' will default to these values,
65b8021494Sopenharmony_ci# but other values will also be accepted.
66b8021494Sopenharmony_ciclass AllChoiceAction(argparse.Action):
67b8021494Sopenharmony_ci
68b8021494Sopenharmony_ci  # At least one option was set by the user.
69b8021494Sopenharmony_ci  WasSetByUser = False
70b8021494Sopenharmony_ci
71b8021494Sopenharmony_ci  def __init__(self, **kwargs):
72b8021494Sopenharmony_ci    if 'choices' in kwargs:
73b8021494Sopenharmony_ci      assert 'soft_choices' not in kwargs,\
74b8021494Sopenharmony_ci          "Can't have both 'choices' and 'soft_choices' options"
75b8021494Sopenharmony_ci      self.all_choices = list(kwargs['choices'])
76b8021494Sopenharmony_ci      kwargs['choices'].append('all')
77b8021494Sopenharmony_ci    else:
78b8021494Sopenharmony_ci      self.all_choices = kwargs['soft_choices']
79b8021494Sopenharmony_ci      kwargs['help'] += ' Supported values: {' + ','.join(
80b8021494Sopenharmony_ci          ['all'] + self.all_choices) + '}'
81b8021494Sopenharmony_ci      del kwargs['soft_choices']
82b8021494Sopenharmony_ci    if 'default' not in kwargs:
83b8021494Sopenharmony_ci      kwargs['default'] = self.all_choices
84b8021494Sopenharmony_ci    super(AllChoiceAction, self).__init__(**kwargs)
85b8021494Sopenharmony_ci
86b8021494Sopenharmony_ci  def __call__(self, parser, namespace, values, option_string=None):
87b8021494Sopenharmony_ci    AllChoiceAction.WasSetByUser = True
88b8021494Sopenharmony_ci    if 'all' in values:
89b8021494Sopenharmony_ci      # Substitute 'all' by the actual values.
90b8021494Sopenharmony_ci      values = self.all_choices + [value for value in values if value != 'all']
91b8021494Sopenharmony_ci
92b8021494Sopenharmony_ci    setattr(namespace, self.dest, RemoveDuplicates(values))
93b8021494Sopenharmony_ci
94b8021494Sopenharmony_ci
95b8021494Sopenharmony_cidef BuildOptions():
96b8021494Sopenharmony_ci  args = argparse.ArgumentParser(
97b8021494Sopenharmony_ci    description =
98b8021494Sopenharmony_ci    '''This tool runs all tests matching the specified filters for multiple
99b8021494Sopenharmony_ci    environment, build options, and runtime options configurations.''',
100b8021494Sopenharmony_ci    # Print default values.
101b8021494Sopenharmony_ci    formatter_class=argparse.ArgumentDefaultsHelpFormatter)
102b8021494Sopenharmony_ci
103b8021494Sopenharmony_ci  args.add_argument('filters', metavar='filter', nargs='*',
104b8021494Sopenharmony_ci                    help='Run tests matching all of the (regexp) filters.')
105b8021494Sopenharmony_ci
106b8021494Sopenharmony_ci  # We automatically build the script options from the options to be tested.
107b8021494Sopenharmony_ci  test_arguments = args.add_argument_group(
108b8021494Sopenharmony_ci    'Test options',
109b8021494Sopenharmony_ci    'These options indicate what should be tested')
110b8021494Sopenharmony_ci  test_arguments.add_argument(
111b8021494Sopenharmony_ci      '--negative_testing',
112b8021494Sopenharmony_ci      help='Tests with negative testing enabled.',
113b8021494Sopenharmony_ci      action='store_const',
114b8021494Sopenharmony_ci      const='on',
115b8021494Sopenharmony_ci      default='off')
116b8021494Sopenharmony_ci  test_arguments.add_argument(
117b8021494Sopenharmony_ci      '--compiler',
118b8021494Sopenharmony_ci      help='Test for the specified compilers.',
119b8021494Sopenharmony_ci      soft_choices=config.tested_compilers,
120b8021494Sopenharmony_ci      action=AllChoiceAction,
121b8021494Sopenharmony_ci      nargs="+")
122b8021494Sopenharmony_ci  test_arguments.add_argument(
123b8021494Sopenharmony_ci      '--mode',
124b8021494Sopenharmony_ci      help='Test with the specified build modes.',
125b8021494Sopenharmony_ci      choices=config.build_options_modes,
126b8021494Sopenharmony_ci      action=AllChoiceAction,
127b8021494Sopenharmony_ci      nargs="+")
128b8021494Sopenharmony_ci  test_arguments.add_argument(
129b8021494Sopenharmony_ci      '--std',
130b8021494Sopenharmony_ci      help='Test with the specified C++ standard.',
131b8021494Sopenharmony_ci      soft_choices=config.tested_cpp_standards,
132b8021494Sopenharmony_ci      action=AllChoiceAction,
133b8021494Sopenharmony_ci      nargs="+")
134b8021494Sopenharmony_ci  test_arguments.add_argument(
135b8021494Sopenharmony_ci      '--target',
136b8021494Sopenharmony_ci      help='Test with the specified isa enabled.',
137b8021494Sopenharmony_ci      soft_choices=config.build_options_target,
138b8021494Sopenharmony_ci      action=AllChoiceAction,
139b8021494Sopenharmony_ci      nargs="+")
140b8021494Sopenharmony_ci
141b8021494Sopenharmony_ci  general_arguments = args.add_argument_group('General options')
142b8021494Sopenharmony_ci  general_arguments.add_argument('--dry-run', action='store_true',
143b8021494Sopenharmony_ci                                 help='''Don't actually build or run anything,
144b8021494Sopenharmony_ci                                 but print the configurations that would be
145b8021494Sopenharmony_ci                                 tested.''')
146b8021494Sopenharmony_ci  general_arguments.add_argument('--verbose', action='store_true',
147b8021494Sopenharmony_ci                                 help='''Print extra information.''')
148b8021494Sopenharmony_ci  general_arguments.add_argument(
149b8021494Sopenharmony_ci    '--jobs', '-j', metavar='N', type=int, nargs='?',
150b8021494Sopenharmony_ci    default=multiprocessing.cpu_count(),
151b8021494Sopenharmony_ci    const=multiprocessing.cpu_count(),
152b8021494Sopenharmony_ci    help='''Runs the tests using N jobs. If the option is set but no value is
153b8021494Sopenharmony_ci    provided, the script will use as many jobs as it thinks useful.''')
154b8021494Sopenharmony_ci  general_arguments.add_argument('--clang-format',
155b8021494Sopenharmony_ci                                 default=clang_format.DEFAULT_CLANG_FORMAT,
156b8021494Sopenharmony_ci                                 help='Path to clang-format.')
157b8021494Sopenharmony_ci  general_arguments.add_argument('--clang-tidy',
158b8021494Sopenharmony_ci                                 default=clang_tidy.DEFAULT_CLANG_TIDY,
159b8021494Sopenharmony_ci                                 help='Path to clang-tidy.')
160b8021494Sopenharmony_ci  general_arguments.add_argument('--nobench', action='store_true',
161b8021494Sopenharmony_ci                                 help='Do not run benchmarks.')
162b8021494Sopenharmony_ci  general_arguments.add_argument('--nolint', action='store_true',
163b8021494Sopenharmony_ci                                 help='Do not run the linter.')
164b8021494Sopenharmony_ci  general_arguments.add_argument('--noclang-format', action='store_true',
165b8021494Sopenharmony_ci                                 help='Do not run clang-format.')
166b8021494Sopenharmony_ci  general_arguments.add_argument('--noclang-tidy', action='store_true',
167b8021494Sopenharmony_ci                                 help='Do not run clang-tidy.')
168b8021494Sopenharmony_ci  general_arguments.add_argument('--notest', action='store_true',
169b8021494Sopenharmony_ci                                 help='Do not run tests.')
170b8021494Sopenharmony_ci  general_arguments.add_argument('--nocheck-code-coverage', action='store_true',
171b8021494Sopenharmony_ci                                 help='Do not check code coverage results log.')
172b8021494Sopenharmony_ci  general_arguments.add_argument('--fail-early', action='store_true',
173b8021494Sopenharmony_ci                                 help='Exit as soon as a test fails.')
174b8021494Sopenharmony_ci  general_arguments.add_argument(
175b8021494Sopenharmony_ci    '--under_valgrind', action='store_true',
176b8021494Sopenharmony_ci    help='''Run the test-runner commands under Valgrind.
177b8021494Sopenharmony_ci            Note that a few tests are known to fail because of
178b8021494Sopenharmony_ci            issues in Valgrind''')
179b8021494Sopenharmony_ci  return args.parse_args()
180b8021494Sopenharmony_ci
181b8021494Sopenharmony_ci
182b8021494Sopenharmony_cidef RunCommand(command, environment_options = None):
183b8021494Sopenharmony_ci  # Create a copy of the environment. We do not want to pollute the environment
184b8021494Sopenharmony_ci  # of future commands run.
185b8021494Sopenharmony_ci  environment = os.environ.copy()
186b8021494Sopenharmony_ci
187b8021494Sopenharmony_ci  printable_command = ''
188b8021494Sopenharmony_ci  if environment_options:
189b8021494Sopenharmony_ci    # Add the environment options to the environment:
190b8021494Sopenharmony_ci    environment.update(environment_options)
191b8021494Sopenharmony_ci    printable_command += ' ' + DictToString(environment_options) + ' '
192b8021494Sopenharmony_ci  printable_command += ' '.join(command)
193b8021494Sopenharmony_ci
194b8021494Sopenharmony_ci  printable_command_orange = \
195b8021494Sopenharmony_ci    printer.COLOUR_ORANGE + printable_command + printer.NO_COLOUR
196b8021494Sopenharmony_ci  printer.PrintOverwritableLine(printable_command_orange)
197b8021494Sopenharmony_ci  sys.stdout.flush()
198b8021494Sopenharmony_ci
199b8021494Sopenharmony_ci  # Start a process for the command.
200b8021494Sopenharmony_ci  # Interleave `stderr` and `stdout`.
201b8021494Sopenharmony_ci  p = subprocess.Popen(command,
202b8021494Sopenharmony_ci                       stdout=subprocess.PIPE,
203b8021494Sopenharmony_ci                       stderr=subprocess.STDOUT,
204b8021494Sopenharmony_ci                       env=environment)
205b8021494Sopenharmony_ci
206b8021494Sopenharmony_ci  # We want to be able to display a continuously updated 'work indicator' while
207b8021494Sopenharmony_ci  # the process is running. Since the process can hang if the `stdout` pipe is
208b8021494Sopenharmony_ci  # full, we need to pull from it regularly. We cannot do so via the
209b8021494Sopenharmony_ci  # `readline()` function because it is blocking, and would thus cause the
210b8021494Sopenharmony_ci  # indicator to not be updated properly. So use file control mechanisms
211b8021494Sopenharmony_ci  # instead.
212b8021494Sopenharmony_ci  indicator = ' (still working: %d seconds elapsed)'
213b8021494Sopenharmony_ci
214b8021494Sopenharmony_ci  # Mark the process output as non-blocking.
215b8021494Sopenharmony_ci  flags = fcntl.fcntl(p.stdout, fcntl.F_GETFL)
216b8021494Sopenharmony_ci  fcntl.fcntl(p.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK)
217b8021494Sopenharmony_ci
218b8021494Sopenharmony_ci  t_start = time.time()
219b8021494Sopenharmony_ci  t_current = t_start
220b8021494Sopenharmony_ci  t_last_indication = t_start
221b8021494Sopenharmony_ci  t_current = t_start
222b8021494Sopenharmony_ci  process_output = b''
223b8021494Sopenharmony_ci
224b8021494Sopenharmony_ci  # Keep looping as long as the process is running.
225b8021494Sopenharmony_ci  while p.poll() is None:
226b8021494Sopenharmony_ci    # Avoid polling too often.
227b8021494Sopenharmony_ci    time.sleep(0.1)
228b8021494Sopenharmony_ci    # Update the progress indicator.
229b8021494Sopenharmony_ci    t_current = time.time()
230b8021494Sopenharmony_ci    if (t_current - t_start >= 2) and (t_current - t_last_indication >= 1):
231b8021494Sopenharmony_ci      printer.PrintOverwritableLine(
232b8021494Sopenharmony_ci        printable_command_orange + indicator % int(t_current - t_start))
233b8021494Sopenharmony_ci      sys.stdout.flush()
234b8021494Sopenharmony_ci      t_last_indication = t_current
235b8021494Sopenharmony_ci    # Pull from the process output.
236b8021494Sopenharmony_ci    while True:
237b8021494Sopenharmony_ci      try:
238b8021494Sopenharmony_ci        line = os.read(p.stdout.fileno(), 1024)
239b8021494Sopenharmony_ci      except OSError:
240b8021494Sopenharmony_ci        line = b''
241b8021494Sopenharmony_ci        break
242b8021494Sopenharmony_ci      if line == b'': break
243b8021494Sopenharmony_ci      process_output += line
244b8021494Sopenharmony_ci
245b8021494Sopenharmony_ci  # The process has exited. Don't forget to retrieve the rest of its output.
246b8021494Sopenharmony_ci  out, err = p.communicate()
247b8021494Sopenharmony_ci  rc = p.poll()
248b8021494Sopenharmony_ci  process_output += out
249b8021494Sopenharmony_ci
250b8021494Sopenharmony_ci  printable_command += ' (took %d seconds)' % int(t_current - t_start)
251b8021494Sopenharmony_ci  if rc == 0:
252b8021494Sopenharmony_ci    printer.Print(printer.COLOUR_GREEN + printable_command + printer.NO_COLOUR)
253b8021494Sopenharmony_ci  else:
254b8021494Sopenharmony_ci    printer.Print(printer.COLOUR_RED + printable_command + printer.NO_COLOUR)
255b8021494Sopenharmony_ci    printer.Print(process_output.decode())
256b8021494Sopenharmony_ci  return rc
257b8021494Sopenharmony_ci
258b8021494Sopenharmony_ci
259b8021494Sopenharmony_cidef RunLinter(jobs):
260b8021494Sopenharmony_ci  return lint.RunLinter([join(dir_root, x) for x in util.get_source_files()],
261b8021494Sopenharmony_ci                        jobs = args.jobs, progress_prefix = 'cpp lint: ')
262b8021494Sopenharmony_ci
263b8021494Sopenharmony_ci
264b8021494Sopenharmony_cidef RunClangFormat(clang_path, jobs):
265b8021494Sopenharmony_ci  return clang_format.ClangFormatFiles(util.get_source_files(exclude_dirs=['.*', '*/traces/*', '*/aarch32/*']),
266b8021494Sopenharmony_ci                                       clang_path,
267b8021494Sopenharmony_ci                                       jobs = jobs,
268b8021494Sopenharmony_ci                                       progress_prefix = 'clang-format: ')
269b8021494Sopenharmony_ci
270b8021494Sopenharmony_cidef RunClangTidy(clang_path, jobs):
271b8021494Sopenharmony_ci  return clang_tidy.ClangTidyFiles(util.get_source_files(exclude_dirs=['.*', '*/traces/*', '*/aarch32/*']),
272b8021494Sopenharmony_ci                                   clang_path,
273b8021494Sopenharmony_ci                                   jobs = jobs,
274b8021494Sopenharmony_ci                                   progress_prefix = 'clang-tidy: ')
275b8021494Sopenharmony_ci
276b8021494Sopenharmony_cidef CheckCodeCoverage():
277b8021494Sopenharmony_ci  command = ['tools/check_recent_coverage.sh']
278b8021494Sopenharmony_ci  return RunCommand(command)
279b8021494Sopenharmony_ci
280b8021494Sopenharmony_cidef BuildAll(build_options, jobs, environment_options):
281b8021494Sopenharmony_ci  scons_command = ['scons', '-C', dir_root, 'all', '-j', str(jobs)]
282b8021494Sopenharmony_ci  if util.IsCommandAvailable('ccache'):
283b8021494Sopenharmony_ci    scons_command += ['compiler_wrapper=ccache']
284b8021494Sopenharmony_ci    # Fixes warnings for ccache 3.3.1 and lower:
285b8021494Sopenharmony_ci    environment_options = environment_options.copy()
286b8021494Sopenharmony_ci    environment_options["CCACHE_CPP2"] = 'yes'
287b8021494Sopenharmony_ci  scons_command += DictToString(build_options).split()
288b8021494Sopenharmony_ci  return RunCommand(scons_command, environment_options)
289b8021494Sopenharmony_ci
290b8021494Sopenharmony_ci
291b8021494Sopenharmony_cidef CanRunAarch64(options, args):
292b8021494Sopenharmony_ci  for target in options['target']:
293b8021494Sopenharmony_ci    if target in ['aarch64', 'a64']:
294b8021494Sopenharmony_ci      return True
295b8021494Sopenharmony_ci
296b8021494Sopenharmony_ci  return False
297b8021494Sopenharmony_ci
298b8021494Sopenharmony_ci
299b8021494Sopenharmony_cidef CanRunAarch32(options, args):
300b8021494Sopenharmony_ci  for target in options['target']:
301b8021494Sopenharmony_ci    if target in ['aarch32', 'a32', 't32']:
302b8021494Sopenharmony_ci      return True
303b8021494Sopenharmony_ci  return False
304b8021494Sopenharmony_ci
305b8021494Sopenharmony_ci
306b8021494Sopenharmony_cidef RunBenchmarks(options, args):
307b8021494Sopenharmony_ci  rc = 0
308b8021494Sopenharmony_ci  if CanRunAarch32(options, args):
309b8021494Sopenharmony_ci    benchmark_names = util.ListCCFilesWithoutExt(config.dir_aarch32_benchmarks)
310b8021494Sopenharmony_ci    for bench in benchmark_names:
311b8021494Sopenharmony_ci      rc |= RunCommand(
312b8021494Sopenharmony_ci        [os.path.realpath(
313b8021494Sopenharmony_ci          join(config.dir_build_latest, 'benchmarks/aarch32', bench)), '10'])
314b8021494Sopenharmony_ci  if CanRunAarch64(options, args):
315b8021494Sopenharmony_ci    benchmark_names = util.ListCCFilesWithoutExt(config.dir_aarch64_benchmarks)
316b8021494Sopenharmony_ci    for bench in benchmark_names:
317b8021494Sopenharmony_ci      rc |= RunCommand(
318b8021494Sopenharmony_ci        [util.relrealpath(
319b8021494Sopenharmony_ci            join(config.dir_build_latest,
320b8021494Sopenharmony_ci                'benchmarks/aarch64', bench)), '10'])
321b8021494Sopenharmony_ci  return rc
322b8021494Sopenharmony_ci
323b8021494Sopenharmony_ci
324b8021494Sopenharmony_ci
325b8021494Sopenharmony_ci# It is a precommit run if the user did not specify any of the
326b8021494Sopenharmony_ci# options that would affect the automatically generated combinations.
327b8021494Sopenharmony_cidef IsPrecommitRun(args):
328b8021494Sopenharmony_ci  return args.negative_testing == "off" and not AllChoiceAction.WasSetByUser
329b8021494Sopenharmony_ci
330b8021494Sopenharmony_ci# Generate a list of all the possible combinations of the passed list:
331b8021494Sopenharmony_ci# ListCombinations( a = [a0, a1], b = [b0, b1] ) will return
332b8021494Sopenharmony_ci# [ {a : a0, b : b0}, {a : a0, b : b1}, {a: a1, b : b0}, {a : a1, b : b1}]
333b8021494Sopenharmony_cidef ListCombinations(**kwargs):
334b8021494Sopenharmony_ci  # End of recursion: no options passed
335b8021494Sopenharmony_ci  if not kwargs:
336b8021494Sopenharmony_ci    return [{}]
337b8021494Sopenharmony_ci  option, values = kwargs.popitem()
338b8021494Sopenharmony_ci  configs = ListCombinations(**kwargs)
339b8021494Sopenharmony_ci  retval = []
340b8021494Sopenharmony_ci  if not isinstance(values, list):
341b8021494Sopenharmony_ci    values = [values]
342b8021494Sopenharmony_ci  for value in values:
343b8021494Sopenharmony_ci    for config in configs:
344b8021494Sopenharmony_ci      new_config = config.copy()
345b8021494Sopenharmony_ci      new_config[option] = value
346b8021494Sopenharmony_ci      retval.append(new_config)
347b8021494Sopenharmony_ci  return retval
348b8021494Sopenharmony_ci
349b8021494Sopenharmony_ci# Convert a dictionary into a space separated string
350b8021494Sopenharmony_ci# {a : a0, b : b0} --> "a=a0 b=b0"
351b8021494Sopenharmony_cidef DictToString(options):
352b8021494Sopenharmony_ci  return " ".join(
353b8021494Sopenharmony_ci      ["{}={}".format(option, value) for option, value in options.items()])
354b8021494Sopenharmony_ci
355b8021494Sopenharmony_ci
356b8021494Sopenharmony_ciif __name__ == '__main__':
357b8021494Sopenharmony_ci  util.require_program('scons')
358b8021494Sopenharmony_ci
359b8021494Sopenharmony_ci  args = BuildOptions()
360b8021494Sopenharmony_ci
361b8021494Sopenharmony_ci  rc = util.ReturnCode(args.fail_early, printer.Print)
362b8021494Sopenharmony_ci
363b8021494Sopenharmony_ci  if args.under_valgrind:
364b8021494Sopenharmony_ci    util.require_program('valgrind')
365b8021494Sopenharmony_ci
366b8021494Sopenharmony_ci  if not args.nocheck_code_coverage:
367b8021494Sopenharmony_ci    rc.Combine(CheckCodeCoverage())
368b8021494Sopenharmony_ci
369b8021494Sopenharmony_ci  tests = test_runner.TestQueue()
370b8021494Sopenharmony_ci  if not args.nolint and not args.dry_run:
371b8021494Sopenharmony_ci    rc.Combine(RunLinter(args.jobs))
372b8021494Sopenharmony_ci
373b8021494Sopenharmony_ci  if not args.noclang_format and not args.dry_run:
374b8021494Sopenharmony_ci    rc.Combine(RunClangFormat(args.clang_format, args.jobs))
375b8021494Sopenharmony_ci
376b8021494Sopenharmony_ci  if not args.noclang_tidy and not args.dry_run:
377b8021494Sopenharmony_ci    rc.Combine(RunClangTidy(args.clang_tidy, args.jobs))
378b8021494Sopenharmony_ci
379b8021494Sopenharmony_ci  list_options = []
380b8021494Sopenharmony_ci  if IsPrecommitRun(args):
381b8021494Sopenharmony_ci    # Maximize the coverage for precommit testing.
382b8021494Sopenharmony_ci
383b8021494Sopenharmony_ci    # Debug builds with negative testing and all targets enabled.
384b8021494Sopenharmony_ci    list_options += ListCombinations(
385b8021494Sopenharmony_ci        compiler = args.compiler,
386b8021494Sopenharmony_ci        negative_testing = 'on',
387b8021494Sopenharmony_ci        std = args.std,
388b8021494Sopenharmony_ci        mode = 'debug',
389b8021494Sopenharmony_ci        target = 'a64,a32,t32')
390b8021494Sopenharmony_ci
391b8021494Sopenharmony_ci    # Release builds with all targets enabled.
392b8021494Sopenharmony_ci    list_options += ListCombinations(
393b8021494Sopenharmony_ci        compiler = args.compiler,
394b8021494Sopenharmony_ci        negative_testing = 'off',
395b8021494Sopenharmony_ci        std = args.std,
396b8021494Sopenharmony_ci        mode = 'release',
397b8021494Sopenharmony_ci        target = 'a64,a32,t32')
398b8021494Sopenharmony_ci
399b8021494Sopenharmony_ci    # Debug builds for individual targets.
400b8021494Sopenharmony_ci    list_options += ListCombinations(
401b8021494Sopenharmony_ci        compiler = args.compiler[0],
402b8021494Sopenharmony_ci        negative_testing = 'off',
403b8021494Sopenharmony_ci        std = args.std,
404b8021494Sopenharmony_ci        mode = 'debug',
405b8021494Sopenharmony_ci        target = ['a32', 't32', 'a64'])
406b8021494Sopenharmony_ci  else:
407b8021494Sopenharmony_ci    list_options = ListCombinations(
408b8021494Sopenharmony_ci        compiler = args.compiler,
409b8021494Sopenharmony_ci        negative_testing = args.negative_testing,
410b8021494Sopenharmony_ci        std = args.std,
411b8021494Sopenharmony_ci        mode = args.mode,
412b8021494Sopenharmony_ci        target = args.target)
413b8021494Sopenharmony_ci
414b8021494Sopenharmony_ci  for options in list_options:
415b8021494Sopenharmony_ci    if (args.dry_run):
416b8021494Sopenharmony_ci      print(DictToString(options))
417b8021494Sopenharmony_ci      continue
418b8021494Sopenharmony_ci    # Convert 'compiler' into an environment variable:
419b8021494Sopenharmony_ci    environment_options = {'CXX': options['compiler']}
420b8021494Sopenharmony_ci    del options['compiler']
421b8021494Sopenharmony_ci
422b8021494Sopenharmony_ci    # Avoid going through the build stage if we are not using the build
423b8021494Sopenharmony_ci    # result.
424b8021494Sopenharmony_ci    if not (args.notest and args.nobench):
425b8021494Sopenharmony_ci      build_rc = BuildAll(options, args.jobs, environment_options)
426b8021494Sopenharmony_ci      # Don't run the tests for this configuration if the build failed.
427b8021494Sopenharmony_ci      if build_rc != 0:
428b8021494Sopenharmony_ci        rc.Combine(build_rc)
429b8021494Sopenharmony_ci        continue
430b8021494Sopenharmony_ci
431b8021494Sopenharmony_ci      # Use the realpath of the test executable so that the commands printed
432b8021494Sopenharmony_ci      # can be copy-pasted and run.
433b8021494Sopenharmony_ci      test_executable = util.relrealpath(
434b8021494Sopenharmony_ci        join(config.dir_build_latest, 'test', 'test-runner'))
435b8021494Sopenharmony_ci
436b8021494Sopenharmony_ci      if not args.notest:
437b8021494Sopenharmony_ci        printer.Print(test_executable)
438b8021494Sopenharmony_ci        tests.AddTests(
439b8021494Sopenharmony_ci            test_executable,
440b8021494Sopenharmony_ci            args.filters,
441b8021494Sopenharmony_ci            list(),
442b8021494Sopenharmony_ci            args.under_valgrind)
443b8021494Sopenharmony_ci
444b8021494Sopenharmony_ci      if not args.nobench:
445b8021494Sopenharmony_ci        rc.Combine(RunBenchmarks(options, args))
446b8021494Sopenharmony_ci
447b8021494Sopenharmony_ci  rc.Combine(tests.Run(args.jobs, args.verbose))
448b8021494Sopenharmony_ci  if not args.dry_run:
449b8021494Sopenharmony_ci    rc.PrintStatus()
450b8021494Sopenharmony_ci
451b8021494Sopenharmony_ci  sys.exit(rc.Value)
452