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 29import os 30import sys 31import argparse 32import re 33import platform 34 35import util 36import git 37 38# Google's cpplint.py from depot_tools is the linter used here. 39CPP_LINTER_RULES = ''' 40build/class 41build/deprecated 42build/endif_comment 43build/forward_decl 44build/include_order 45build/printf_format 46build/storage_class 47legal/copyright 48readability/boost 49readability/braces 50readability/casting 51readability/constructors 52readability/fn_size 53readability/function 54readability/multiline_comment 55readability/multiline_string 56readability/streams 57readability/utf8 58runtime/arrays 59runtime/casting 60runtime/deprecated_fn 61runtime/explicit 62runtime/int 63runtime/memset 64runtime/mutex 65runtime/nonconf 66runtime/printf 67runtime/printf_format 68runtime/references 69runtime/rtti 70runtime/sizeof 71runtime/string 72runtime/virtual 73runtime/vlog 74whitespace/blank_line 75whitespace/braces 76whitespace/comma 77whitespace/comments 78whitespace/end_of_line 79whitespace/ending_newline 80whitespace/indent 81whitespace/labels 82whitespace/line_length 83whitespace/newline 84whitespace/operators 85whitespace/parens 86whitespace/tab 87whitespace/todo 88'''.split() 89 90 91def 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 111def 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 124def 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 137NOT_RUN = 'NOT RUN' 138PASSED = 'PASSED' 139FAILED = 'FAILED' 140 141 142class 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 165class 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 186if __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