• 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  out, _ = dump.communicate()
92  return {
93    # Extract the macro name as key and value as element.
94    match.group(1): match.group(2)
95    for match in [
96      # Capture macro name.
97      re.search('^#define (\S+?) (.+)$', macro)
98      for macro in out.split('\n')
99    ]
100    # Filter out non-matches.
101    if match
102  }
103
104# Query the target architecture of the compiler. The 'target' architecture of
105# the compiler used to build VIXL is considered to be the 'host' architecture of
106# VIXL itself.
107def GetHostArch(env):
108  directives = GetCompilerDirectives(env)
109  if "__x86_64__" in directives:
110    return "x86_64"
111  elif "__i386__" in directives:
112    return "i386"
113  elif "__arm__" in directives:
114    return "aarch32"
115  elif "__aarch64__" in directives:
116    return "aarch64"
117  else:
118    raise Exception("Unsupported archtecture")
119
120# Class representing the compiler toolchain and version.
121class CompilerInformation(object):
122  def __init__(self, env):
123    directives = GetCompilerDirectives(env)
124    if '__llvm__' in directives:
125      major = int(directives['__clang_major__'])
126      minor = int(directives['__clang_minor__'])
127      self.compiler = 'clang'
128      self.version = '{}.{}'.format(major, minor)
129    elif '__GNUC__' in directives:
130      major = int(directives['__GNUC__'])
131      minor = int(directives['__GNUC_MINOR__'])
132      self.compiler = 'gcc'
133      self.version = '{}.{}'.format(major, minor)
134    else:
135      # Allow other compilers to be used for building VIXL. However, one would
136      # need to teach this class how to extract version information in order to
137      # make use of it.
138      self.compiler = None
139      self.version = None
140
141  def GetDescription(self):
142    return "{}-{}".format(self.compiler, self.version)
143
144  def __str__(self):
145    return self.GetDescription()
146
147  # Compare string descriptions with our object. The semantics are:
148  #
149  # - Equality
150  #
151  #   If the description does not have a version number, then we compare the
152  #   compiler names. For instance, "clang-3.6" is still equal to "clang" but of
153  #   course is not to "gcc".
154  #
155  # - Ordering
156  #
157  #   Asking whether a compiler is lower than another depends on the version
158  #   number. What we are really asking here when using these operator is
159  #   "Is my compiler in the allowed range?". So with this in mind, comparing
160  #   two different compilers will always return false. If the compilers are the
161  #   same, then the version numbers are compared. Of course, we cannot use
162  #   ordering operators if no version number is provided.
163
164  def __eq__(self, description):
165    if description == self.GetDescription():
166      return True
167    else:
168      # The user may not have provided a version, let's see if it matches the
169      # compiler.
170      return self.compiler == description
171
172  def __ne__(self, description):
173    return not self.__eq__(description)
174
175  def __lt__(self, description):
176    return self.CompareVersion(operator.lt, description)
177
178  def __le__(self, description):
179    return self.CompareVersion(operator.le, description)
180
181  def __ge__(self, description):
182    return self.CompareVersion(operator.ge, description)
183
184  def __gt__(self, description):
185    return self.CompareVersion(operator.gt, description)
186
187  # Comparing the provided `description` string, in the form of
188  # "{compiler}-{major}.{minor}". The comparison is done using the provided
189  # `operator` argument.
190  def CompareVersion(self, operator, description):
191    match = re.search('^(\S+)-(.*?)$', description)
192    if not match:
193      raise Exception("A version number is required when comparing compilers")
194    compiler, version = match.group(1), match.group(2)
195    # The result is false if the compilers are different, otherwise compare the
196    # version numbers.
197    return self.compiler == compiler and \
198           operator(LooseVersion(self.version), LooseVersion(version))
199
200class ReturnCode:
201  def __init__(self, exit_on_error, printer_fn):
202    self.rc = 0
203    self.exit_on_error = exit_on_error
204    self.printer_fn = printer_fn
205
206  def Combine(self, rc):
207    self.rc |= rc
208    if self.exit_on_error and rc != 0:
209      self.PrintStatus()
210      sys.exit(rc)
211
212  @property
213  def Value(self):
214    return self.rc
215
216  def PrintStatus(self):
217    self.printer_fn('\n$ ' + ' '.join(sys.argv))
218    if self.rc == 0:
219      self.printer_fn('SUCCESS')
220    else:
221      self.printer_fn('FAILURE')
222
223# Return a list of files whose name matches at least one `include` pattern, and
224# no `exclude` patterns, and whose directory (relative to the repository base)
225# matches at least one `include_dirs` and no `exclude_dirs` patterns.
226#
227# For directory matches, leading and trailing slashes are added first (so that
228# "*/foo/*" matches all of 'foo/bar', 'bar/foo' and 'bar/foo/bar').
229def get_source_files(
230    include = ['*.h', '*.cc'],
231    include_dirs = ['/src/*', '/test/*', '/examples/*', '/benchmarks/*'],
232    exclude = [],
233    exclude_dirs = ['.*', '*/traces/*']):
234  def NameMatchesAnyFilter(name, filters):
235    for f in filters:
236      if fnmatch.fnmatch(name, f):
237        return True
238    return False
239
240  files_found = []
241  for root, dirs, files in os.walk(config.dir_root):
242    git_path = os.path.join('/', os.path.relpath(root, config.dir_root), '')
243    if NameMatchesAnyFilter(git_path, include_dirs) and \
244        not NameMatchesAnyFilter(git_path, exclude_dirs):
245      files_found += [
246        os.path.join(root, name)
247        for name in files
248        if NameMatchesAnyFilter(name, include) and \
249            not NameMatchesAnyFilter(name, exclude)
250      ]
251  return files_found
252