1 #!/usr/bin/env python2.7 2 3 # Copyright 2013, ARM Limited 4 # All rights reserved. 5 # 6 # Redistribution and use in source and binary forms, with or without 7 # modification, are permitted provided that the following conditions are met: 8 # 9 # * Redistributions of source code must retain the above copyright notice, 10 # this list of conditions and the following disclaimer. 11 # * Redistributions in binary form must reproduce the above copyright notice, 12 # this list of conditions and the following disclaimer in the documentation 13 # and/or other materials provided with the distribution. 14 # * Neither the name of ARM Limited nor the names of its contributors may be 15 # used to endorse or promote products derived from this software without 16 # specific prior written permission. 17 # 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 import os 30 import sys 31 import argparse 32 import re 33 import platform 34 35 import util 36 import git 37 38 # Google's cpplint.py from depot_tools is the linter used here. 39 CPP_LINTER_RULES = ''' 40 build/class 41 build/deprecated 42 build/endif_comment 43 build/forward_decl 44 build/include_order 45 build/printf_format 46 build/storage_class 47 legal/copyright 48 readability/boost 49 readability/braces 50 readability/casting 51 readability/constructors 52 readability/fn_size 53 readability/function 54 readability/multiline_comment 55 readability/multiline_string 56 readability/streams 57 readability/utf8 58 runtime/arrays 59 runtime/casting 60 runtime/deprecated_fn 61 runtime/explicit 62 runtime/int 63 runtime/memset 64 runtime/mutex 65 runtime/nonconf 66 runtime/printf 67 runtime/printf_format 68 runtime/references 69 runtime/rtti 70 runtime/sizeof 71 runtime/string 72 runtime/virtual 73 runtime/vlog 74 whitespace/blank_line 75 whitespace/braces 76 whitespace/comma 77 whitespace/comments 78 whitespace/end_of_line 79 whitespace/ending_newline 80 whitespace/indent 81 whitespace/labels 82 whitespace/line_length 83 whitespace/newline 84 whitespace/operators 85 whitespace/parens 86 whitespace/tab 87 whitespace/todo 88 '''.split() 89 90 91 def BuildOptions(): 92 result = argparse.ArgumentParser(description='Run the linter and unit tests.') 93 result.add_argument('--verbose', '-v', action='store_true', 94 help='Print all tests output at the end.') 95 result.add_argument('--notest', action='store_true', 96 help='Do not run tests. Run the linter only.') 97 result.add_argument('--nolint', action='store_true', 98 help='Do not run the linter. Run the tests only.') 99 result.add_argument('--noclean', action='store_true', 100 help='Do not clean before build.') 101 result.add_argument('--jobs', '-j', metavar='N', type=int, default=1, 102 help='Allow N jobs at once.') 103 sim_default = 'off' if platform.machine() == 'aarch64' else 'on' 104 result.add_argument('--simulator', action='store', choices=['on', 'off'], 105 default=sim_default, 106 help='''Explicitly enable or disable the simulator. On 107 this system, the default is "''' + sim_default + '".') 108 return result.parse_args() 109 110 111 def CleanBuildSystem(): 112 def clean(mode): 113 if args.verbose: print('Cleaning ' + mode + ' mode cctest...') 114 command = 'scons mode=%s simulator=%s target=cctest --clean' % \ 115 (mode, args.simulator) 116 status, output = util.getstatusoutput(command) 117 if status != 0: 118 print(output) 119 util.abort('Failed cleaning cctest: ' + command) 120 clean('debug') 121 clean('release') 122 123 124 def BuildRequiredObjects(): 125 def build(mode): 126 if args.verbose: print('Building ' + mode + ' mode cctest...') 127 command = 'scons mode=%s simulator=%s target=cctest -j%u' % \ 128 (mode, args.simulator, args.jobs) 129 status, output = util.getstatusoutput(command) 130 if status != 0: 131 print(output) 132 util.abort('Failed building cctest: ' + command) 133 build('debug') 134 build('release') 135 136 137 NOT_RUN = 'NOT RUN' 138 PASSED = 'PASSED' 139 FAILED = 'FAILED' 140 141 142 class Test: 143 def __init__(self, name, command, get_summary = util.last_line): 144 self.name = name 145 self.command = command 146 self.get_summary = get_summary 147 self.output = NOT_RUN 148 self.status = NOT_RUN 149 self.summary = NOT_RUN 150 151 def Run(self): 152 if args.verbose: print('Running ' + self.name + '...') 153 retcode, self.output = util.getstatusoutput(self.command) 154 self.status = PASSED if retcode == 0 else FAILED 155 self.summary = self.get_summary(self.output) 156 157 def PrintOutcome(self): 158 print(('%-26s : %s') % (self.name, self.summary)) 159 160 def PrintOutput(self): 161 print('\n\n=== OUTPUT of: ' + self.command + '\n') 162 print(self.output) 163 164 165 class Tester: 166 def __init__(self): 167 self.tests = [] 168 169 def AddTest(self, test): 170 self.tests.append(test) 171 172 def RunAll(self): 173 result = PASSED 174 for test in self.tests: 175 test.Run() 176 if test.status != PASSED: result = FAILED 177 test.PrintOutcome() 178 print('Presubmit tests ' + result + '.') 179 180 def PrintFailedTestOutput(self): 181 for test in self.tests: 182 if test.status == FAILED: 183 test.PrintOutput(); 184 185 186 if __name__ == '__main__': 187 original_dir = os.path.abspath('.') 188 # $ROOT/tools/presubmit.py 189 root_dir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) 190 os.chdir(root_dir) 191 args = BuildOptions() 192 193 if not args.nolint and not git.is_git_repository_root(): 194 print 'WARNING: This is not a Git repository. The linter will not run.' 195 args.nolint = True 196 197 tester = Tester() 198 if not args.nolint: 199 CPP_EXT_REGEXP = re.compile('\.(cc|h)$') 200 SIM_TRACES_REGEXP = re.compile('test-simulator-traces-a64\.h$') 201 def is_linter_input(filename): 202 # Don't lint the simulator traces file; it takes a very long time to check 203 # and it's (mostly) generated automatically anyway. 204 if SIM_TRACES_REGEXP.search(filename): return False 205 # Otherwise, lint all C++ files. 206 return CPP_EXT_REGEXP.search(filename) != None 207 208 lint_args = '--filter=-,+' + ',+'.join(CPP_LINTER_RULES) + ' ' 209 tracked_files = git.get_tracked_files().split() 210 tracked_files = filter(is_linter_input, tracked_files) 211 tracked_files = ' '.join(tracked_files) 212 lint = Test('cpp lint', 'cpplint.py ' + lint_args + tracked_files) 213 tester.AddTest(lint) 214 215 if not args.notest: 216 if not args.noclean: 217 CleanBuildSystem() 218 BuildRequiredObjects() 219 220 def command(*test_args): 221 if args.verbose: 222 return 'tools/test.py --verbose ' + ' '.join(test_args) 223 else: 224 return 'tools/test.py ' + ' '.join(test_args) 225 226 if args.simulator == 'on': 227 tester.AddTest(Test('cctest release (debugger)', 228 command('--cctest=cctest_sim', '--debugger'))) 229 tester.AddTest(Test('cctest debug (debugger)', 230 command('--cctest=cctest_sim_g', '--debugger'))) 231 tester.AddTest(Test('cctest release (simulator)', 232 command('--cctest=cctest_sim'))) 233 tester.AddTest(Test('cctest debug (simulator)', 234 command('--cctest=cctest_sim_g'))) 235 else: 236 tester.AddTest(Test('cctest release', command('--cctest=cctest'))) 237 tester.AddTest(Test('cctest debug', command('--cctest=cctest_g'))) 238 239 tester.RunAll() 240 241 # If tests failed, print their output. 242 tester.PrintFailedTestOutput() 243 244 if git.is_git_repository_root(): 245 untracked_files = git.get_untracked_files() 246 if untracked_files: 247 print '\nWARNING: The following files are untracked:' 248 for f in untracked_files: 249 print f.lstrip('?') 250