Home | History | Annotate | Download | only in tools
      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 
     27 from distutils.version import LooseVersion
     28 import glob
     29 import operator
     30 import os
     31 import re
     32 import shlex
     33 import subprocess
     34 import sys
     35 
     36 
     37 def 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 
     42 def abort(message):
     43   print('ABORTING: ' + message)
     44   sys.exit(1)
     45 
     46 
     47 # Emulate python3 subprocess.getstatusoutput.
     48 def 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 
     57 def IsCommandAvailable(command):
     58     retcode, unused_output = getstatusoutput('which %s' % command)
     59     return retcode == 0
     60 
     61 
     62 def 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.
     68 def 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 
     74 def 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.
     79 def 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.
    105 def 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.
    119 class 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