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 src_files = glob.glob(os.path.join(path, '*.cc')) 41 return [os.path.splitext(os.path.basename(x))[0] for x in src_files] 42 43 44def abort(message): 45 print('ABORTING: ' + message) 46 sys.exit(1) 47 48 49def getstatusoutput(command): 50 return subprocess.getstatusoutput(command) 51 52 53def IsCommandAvailable(command): 54 retcode, unused_output = getstatusoutput('which %s' % command) 55 return retcode == 0 56 57 58def ensure_dir(path_name): 59 if not os.path.exists(path_name): 60 os.makedirs(path_name) 61 62 63# Check that the specified program is available. 64def require_program(program_name): 65 rc, out = getstatusoutput('which %s' % program_name) 66 if rc != 0: 67 print('ERROR: The required program %s was not found.' % program_name) 68 sys.exit(rc) 69 70def relrealpath(path, start=os.getcwd()): 71 return os.path.relpath(os.path.realpath(path), start) 72 73# Query the compiler about its preprocessor directives and return all of them as 74# a dictionary. 75def GetCompilerDirectives(env): 76 args = [env['compiler']] 77 # Pass the CXXFLAGS variables to the compile, in case we've used "-m32" to 78 # compile for i386. 79 if env['CXXFLAGS']: 80 args.append(str(env['CXXFLAGS'])) 81 args += ['-E', '-dM', '-'] 82 83 # Instruct the compiler to dump all its preprocessor macros. 84 dump = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, 85 universal_newlines=True) 86 out, _ = dump.communicate() 87 return { 88 # Extract the macro name as key and value as element. 89 match.group(1): match.group(2) 90 for match in [ 91 # Capture macro name. 92 re.search('^#define (\S+?) (.+)$', macro) 93 for macro in out.split('\n') 94 ] 95 # Filter out non-matches. 96 if match 97 } 98 99# Query the target architecture of the compiler. The 'target' architecture of 100# the compiler used to build VIXL is considered to be the 'host' architecture of 101# VIXL itself. 102def GetHostArch(env): 103 directives = GetCompilerDirectives(env) 104 if "__x86_64__" in directives: 105 return "x86_64" 106 elif "__i386__" in directives: 107 return "i386" 108 elif "__arm__" in directives: 109 return "aarch32" 110 elif "__aarch64__" in directives: 111 return "aarch64" 112 else: 113 raise Exception("Unsupported architecture") 114 115# Class representing the compiler toolchain and version. 116class CompilerInformation(object): 117 def __init__(self, env): 118 directives = GetCompilerDirectives(env) 119 if '__llvm__' in directives: 120 major = int(directives['__clang_major__']) 121 minor = int(directives['__clang_minor__']) 122 self.compiler = 'clang' 123 self.version = '{}.{}'.format(major, minor) 124 elif '__GNUC__' in directives: 125 major = int(directives['__GNUC__']) 126 minor = int(directives['__GNUC_MINOR__']) 127 self.compiler = 'gcc' 128 self.version = '{}.{}'.format(major, minor) 129 else: 130 # Allow other compilers to be used for building VIXL. However, one would 131 # need to teach this class how to extract version information in order to 132 # make use of it. 133 self.compiler = None 134 self.version = None 135 136 def GetDescription(self): 137 return "{}-{}".format(self.compiler, self.version) 138 139 def __str__(self): 140 return self.GetDescription() 141 142 # Compare string descriptions with our object. The semantics are: 143 # 144 # - Equality 145 # 146 # If the description does not have a version number, then we compare the 147 # compiler names. For instance, "clang-3.6" is still equal to "clang" but of 148 # course is not to "gcc". 149 # 150 # - Ordering 151 # 152 # Asking whether a compiler is lower than another depends on the version 153 # number. What we are really asking here when using these operator is 154 # "Is my compiler in the allowed range?". So with this in mind, comparing 155 # two different compilers will always return false. If the compilers are the 156 # same, then the version numbers are compared. Of course, we cannot use 157 # ordering operators if no version number is provided. 158 159 def __eq__(self, description): 160 if description == self.GetDescription(): 161 return True 162 else: 163 # The user may not have provided a version, let's see if it matches the 164 # compiler. 165 return self.compiler == description 166 167 def __ne__(self, description): 168 return not self.__eq__(description) 169 170 def __lt__(self, description): 171 return self.CompareVersion(operator.lt, description) 172 173 def __le__(self, description): 174 return self.CompareVersion(operator.le, description) 175 176 def __ge__(self, description): 177 return self.CompareVersion(operator.ge, description) 178 179 def __gt__(self, description): 180 return self.CompareVersion(operator.gt, description) 181 182 # Comparing the provided `description` string, in the form of 183 # "{compiler}-{major}.{minor}". The comparison is done using the provided 184 # `operator` argument. 185 def CompareVersion(self, operator, description): 186 match = re.search('^(\S+)-(.*?)$', description) 187 if not match: 188 raise Exception("A version number is required when comparing compilers") 189 compiler, version = match.group(1), match.group(2) 190 # The result is false if the compilers are different, otherwise compare the 191 # version numbers. 192 return self.compiler == compiler and \ 193 operator(LooseVersion(self.version), LooseVersion(version)) 194 195class ReturnCode: 196 def __init__(self, exit_on_error, printer_fn): 197 self.rc = 0 198 self.exit_on_error = exit_on_error 199 self.printer_fn = printer_fn 200 201 def Combine(self, rc): 202 self.rc |= rc 203 if self.exit_on_error and rc != 0: 204 self.PrintStatus() 205 sys.exit(rc) 206 207 @property 208 def Value(self): 209 return self.rc 210 211 def PrintStatus(self): 212 self.printer_fn('\n$ ' + ' '.join(sys.argv)) 213 if self.rc == 0: 214 self.printer_fn('SUCCESS') 215 else: 216 self.printer_fn('FAILURE') 217 218# Return a list of files whose name matches at least one `include` pattern, and 219# no `exclude` patterns, and whose directory (relative to the repository base) 220# matches at least one `include_dirs` and no `exclude_dirs` patterns. 221# 222# For directory matches, leading and trailing slashes are added first (so that 223# "*/foo/*" matches all of 'foo/bar', 'bar/foo' and 'bar/foo/bar'). 224def get_source_files( 225 include = ['*.h', '*.cc'], 226 include_dirs = ['/src/*', '/test/*', '/examples/*', '/benchmarks/*'], 227 exclude = [], 228 exclude_dirs = ['.*', '*/traces/*']): 229 def NameMatchesAnyFilter(name, filters): 230 for f in filters: 231 if fnmatch.fnmatch(name, f): 232 return True 233 return False 234 235 files_found = [] 236 for root, dirs, files in os.walk(config.dir_root): 237 git_path = os.path.join('/', os.path.relpath(root, config.dir_root), '') 238 if NameMatchesAnyFilter(git_path, include_dirs) and \ 239 not NameMatchesAnyFilter(git_path, exclude_dirs): 240 files_found += [ 241 os.path.join(root, name) 242 for name in files 243 if NameMatchesAnyFilter(name, include) and \ 244 not NameMatchesAnyFilter(name, exclude) 245 ] 246 return files_found 247