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