• 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 glob
29import operator
30import os
31import re
32import shlex
33import subprocess
34import sys
35
36
37def ListCCFilesWithoutExt(path):
38  return map(lambda x : os.path.splitext(os.path.basename(x))[0],
39             glob.glob(os.path.join(path, '*.cc')))
40
41
42def abort(message):
43  print('ABORTING: ' + message)
44  sys.exit(1)
45
46
47# Emulate python3 subprocess.getstatusoutput.
48def getstatusoutput(command, shell=False):
49  try:
50    args = shlex.split(command)
51    output = subprocess.check_output(args, stderr=subprocess.STDOUT, shell=shell)
52    return 0, output.rstrip('\n')
53  except subprocess.CalledProcessError as e:
54    return e.returncode, e.output.rstrip('\n')
55
56
57def IsCommandAvailable(command):
58    retcode, unused_output = getstatusoutput('which %s' % command)
59    return retcode == 0
60
61
62def ensure_dir(path_name):
63  if not os.path.exists(path_name):
64    os.makedirs(path_name)
65
66
67# Check that the specified program is available.
68def require_program(program_name):
69  rc, out = getstatusoutput('which %s' % program_name)
70  if rc != 0:
71    print('ERROR: The required program %s was not found.' % program_name)
72    sys.exit(rc)
73
74def relrealpath(path, start=os.getcwd()):
75  return os.path.relpath(os.path.realpath(path), start)
76
77# Query the compiler about its preprocessor directives and return all of them as
78# a dictionnary.
79def GetCompilerDirectives(env):
80  args = [env['CXX']]
81  # Pass the CXXFLAGS varables to the compile, in case we've used "-m32" to
82  # compile for i386.
83  if env['CXXFLAGS']:
84    args.append(str(env['CXXFLAGS']))
85  args += ['-E', '-dM', '-']
86
87  # Instruct the compiler to dump all its preprocessor macros.
88  dump = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
89  out, _ = dump.communicate()
90  return {
91    # Extract the macro name as key and value as element.
92    match.group(1): match.group(2)
93    for match in [
94      # Capture macro name.
95      re.search('^#define (\S+?) (.+)$', macro)
96      for macro in out.split('\n')
97    ]
98    # Filter out non-matches.
99    if match
100  }
101
102# Query the target architecture of the compiler. The 'target' architecture of
103# the compiler used to build VIXL is considered to be the 'host' architecture of
104# VIXL itself.
105def GetHostArch(env):
106  directives = GetCompilerDirectives(env)
107  if "__x86_64__" in directives:
108    return "x86_64"
109  elif "__i386__" in directives:
110    return "i386"
111  elif "__arm__" in directives:
112    return "aarch32"
113  elif "__aarch64__" in directives:
114    return "aarch64"
115  else:
116    raise Exception("Unsupported archtecture")
117
118# Class representing the compiler toolchain and version.
119class CompilerInformation(object):
120  def __init__(self, env):
121    directives = GetCompilerDirectives(env)
122    if '__llvm__' in directives:
123      major = int(directives['__clang_major__'])
124      minor = int(directives['__clang_minor__'])
125      self.compiler = 'clang'
126      self.version = '{}.{}'.format(major, minor)
127    elif '__GNUC__' in directives:
128      major = int(directives['__GNUC__'])
129      minor = int(directives['__GNUC_MINOR__'])
130      self.compiler = 'gcc'
131      self.version = '{}.{}'.format(major, minor)
132    else:
133      # Allow other compilers to be used for building VIXL. However, one would
134      # need to teach this class how to extract version information in order to
135      # make use of it.
136      self.compiler = None
137      self.version = None
138
139  def GetDescription(self):
140    return "{}-{}".format(self.compiler, self.version)
141
142  def __str__(self):
143    return self.GetDescription()
144
145  # Compare string descriptions with our object. The semantics are:
146  #
147  # - Equality
148  #
149  #   If the description does not have a version number, then we compare the
150  #   compiler names. For instance, "clang-3.6" is still equal to "clang" but of
151  #   course is not to "gcc".
152  #
153  # - Ordering
154  #
155  #   Asking whether a compiler is lower than another depends on the version
156  #   number. What we are really asking here when using these operator is
157  #   "Is my compiler in the allowed range?". So with this in mind, comparing
158  #   two different compilers will always return false. If the compilers are the
159  #   same, then the version numbers are compared. Of course, we cannot use
160  #   ordering operators if no version number is provided.
161
162  def __eq__(self, description):
163    if description == self.GetDescription():
164      return True
165    else:
166      # The user may not have provided a version, let's see if it matches the
167      # compiler.
168      return self.compiler == description
169
170  def __ne__(self, description):
171    return not self.__eq__(description)
172
173  def __lt__(self, description):
174    return self.CompareVersion(operator.lt, description)
175
176  def __le__(self, description):
177    return self.CompareVersion(operator.le, description)
178
179  def __ge__(self, description):
180    return self.CompareVersion(operator.ge, description)
181
182  def __gt__(self, description):
183    return self.CompareVersion(operator.gt, description)
184
185  # Comparing the provided `description` string, in the form of
186  # "{compiler}-{major}.{minor}". The comparison is done using the provided
187  # `operator` argument.
188  def CompareVersion(self, operator, description):
189    match = re.search('^(\S+)-(.*?)$', description)
190    if not match:
191      raise Exception("A version number is required when comparing compilers")
192    compiler, version = match.group(1), match.group(2)
193    # The result is false if the compilers are different, otherwise compare the
194    # version numbers.
195    return self.compiler == compiler and \
196           operator(LooseVersion(self.version), LooseVersion(version))
197