blob: 240c69724630a8c6b3ae616f8b48ead4cf9d30fb [file] [log] [blame]
Alexandre Ramesb78f1392016-07-01 14:22:22 +01001# Copyright 2015, VIXL authors
armvixlad96eda2013-06-14 11:42:37 +01002# 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
Pierre Langloisf737e0a2016-11-02 13:08:11 +000027from distutils.version import LooseVersion
Jacob Bramleyf89cb482019-06-28 15:53:18 +010028import config
29import fnmatch
armvixldb644342015-07-21 11:37:10 +010030import glob
Pierre Langloisf737e0a2016-11-02 13:08:11 +000031import operator
armvixl6e2c8272015-03-31 11:04:14 +010032import os
armvixlad96eda2013-06-14 11:42:37 +010033import re
armvixldb644342015-07-21 11:37:10 +010034import shlex
35import subprocess
36import sys
37
38
39def ListCCFilesWithoutExt(path):
snickolls-arm2decd2c2024-01-17 11:24:49 +000040 src_files = glob.glob(os.path.join(path, '*.cc'))
41 return [os.path.splitext(os.path.basename(x))[0] for x in src_files]
armvixlad96eda2013-06-14 11:42:37 +010042
43
44def abort(message):
45 print('ABORTING: ' + message)
46 sys.exit(1)
47
48
Jacob Bramley631ad382019-07-05 14:55:13 +010049def getstatusoutput(command):
snickolls-arm2decd2c2024-01-17 11:24:49 +000050 return subprocess.getstatusoutput(command)
armvixlad96eda2013-06-14 11:42:37 +010051
52
armvixl0f35e362016-05-10 13:57:58 +010053def IsCommandAvailable(command):
54 retcode, unused_output = getstatusoutput('which %s' % command)
55 return retcode == 0
56
57
armvixldb644342015-07-21 11:37:10 +010058def ensure_dir(path_name):
59 if not os.path.exists(path_name):
60 os.makedirs(path_name)
armvixl6e2c8272015-03-31 11:04:14 +010061
62
armvixldb644342015-07-21 11:37:10 +010063# 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)
Alexandre Rames81c76e62016-07-19 09:53:09 +010069
70def relrealpath(path, start=os.getcwd()):
71 return os.path.relpath(os.path.realpath(path), start)
Pierre Langloisa3b21462016-08-04 16:01:51 +010072
Pierre Langloisf737e0a2016-11-02 13:08:11 +000073# Query the compiler about its preprocessor directives and return all of them as
Josh Sorefb43d6ef2022-08-03 12:47:14 -040074# a dictionary.
Pierre Langlois8253a3c2016-12-14 18:54:22 +000075def GetCompilerDirectives(env):
Anthony Barbier9c4ba7a2019-02-15 15:20:25 +000076 args = [env['compiler']]
Josh Sorefb43d6ef2022-08-03 12:47:14 -040077 # Pass the CXXFLAGS variables to the compile, in case we've used "-m32" to
Pierre Langlois8253a3c2016-12-14 18:54:22 +000078 # compile for i386.
79 if env['CXXFLAGS']:
80 args.append(str(env['CXXFLAGS']))
81 args += ['-E', '-dM', '-']
82
Pierre Langloisa3b21462016-08-04 16:01:51 +010083 # Instruct the compiler to dump all its preprocessor macros.
Matthew Benthamfe7cb102020-05-30 13:28:36 +010084 dump = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
85 universal_newlines=True)
Pierre Langloisa3b21462016-08-04 16:01:51 +010086 out, _ = dump.communicate()
Pierre Langloisf737e0a2016-11-02 13:08:11 +000087 return {
88 # Extract the macro name as key and value as element.
89 match.group(1): match.group(2)
Pierre Langloisa3b21462016-08-04 16:01:51 +010090 for match in [
91 # Capture macro name.
Jacob Bramleya22e9a52024-05-23 11:17:39 +010092 re.search(r'^#define (\S+?) (.+)$', macro)
Pierre Langloisa3b21462016-08-04 16:01:51 +010093 for macro in out.split('\n')
94 ]
95 # Filter out non-matches.
96 if match
Pierre Langloisf737e0a2016-11-02 13:08:11 +000097 }
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.
Jacob Bramley1fa6f062016-12-19 11:40:08 +0000102def GetHostArch(env):
103 directives = GetCompilerDirectives(env)
Pierre Langloisf737e0a2016-11-02 13:08:11 +0000104 if "__x86_64__" in directives:
Pierre Langloisa3b21462016-08-04 16:01:51 +0100105 return "x86_64"
Pierre Langlois8253a3c2016-12-14 18:54:22 +0000106 elif "__i386__" in directives:
107 return "i386"
Pierre Langloisf737e0a2016-11-02 13:08:11 +0000108 elif "__arm__" in directives:
Pierre Langloisa3b21462016-08-04 16:01:51 +0100109 return "aarch32"
Pierre Langloisf737e0a2016-11-02 13:08:11 +0000110 elif "__aarch64__" in directives:
Pierre Langloisa3b21462016-08-04 16:01:51 +0100111 return "aarch64"
112 else:
Josh Sorefb43d6ef2022-08-03 12:47:14 -0400113 raise Exception("Unsupported architecture")
Pierre Langloisf737e0a2016-11-02 13:08:11 +0000114
115# Class representing the compiler toolchain and version.
116class CompilerInformation(object):
Jacob Bramley1fa6f062016-12-19 11:40:08 +0000117 def __init__(self, env):
118 directives = GetCompilerDirectives(env)
Pierre Langloisf737e0a2016-11-02 13:08:11 +0000119 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):
Jacob Bramleya22e9a52024-05-23 11:17:39 +0100186 match = re.search(r'^(\S+)-(.*?)$', description)
Pierre Langloisf737e0a2016-11-02 13:08:11 +0000187 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))
Anthony Barbierf2986e12019-02-28 16:49:23 +0000194
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')
Jacob Bramleyf89cb482019-06-28 15:53:18 +0100217
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