1*f5c631daSSadaf Ebrahimi# Copyright 2015, VIXL authors 2*f5c631daSSadaf Ebrahimi# All rights reserved. 3*f5c631daSSadaf Ebrahimi# 4*f5c631daSSadaf Ebrahimi# Redistribution and use in source and binary forms, with or without 5*f5c631daSSadaf Ebrahimi# modification, are permitted provided that the following conditions are met: 6*f5c631daSSadaf Ebrahimi# 7*f5c631daSSadaf Ebrahimi# * Redistributions of source code must retain the above copyright notice, 8*f5c631daSSadaf Ebrahimi# this list of conditions and the following disclaimer. 9*f5c631daSSadaf Ebrahimi# * Redistributions in binary form must reproduce the above copyright notice, 10*f5c631daSSadaf Ebrahimi# this list of conditions and the following disclaimer in the documentation 11*f5c631daSSadaf Ebrahimi# and/or other materials provided with the distribution. 12*f5c631daSSadaf Ebrahimi# * Neither the name of ARM Limited nor the names of its contributors may be 13*f5c631daSSadaf Ebrahimi# used to endorse or promote products derived from this software without 14*f5c631daSSadaf Ebrahimi# specific prior written permission. 15*f5c631daSSadaf Ebrahimi# 16*f5c631daSSadaf Ebrahimi# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 17*f5c631daSSadaf Ebrahimi# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18*f5c631daSSadaf Ebrahimi# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19*f5c631daSSadaf Ebrahimi# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20*f5c631daSSadaf Ebrahimi# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21*f5c631daSSadaf Ebrahimi# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22*f5c631daSSadaf Ebrahimi# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23*f5c631daSSadaf Ebrahimi# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24*f5c631daSSadaf Ebrahimi# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25*f5c631daSSadaf Ebrahimi# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26*f5c631daSSadaf Ebrahimi 27*f5c631daSSadaf Ebrahimifrom distutils.version import LooseVersion 28*f5c631daSSadaf Ebrahimiimport config 29*f5c631daSSadaf Ebrahimiimport fnmatch 30*f5c631daSSadaf Ebrahimiimport glob 31*f5c631daSSadaf Ebrahimiimport operator 32*f5c631daSSadaf Ebrahimiimport os 33*f5c631daSSadaf Ebrahimiimport re 34*f5c631daSSadaf Ebrahimiimport shlex 35*f5c631daSSadaf Ebrahimiimport subprocess 36*f5c631daSSadaf Ebrahimiimport sys 37*f5c631daSSadaf Ebrahimi 38*f5c631daSSadaf Ebrahimi 39*f5c631daSSadaf Ebrahimidef ListCCFilesWithoutExt(path): 40*f5c631daSSadaf Ebrahimi return map(lambda x : os.path.splitext(os.path.basename(x))[0], 41*f5c631daSSadaf Ebrahimi glob.glob(os.path.join(path, '*.cc'))) 42*f5c631daSSadaf Ebrahimi 43*f5c631daSSadaf Ebrahimi 44*f5c631daSSadaf Ebrahimidef abort(message): 45*f5c631daSSadaf Ebrahimi print('ABORTING: ' + message) 46*f5c631daSSadaf Ebrahimi sys.exit(1) 47*f5c631daSSadaf Ebrahimi 48*f5c631daSSadaf Ebrahimi 49*f5c631daSSadaf Ebrahimi# Emulate python3 subprocess.getstatusoutput. 50*f5c631daSSadaf Ebrahimidef getstatusoutput(command): 51*f5c631daSSadaf Ebrahimi try: 52*f5c631daSSadaf Ebrahimi args = shlex.split(command) 53*f5c631daSSadaf Ebrahimi output = subprocess.check_output(args, stderr=subprocess.STDOUT) 54*f5c631daSSadaf Ebrahimi return 0, output.rstrip('\n') 55*f5c631daSSadaf Ebrahimi except subprocess.CalledProcessError as e: 56*f5c631daSSadaf Ebrahimi return e.returncode, e.output.rstrip('\n') 57*f5c631daSSadaf Ebrahimi 58*f5c631daSSadaf Ebrahimi 59*f5c631daSSadaf Ebrahimidef IsCommandAvailable(command): 60*f5c631daSSadaf Ebrahimi retcode, unused_output = getstatusoutput('which %s' % command) 61*f5c631daSSadaf Ebrahimi return retcode == 0 62*f5c631daSSadaf Ebrahimi 63*f5c631daSSadaf Ebrahimi 64*f5c631daSSadaf Ebrahimidef ensure_dir(path_name): 65*f5c631daSSadaf Ebrahimi if not os.path.exists(path_name): 66*f5c631daSSadaf Ebrahimi os.makedirs(path_name) 67*f5c631daSSadaf Ebrahimi 68*f5c631daSSadaf Ebrahimi 69*f5c631daSSadaf Ebrahimi# Check that the specified program is available. 70*f5c631daSSadaf Ebrahimidef require_program(program_name): 71*f5c631daSSadaf Ebrahimi rc, out = getstatusoutput('which %s' % program_name) 72*f5c631daSSadaf Ebrahimi if rc != 0: 73*f5c631daSSadaf Ebrahimi print('ERROR: The required program %s was not found.' % program_name) 74*f5c631daSSadaf Ebrahimi sys.exit(rc) 75*f5c631daSSadaf Ebrahimi 76*f5c631daSSadaf Ebrahimidef relrealpath(path, start=os.getcwd()): 77*f5c631daSSadaf Ebrahimi return os.path.relpath(os.path.realpath(path), start) 78*f5c631daSSadaf Ebrahimi 79*f5c631daSSadaf Ebrahimi# Query the compiler about its preprocessor directives and return all of them as 80*f5c631daSSadaf Ebrahimi# a dictionnary. 81*f5c631daSSadaf Ebrahimidef GetCompilerDirectives(env): 82*f5c631daSSadaf Ebrahimi args = [env['compiler']] 83*f5c631daSSadaf Ebrahimi # Pass the CXXFLAGS varables to the compile, in case we've used "-m32" to 84*f5c631daSSadaf Ebrahimi # compile for i386. 85*f5c631daSSadaf Ebrahimi if env['CXXFLAGS']: 86*f5c631daSSadaf Ebrahimi args.append(str(env['CXXFLAGS'])) 87*f5c631daSSadaf Ebrahimi args += ['-E', '-dM', '-'] 88*f5c631daSSadaf Ebrahimi 89*f5c631daSSadaf Ebrahimi # Instruct the compiler to dump all its preprocessor macros. 90*f5c631daSSadaf Ebrahimi dump = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, 91*f5c631daSSadaf Ebrahimi universal_newlines=True) 92*f5c631daSSadaf Ebrahimi out, _ = dump.communicate() 93*f5c631daSSadaf Ebrahimi return { 94*f5c631daSSadaf Ebrahimi # Extract the macro name as key and value as element. 95*f5c631daSSadaf Ebrahimi match.group(1): match.group(2) 96*f5c631daSSadaf Ebrahimi for match in [ 97*f5c631daSSadaf Ebrahimi # Capture macro name. 98*f5c631daSSadaf Ebrahimi re.search('^#define (\S+?) (.+)$', macro) 99*f5c631daSSadaf Ebrahimi for macro in out.split('\n') 100*f5c631daSSadaf Ebrahimi ] 101*f5c631daSSadaf Ebrahimi # Filter out non-matches. 102*f5c631daSSadaf Ebrahimi if match 103*f5c631daSSadaf Ebrahimi } 104*f5c631daSSadaf Ebrahimi 105*f5c631daSSadaf Ebrahimi# Query the target architecture of the compiler. The 'target' architecture of 106*f5c631daSSadaf Ebrahimi# the compiler used to build VIXL is considered to be the 'host' architecture of 107*f5c631daSSadaf Ebrahimi# VIXL itself. 108*f5c631daSSadaf Ebrahimidef GetHostArch(env): 109*f5c631daSSadaf Ebrahimi directives = GetCompilerDirectives(env) 110*f5c631daSSadaf Ebrahimi if "__x86_64__" in directives: 111*f5c631daSSadaf Ebrahimi return "x86_64" 112*f5c631daSSadaf Ebrahimi elif "__i386__" in directives: 113*f5c631daSSadaf Ebrahimi return "i386" 114*f5c631daSSadaf Ebrahimi elif "__arm__" in directives: 115*f5c631daSSadaf Ebrahimi return "aarch32" 116*f5c631daSSadaf Ebrahimi elif "__aarch64__" in directives: 117*f5c631daSSadaf Ebrahimi return "aarch64" 118*f5c631daSSadaf Ebrahimi else: 119*f5c631daSSadaf Ebrahimi raise Exception("Unsupported archtecture") 120*f5c631daSSadaf Ebrahimi 121*f5c631daSSadaf Ebrahimi# Class representing the compiler toolchain and version. 122*f5c631daSSadaf Ebrahimiclass CompilerInformation(object): 123*f5c631daSSadaf Ebrahimi def __init__(self, env): 124*f5c631daSSadaf Ebrahimi directives = GetCompilerDirectives(env) 125*f5c631daSSadaf Ebrahimi if '__llvm__' in directives: 126*f5c631daSSadaf Ebrahimi major = int(directives['__clang_major__']) 127*f5c631daSSadaf Ebrahimi minor = int(directives['__clang_minor__']) 128*f5c631daSSadaf Ebrahimi self.compiler = 'clang' 129*f5c631daSSadaf Ebrahimi self.version = '{}.{}'.format(major, minor) 130*f5c631daSSadaf Ebrahimi elif '__GNUC__' in directives: 131*f5c631daSSadaf Ebrahimi major = int(directives['__GNUC__']) 132*f5c631daSSadaf Ebrahimi minor = int(directives['__GNUC_MINOR__']) 133*f5c631daSSadaf Ebrahimi self.compiler = 'gcc' 134*f5c631daSSadaf Ebrahimi self.version = '{}.{}'.format(major, minor) 135*f5c631daSSadaf Ebrahimi else: 136*f5c631daSSadaf Ebrahimi # Allow other compilers to be used for building VIXL. However, one would 137*f5c631daSSadaf Ebrahimi # need to teach this class how to extract version information in order to 138*f5c631daSSadaf Ebrahimi # make use of it. 139*f5c631daSSadaf Ebrahimi self.compiler = None 140*f5c631daSSadaf Ebrahimi self.version = None 141*f5c631daSSadaf Ebrahimi 142*f5c631daSSadaf Ebrahimi def GetDescription(self): 143*f5c631daSSadaf Ebrahimi return "{}-{}".format(self.compiler, self.version) 144*f5c631daSSadaf Ebrahimi 145*f5c631daSSadaf Ebrahimi def __str__(self): 146*f5c631daSSadaf Ebrahimi return self.GetDescription() 147*f5c631daSSadaf Ebrahimi 148*f5c631daSSadaf Ebrahimi # Compare string descriptions with our object. The semantics are: 149*f5c631daSSadaf Ebrahimi # 150*f5c631daSSadaf Ebrahimi # - Equality 151*f5c631daSSadaf Ebrahimi # 152*f5c631daSSadaf Ebrahimi # If the description does not have a version number, then we compare the 153*f5c631daSSadaf Ebrahimi # compiler names. For instance, "clang-3.6" is still equal to "clang" but of 154*f5c631daSSadaf Ebrahimi # course is not to "gcc". 155*f5c631daSSadaf Ebrahimi # 156*f5c631daSSadaf Ebrahimi # - Ordering 157*f5c631daSSadaf Ebrahimi # 158*f5c631daSSadaf Ebrahimi # Asking whether a compiler is lower than another depends on the version 159*f5c631daSSadaf Ebrahimi # number. What we are really asking here when using these operator is 160*f5c631daSSadaf Ebrahimi # "Is my compiler in the allowed range?". So with this in mind, comparing 161*f5c631daSSadaf Ebrahimi # two different compilers will always return false. If the compilers are the 162*f5c631daSSadaf Ebrahimi # same, then the version numbers are compared. Of course, we cannot use 163*f5c631daSSadaf Ebrahimi # ordering operators if no version number is provided. 164*f5c631daSSadaf Ebrahimi 165*f5c631daSSadaf Ebrahimi def __eq__(self, description): 166*f5c631daSSadaf Ebrahimi if description == self.GetDescription(): 167*f5c631daSSadaf Ebrahimi return True 168*f5c631daSSadaf Ebrahimi else: 169*f5c631daSSadaf Ebrahimi # The user may not have provided a version, let's see if it matches the 170*f5c631daSSadaf Ebrahimi # compiler. 171*f5c631daSSadaf Ebrahimi return self.compiler == description 172*f5c631daSSadaf Ebrahimi 173*f5c631daSSadaf Ebrahimi def __ne__(self, description): 174*f5c631daSSadaf Ebrahimi return not self.__eq__(description) 175*f5c631daSSadaf Ebrahimi 176*f5c631daSSadaf Ebrahimi def __lt__(self, description): 177*f5c631daSSadaf Ebrahimi return self.CompareVersion(operator.lt, description) 178*f5c631daSSadaf Ebrahimi 179*f5c631daSSadaf Ebrahimi def __le__(self, description): 180*f5c631daSSadaf Ebrahimi return self.CompareVersion(operator.le, description) 181*f5c631daSSadaf Ebrahimi 182*f5c631daSSadaf Ebrahimi def __ge__(self, description): 183*f5c631daSSadaf Ebrahimi return self.CompareVersion(operator.ge, description) 184*f5c631daSSadaf Ebrahimi 185*f5c631daSSadaf Ebrahimi def __gt__(self, description): 186*f5c631daSSadaf Ebrahimi return self.CompareVersion(operator.gt, description) 187*f5c631daSSadaf Ebrahimi 188*f5c631daSSadaf Ebrahimi # Comparing the provided `description` string, in the form of 189*f5c631daSSadaf Ebrahimi # "{compiler}-{major}.{minor}". The comparison is done using the provided 190*f5c631daSSadaf Ebrahimi # `operator` argument. 191*f5c631daSSadaf Ebrahimi def CompareVersion(self, operator, description): 192*f5c631daSSadaf Ebrahimi match = re.search('^(\S+)-(.*?)$', description) 193*f5c631daSSadaf Ebrahimi if not match: 194*f5c631daSSadaf Ebrahimi raise Exception("A version number is required when comparing compilers") 195*f5c631daSSadaf Ebrahimi compiler, version = match.group(1), match.group(2) 196*f5c631daSSadaf Ebrahimi # The result is false if the compilers are different, otherwise compare the 197*f5c631daSSadaf Ebrahimi # version numbers. 198*f5c631daSSadaf Ebrahimi return self.compiler == compiler and \ 199*f5c631daSSadaf Ebrahimi operator(LooseVersion(self.version), LooseVersion(version)) 200*f5c631daSSadaf Ebrahimi 201*f5c631daSSadaf Ebrahimiclass ReturnCode: 202*f5c631daSSadaf Ebrahimi def __init__(self, exit_on_error, printer_fn): 203*f5c631daSSadaf Ebrahimi self.rc = 0 204*f5c631daSSadaf Ebrahimi self.exit_on_error = exit_on_error 205*f5c631daSSadaf Ebrahimi self.printer_fn = printer_fn 206*f5c631daSSadaf Ebrahimi 207*f5c631daSSadaf Ebrahimi def Combine(self, rc): 208*f5c631daSSadaf Ebrahimi self.rc |= rc 209*f5c631daSSadaf Ebrahimi if self.exit_on_error and rc != 0: 210*f5c631daSSadaf Ebrahimi self.PrintStatus() 211*f5c631daSSadaf Ebrahimi sys.exit(rc) 212*f5c631daSSadaf Ebrahimi 213*f5c631daSSadaf Ebrahimi @property 214*f5c631daSSadaf Ebrahimi def Value(self): 215*f5c631daSSadaf Ebrahimi return self.rc 216*f5c631daSSadaf Ebrahimi 217*f5c631daSSadaf Ebrahimi def PrintStatus(self): 218*f5c631daSSadaf Ebrahimi self.printer_fn('\n$ ' + ' '.join(sys.argv)) 219*f5c631daSSadaf Ebrahimi if self.rc == 0: 220*f5c631daSSadaf Ebrahimi self.printer_fn('SUCCESS') 221*f5c631daSSadaf Ebrahimi else: 222*f5c631daSSadaf Ebrahimi self.printer_fn('FAILURE') 223*f5c631daSSadaf Ebrahimi 224*f5c631daSSadaf Ebrahimi# Return a list of files whose name matches at least one `include` pattern, and 225*f5c631daSSadaf Ebrahimi# no `exclude` patterns, and whose directory (relative to the repository base) 226*f5c631daSSadaf Ebrahimi# matches at least one `include_dirs` and no `exclude_dirs` patterns. 227*f5c631daSSadaf Ebrahimi# 228*f5c631daSSadaf Ebrahimi# For directory matches, leading and trailing slashes are added first (so that 229*f5c631daSSadaf Ebrahimi# "*/foo/*" matches all of 'foo/bar', 'bar/foo' and 'bar/foo/bar'). 230*f5c631daSSadaf Ebrahimidef get_source_files( 231*f5c631daSSadaf Ebrahimi include = ['*.h', '*.cc'], 232*f5c631daSSadaf Ebrahimi include_dirs = ['/src/*', '/test/*', '/examples/*', '/benchmarks/*'], 233*f5c631daSSadaf Ebrahimi exclude = [], 234*f5c631daSSadaf Ebrahimi exclude_dirs = ['.*', '*/traces/*']): 235*f5c631daSSadaf Ebrahimi def NameMatchesAnyFilter(name, filters): 236*f5c631daSSadaf Ebrahimi for f in filters: 237*f5c631daSSadaf Ebrahimi if fnmatch.fnmatch(name, f): 238*f5c631daSSadaf Ebrahimi return True 239*f5c631daSSadaf Ebrahimi return False 240*f5c631daSSadaf Ebrahimi 241*f5c631daSSadaf Ebrahimi files_found = [] 242*f5c631daSSadaf Ebrahimi for root, dirs, files in os.walk(config.dir_root): 243*f5c631daSSadaf Ebrahimi git_path = os.path.join('/', os.path.relpath(root, config.dir_root), '') 244*f5c631daSSadaf Ebrahimi if NameMatchesAnyFilter(git_path, include_dirs) and \ 245*f5c631daSSadaf Ebrahimi not NameMatchesAnyFilter(git_path, exclude_dirs): 246*f5c631daSSadaf Ebrahimi files_found += [ 247*f5c631daSSadaf Ebrahimi os.path.join(root, name) 248*f5c631daSSadaf Ebrahimi for name in files 249*f5c631daSSadaf Ebrahimi if NameMatchesAnyFilter(name, include) and \ 250*f5c631daSSadaf Ebrahimi not NameMatchesAnyFilter(name, exclude) 251*f5c631daSSadaf Ebrahimi ] 252*f5c631daSSadaf Ebrahimi return files_found 253