xref: /aosp_15_r20/external/vixl/tools/util.py (revision f5c631da2f1efdd72b5fd1e20510e4042af13d77)
1*f5c631daSSadaf Ebrahimi# Copyright 2015, VIXL authors
2*f5c631daSSadaf Ebrahimi# All rights reserved.
3*f5c631daSSadaf Ebrahimi#
4*f5c631daSSadaf Ebrahimi# Redistribution and use in source and binary forms, with or without
5*f5c631daSSadaf Ebrahimi# modification, are permitted provided that the following conditions are met:
6*f5c631daSSadaf Ebrahimi#
7*f5c631daSSadaf Ebrahimi#   * Redistributions of source code must retain the above copyright notice,
8*f5c631daSSadaf Ebrahimi#     this list of conditions and the following disclaimer.
9*f5c631daSSadaf Ebrahimi#   * Redistributions in binary form must reproduce the above copyright notice,
10*f5c631daSSadaf Ebrahimi#     this list of conditions and the following disclaimer in the documentation
11*f5c631daSSadaf Ebrahimi#     and/or other materials provided with the distribution.
12*f5c631daSSadaf Ebrahimi#   * Neither the name of ARM Limited nor the names of its contributors may be
13*f5c631daSSadaf Ebrahimi#     used to endorse or promote products derived from this software without
14*f5c631daSSadaf Ebrahimi#     specific prior written permission.
15*f5c631daSSadaf Ebrahimi#
16*f5c631daSSadaf Ebrahimi# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17*f5c631daSSadaf Ebrahimi# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18*f5c631daSSadaf Ebrahimi# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19*f5c631daSSadaf Ebrahimi# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20*f5c631daSSadaf Ebrahimi# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*f5c631daSSadaf Ebrahimi# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22*f5c631daSSadaf Ebrahimi# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23*f5c631daSSadaf Ebrahimi# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*f5c631daSSadaf Ebrahimi# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25*f5c631daSSadaf Ebrahimi# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*f5c631daSSadaf Ebrahimi
27*f5c631daSSadaf Ebrahimifrom distutils.version import LooseVersion
28*f5c631daSSadaf Ebrahimiimport config
29*f5c631daSSadaf Ebrahimiimport fnmatch
30*f5c631daSSadaf Ebrahimiimport glob
31*f5c631daSSadaf Ebrahimiimport operator
32*f5c631daSSadaf Ebrahimiimport os
33*f5c631daSSadaf Ebrahimiimport re
34*f5c631daSSadaf Ebrahimiimport shlex
35*f5c631daSSadaf Ebrahimiimport subprocess
36*f5c631daSSadaf Ebrahimiimport sys
37*f5c631daSSadaf Ebrahimi
38*f5c631daSSadaf Ebrahimi
39*f5c631daSSadaf Ebrahimidef ListCCFilesWithoutExt(path):
40*f5c631daSSadaf Ebrahimi  return map(lambda x : os.path.splitext(os.path.basename(x))[0],
41*f5c631daSSadaf Ebrahimi             glob.glob(os.path.join(path, '*.cc')))
42*f5c631daSSadaf Ebrahimi
43*f5c631daSSadaf Ebrahimi
44*f5c631daSSadaf Ebrahimidef abort(message):
45*f5c631daSSadaf Ebrahimi  print('ABORTING: ' + message)
46*f5c631daSSadaf Ebrahimi  sys.exit(1)
47*f5c631daSSadaf Ebrahimi
48*f5c631daSSadaf Ebrahimi
49*f5c631daSSadaf Ebrahimi# Emulate python3 subprocess.getstatusoutput.
50*f5c631daSSadaf Ebrahimidef getstatusoutput(command):
51*f5c631daSSadaf Ebrahimi  try:
52*f5c631daSSadaf Ebrahimi    args = shlex.split(command)
53*f5c631daSSadaf Ebrahimi    output = subprocess.check_output(args, stderr=subprocess.STDOUT)
54*f5c631daSSadaf Ebrahimi    return 0, output.rstrip('\n')
55*f5c631daSSadaf Ebrahimi  except subprocess.CalledProcessError as e:
56*f5c631daSSadaf Ebrahimi    return e.returncode, e.output.rstrip('\n')
57*f5c631daSSadaf Ebrahimi
58*f5c631daSSadaf Ebrahimi
59*f5c631daSSadaf Ebrahimidef IsCommandAvailable(command):
60*f5c631daSSadaf Ebrahimi    retcode, unused_output = getstatusoutput('which %s' % command)
61*f5c631daSSadaf Ebrahimi    return retcode == 0
62*f5c631daSSadaf Ebrahimi
63*f5c631daSSadaf Ebrahimi
64*f5c631daSSadaf Ebrahimidef ensure_dir(path_name):
65*f5c631daSSadaf Ebrahimi  if not os.path.exists(path_name):
66*f5c631daSSadaf Ebrahimi    os.makedirs(path_name)
67*f5c631daSSadaf Ebrahimi
68*f5c631daSSadaf Ebrahimi
69*f5c631daSSadaf Ebrahimi# Check that the specified program is available.
70*f5c631daSSadaf Ebrahimidef require_program(program_name):
71*f5c631daSSadaf Ebrahimi  rc, out = getstatusoutput('which %s' % program_name)
72*f5c631daSSadaf Ebrahimi  if rc != 0:
73*f5c631daSSadaf Ebrahimi    print('ERROR: The required program %s was not found.' % program_name)
74*f5c631daSSadaf Ebrahimi    sys.exit(rc)
75*f5c631daSSadaf Ebrahimi
76*f5c631daSSadaf Ebrahimidef relrealpath(path, start=os.getcwd()):
77*f5c631daSSadaf Ebrahimi  return os.path.relpath(os.path.realpath(path), start)
78*f5c631daSSadaf Ebrahimi
79*f5c631daSSadaf Ebrahimi# Query the compiler about its preprocessor directives and return all of them as
80*f5c631daSSadaf Ebrahimi# a dictionnary.
81*f5c631daSSadaf Ebrahimidef GetCompilerDirectives(env):
82*f5c631daSSadaf Ebrahimi  args = [env['compiler']]
83*f5c631daSSadaf Ebrahimi  # Pass the CXXFLAGS varables to the compile, in case we've used "-m32" to
84*f5c631daSSadaf Ebrahimi  # compile for i386.
85*f5c631daSSadaf Ebrahimi  if env['CXXFLAGS']:
86*f5c631daSSadaf Ebrahimi    args.append(str(env['CXXFLAGS']))
87*f5c631daSSadaf Ebrahimi  args += ['-E', '-dM', '-']
88*f5c631daSSadaf Ebrahimi
89*f5c631daSSadaf Ebrahimi  # Instruct the compiler to dump all its preprocessor macros.
90*f5c631daSSadaf Ebrahimi  dump = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
91*f5c631daSSadaf Ebrahimi                          universal_newlines=True)
92*f5c631daSSadaf Ebrahimi  out, _ = dump.communicate()
93*f5c631daSSadaf Ebrahimi  return {
94*f5c631daSSadaf Ebrahimi    # Extract the macro name as key and value as element.
95*f5c631daSSadaf Ebrahimi    match.group(1): match.group(2)
96*f5c631daSSadaf Ebrahimi    for match in [
97*f5c631daSSadaf Ebrahimi      # Capture macro name.
98*f5c631daSSadaf Ebrahimi      re.search('^#define (\S+?) (.+)$', macro)
99*f5c631daSSadaf Ebrahimi      for macro in out.split('\n')
100*f5c631daSSadaf Ebrahimi    ]
101*f5c631daSSadaf Ebrahimi    # Filter out non-matches.
102*f5c631daSSadaf Ebrahimi    if match
103*f5c631daSSadaf Ebrahimi  }
104*f5c631daSSadaf Ebrahimi
105*f5c631daSSadaf Ebrahimi# Query the target architecture of the compiler. The 'target' architecture of
106*f5c631daSSadaf Ebrahimi# the compiler used to build VIXL is considered to be the 'host' architecture of
107*f5c631daSSadaf Ebrahimi# VIXL itself.
108*f5c631daSSadaf Ebrahimidef GetHostArch(env):
109*f5c631daSSadaf Ebrahimi  directives = GetCompilerDirectives(env)
110*f5c631daSSadaf Ebrahimi  if "__x86_64__" in directives:
111*f5c631daSSadaf Ebrahimi    return "x86_64"
112*f5c631daSSadaf Ebrahimi  elif "__i386__" in directives:
113*f5c631daSSadaf Ebrahimi    return "i386"
114*f5c631daSSadaf Ebrahimi  elif "__arm__" in directives:
115*f5c631daSSadaf Ebrahimi    return "aarch32"
116*f5c631daSSadaf Ebrahimi  elif "__aarch64__" in directives:
117*f5c631daSSadaf Ebrahimi    return "aarch64"
118*f5c631daSSadaf Ebrahimi  else:
119*f5c631daSSadaf Ebrahimi    raise Exception("Unsupported archtecture")
120*f5c631daSSadaf Ebrahimi
121*f5c631daSSadaf Ebrahimi# Class representing the compiler toolchain and version.
122*f5c631daSSadaf Ebrahimiclass CompilerInformation(object):
123*f5c631daSSadaf Ebrahimi  def __init__(self, env):
124*f5c631daSSadaf Ebrahimi    directives = GetCompilerDirectives(env)
125*f5c631daSSadaf Ebrahimi    if '__llvm__' in directives:
126*f5c631daSSadaf Ebrahimi      major = int(directives['__clang_major__'])
127*f5c631daSSadaf Ebrahimi      minor = int(directives['__clang_minor__'])
128*f5c631daSSadaf Ebrahimi      self.compiler = 'clang'
129*f5c631daSSadaf Ebrahimi      self.version = '{}.{}'.format(major, minor)
130*f5c631daSSadaf Ebrahimi    elif '__GNUC__' in directives:
131*f5c631daSSadaf Ebrahimi      major = int(directives['__GNUC__'])
132*f5c631daSSadaf Ebrahimi      minor = int(directives['__GNUC_MINOR__'])
133*f5c631daSSadaf Ebrahimi      self.compiler = 'gcc'
134*f5c631daSSadaf Ebrahimi      self.version = '{}.{}'.format(major, minor)
135*f5c631daSSadaf Ebrahimi    else:
136*f5c631daSSadaf Ebrahimi      # Allow other compilers to be used for building VIXL. However, one would
137*f5c631daSSadaf Ebrahimi      # need to teach this class how to extract version information in order to
138*f5c631daSSadaf Ebrahimi      # make use of it.
139*f5c631daSSadaf Ebrahimi      self.compiler = None
140*f5c631daSSadaf Ebrahimi      self.version = None
141*f5c631daSSadaf Ebrahimi
142*f5c631daSSadaf Ebrahimi  def GetDescription(self):
143*f5c631daSSadaf Ebrahimi    return "{}-{}".format(self.compiler, self.version)
144*f5c631daSSadaf Ebrahimi
145*f5c631daSSadaf Ebrahimi  def __str__(self):
146*f5c631daSSadaf Ebrahimi    return self.GetDescription()
147*f5c631daSSadaf Ebrahimi
148*f5c631daSSadaf Ebrahimi  # Compare string descriptions with our object. The semantics are:
149*f5c631daSSadaf Ebrahimi  #
150*f5c631daSSadaf Ebrahimi  # - Equality
151*f5c631daSSadaf Ebrahimi  #
152*f5c631daSSadaf Ebrahimi  #   If the description does not have a version number, then we compare the
153*f5c631daSSadaf Ebrahimi  #   compiler names. For instance, "clang-3.6" is still equal to "clang" but of
154*f5c631daSSadaf Ebrahimi  #   course is not to "gcc".
155*f5c631daSSadaf Ebrahimi  #
156*f5c631daSSadaf Ebrahimi  # - Ordering
157*f5c631daSSadaf Ebrahimi  #
158*f5c631daSSadaf Ebrahimi  #   Asking whether a compiler is lower than another depends on the version
159*f5c631daSSadaf Ebrahimi  #   number. What we are really asking here when using these operator is
160*f5c631daSSadaf Ebrahimi  #   "Is my compiler in the allowed range?". So with this in mind, comparing
161*f5c631daSSadaf Ebrahimi  #   two different compilers will always return false. If the compilers are the
162*f5c631daSSadaf Ebrahimi  #   same, then the version numbers are compared. Of course, we cannot use
163*f5c631daSSadaf Ebrahimi  #   ordering operators if no version number is provided.
164*f5c631daSSadaf Ebrahimi
165*f5c631daSSadaf Ebrahimi  def __eq__(self, description):
166*f5c631daSSadaf Ebrahimi    if description == self.GetDescription():
167*f5c631daSSadaf Ebrahimi      return True
168*f5c631daSSadaf Ebrahimi    else:
169*f5c631daSSadaf Ebrahimi      # The user may not have provided a version, let's see if it matches the
170*f5c631daSSadaf Ebrahimi      # compiler.
171*f5c631daSSadaf Ebrahimi      return self.compiler == description
172*f5c631daSSadaf Ebrahimi
173*f5c631daSSadaf Ebrahimi  def __ne__(self, description):
174*f5c631daSSadaf Ebrahimi    return not self.__eq__(description)
175*f5c631daSSadaf Ebrahimi
176*f5c631daSSadaf Ebrahimi  def __lt__(self, description):
177*f5c631daSSadaf Ebrahimi    return self.CompareVersion(operator.lt, description)
178*f5c631daSSadaf Ebrahimi
179*f5c631daSSadaf Ebrahimi  def __le__(self, description):
180*f5c631daSSadaf Ebrahimi    return self.CompareVersion(operator.le, description)
181*f5c631daSSadaf Ebrahimi
182*f5c631daSSadaf Ebrahimi  def __ge__(self, description):
183*f5c631daSSadaf Ebrahimi    return self.CompareVersion(operator.ge, description)
184*f5c631daSSadaf Ebrahimi
185*f5c631daSSadaf Ebrahimi  def __gt__(self, description):
186*f5c631daSSadaf Ebrahimi    return self.CompareVersion(operator.gt, description)
187*f5c631daSSadaf Ebrahimi
188*f5c631daSSadaf Ebrahimi  # Comparing the provided `description` string, in the form of
189*f5c631daSSadaf Ebrahimi  # "{compiler}-{major}.{minor}". The comparison is done using the provided
190*f5c631daSSadaf Ebrahimi  # `operator` argument.
191*f5c631daSSadaf Ebrahimi  def CompareVersion(self, operator, description):
192*f5c631daSSadaf Ebrahimi    match = re.search('^(\S+)-(.*?)$', description)
193*f5c631daSSadaf Ebrahimi    if not match:
194*f5c631daSSadaf Ebrahimi      raise Exception("A version number is required when comparing compilers")
195*f5c631daSSadaf Ebrahimi    compiler, version = match.group(1), match.group(2)
196*f5c631daSSadaf Ebrahimi    # The result is false if the compilers are different, otherwise compare the
197*f5c631daSSadaf Ebrahimi    # version numbers.
198*f5c631daSSadaf Ebrahimi    return self.compiler == compiler and \
199*f5c631daSSadaf Ebrahimi           operator(LooseVersion(self.version), LooseVersion(version))
200*f5c631daSSadaf Ebrahimi
201*f5c631daSSadaf Ebrahimiclass ReturnCode:
202*f5c631daSSadaf Ebrahimi  def __init__(self, exit_on_error, printer_fn):
203*f5c631daSSadaf Ebrahimi    self.rc = 0
204*f5c631daSSadaf Ebrahimi    self.exit_on_error = exit_on_error
205*f5c631daSSadaf Ebrahimi    self.printer_fn = printer_fn
206*f5c631daSSadaf Ebrahimi
207*f5c631daSSadaf Ebrahimi  def Combine(self, rc):
208*f5c631daSSadaf Ebrahimi    self.rc |= rc
209*f5c631daSSadaf Ebrahimi    if self.exit_on_error and rc != 0:
210*f5c631daSSadaf Ebrahimi      self.PrintStatus()
211*f5c631daSSadaf Ebrahimi      sys.exit(rc)
212*f5c631daSSadaf Ebrahimi
213*f5c631daSSadaf Ebrahimi  @property
214*f5c631daSSadaf Ebrahimi  def Value(self):
215*f5c631daSSadaf Ebrahimi    return self.rc
216*f5c631daSSadaf Ebrahimi
217*f5c631daSSadaf Ebrahimi  def PrintStatus(self):
218*f5c631daSSadaf Ebrahimi    self.printer_fn('\n$ ' + ' '.join(sys.argv))
219*f5c631daSSadaf Ebrahimi    if self.rc == 0:
220*f5c631daSSadaf Ebrahimi      self.printer_fn('SUCCESS')
221*f5c631daSSadaf Ebrahimi    else:
222*f5c631daSSadaf Ebrahimi      self.printer_fn('FAILURE')
223*f5c631daSSadaf Ebrahimi
224*f5c631daSSadaf Ebrahimi# Return a list of files whose name matches at least one `include` pattern, and
225*f5c631daSSadaf Ebrahimi# no `exclude` patterns, and whose directory (relative to the repository base)
226*f5c631daSSadaf Ebrahimi# matches at least one `include_dirs` and no `exclude_dirs` patterns.
227*f5c631daSSadaf Ebrahimi#
228*f5c631daSSadaf Ebrahimi# For directory matches, leading and trailing slashes are added first (so that
229*f5c631daSSadaf Ebrahimi# "*/foo/*" matches all of 'foo/bar', 'bar/foo' and 'bar/foo/bar').
230*f5c631daSSadaf Ebrahimidef get_source_files(
231*f5c631daSSadaf Ebrahimi    include = ['*.h', '*.cc'],
232*f5c631daSSadaf Ebrahimi    include_dirs = ['/src/*', '/test/*', '/examples/*', '/benchmarks/*'],
233*f5c631daSSadaf Ebrahimi    exclude = [],
234*f5c631daSSadaf Ebrahimi    exclude_dirs = ['.*', '*/traces/*']):
235*f5c631daSSadaf Ebrahimi  def NameMatchesAnyFilter(name, filters):
236*f5c631daSSadaf Ebrahimi    for f in filters:
237*f5c631daSSadaf Ebrahimi      if fnmatch.fnmatch(name, f):
238*f5c631daSSadaf Ebrahimi        return True
239*f5c631daSSadaf Ebrahimi    return False
240*f5c631daSSadaf Ebrahimi
241*f5c631daSSadaf Ebrahimi  files_found = []
242*f5c631daSSadaf Ebrahimi  for root, dirs, files in os.walk(config.dir_root):
243*f5c631daSSadaf Ebrahimi    git_path = os.path.join('/', os.path.relpath(root, config.dir_root), '')
244*f5c631daSSadaf Ebrahimi    if NameMatchesAnyFilter(git_path, include_dirs) and \
245*f5c631daSSadaf Ebrahimi        not NameMatchesAnyFilter(git_path, exclude_dirs):
246*f5c631daSSadaf Ebrahimi      files_found += [
247*f5c631daSSadaf Ebrahimi        os.path.join(root, name)
248*f5c631daSSadaf Ebrahimi        for name in files
249*f5c631daSSadaf Ebrahimi        if NameMatchesAnyFilter(name, include) and \
250*f5c631daSSadaf Ebrahimi            not NameMatchesAnyFilter(name, exclude)
251*f5c631daSSadaf Ebrahimi      ]
252*f5c631daSSadaf Ebrahimi  return files_found
253