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