#!/usr/bin/env python2.7 # Copyright 2013, ARM Limited # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of ARM Limited nor the names of its contributors may be # used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os import sys import argparse import re import platform import util import git # Google's cpplint.py from depot_tools is the linter used here. CPP_LINTER_RULES = ''' build/class build/deprecated build/endif_comment build/forward_decl build/include_order build/printf_format build/storage_class legal/copyright readability/boost readability/braces readability/casting readability/constructors readability/fn_size readability/function readability/multiline_comment readability/multiline_string readability/streams readability/utf8 runtime/arrays runtime/casting runtime/deprecated_fn runtime/explicit runtime/int runtime/memset runtime/mutex runtime/nonconf runtime/printf runtime/printf_format runtime/references runtime/rtti runtime/sizeof runtime/string runtime/virtual runtime/vlog whitespace/blank_line whitespace/braces whitespace/comma whitespace/comments whitespace/end_of_line whitespace/ending_newline whitespace/indent whitespace/labels whitespace/line_length whitespace/newline whitespace/operators whitespace/parens whitespace/tab whitespace/todo '''.split() def BuildOptions(): result = argparse.ArgumentParser(description='Run the linter and unit tests.') result.add_argument('--verbose', '-v', action='store_true', help='Print all tests output at the end.') result.add_argument('--notest', action='store_true', help='Do not run tests. Run the linter only.') result.add_argument('--nolint', action='store_true', help='Do not run the linter. Run the tests only.') result.add_argument('--noclean', action='store_true', help='Do not clean before build.') result.add_argument('--jobs', '-j', metavar='N', type=int, default=1, help='Allow N jobs at once.') sim_default = 'off' if platform.machine() == 'aarch64' else 'on' result.add_argument('--simulator', action='store', choices=['on', 'off'], default=sim_default, help='''Explicitly enable or disable the simulator. On this system, the default is "''' + sim_default + '".') return result.parse_args() def CleanBuildSystem(): def clean(mode): if args.verbose: print('Cleaning ' + mode + ' mode cctest...') command = 'scons mode=%s simulator=%s target=cctest --clean' % \ (mode, args.simulator) status, output = util.getstatusoutput(command) if status != 0: print(output) util.abort('Failed cleaning cctest: ' + command) clean('debug') clean('release') def BuildRequiredObjects(): def build(mode): if args.verbose: print('Building ' + mode + ' mode cctest...') command = 'scons mode=%s simulator=%s target=cctest -j%u' % \ (mode, args.simulator, args.jobs) status, output = util.getstatusoutput(command) if status != 0: print(output) util.abort('Failed building cctest: ' + command) build('debug') build('release') NOT_RUN = 'NOT RUN' PASSED = 'PASSED' FAILED = 'FAILED' class Test: def __init__(self, name, command, get_summary = util.last_line): self.name = name self.command = command self.get_summary = get_summary self.output = NOT_RUN self.status = NOT_RUN self.summary = NOT_RUN def Run(self): if args.verbose: print('Running ' + self.name + '...') retcode, self.output = util.getstatusoutput(self.command) self.status = PASSED if retcode == 0 else FAILED self.summary = self.get_summary(self.output) def PrintOutcome(self): print(('%-26s : %s') % (self.name, self.summary)) def PrintOutput(self): print('\n\n=== OUTPUT of: ' + self.command + '\n') print(self.output) class Tester: def __init__(self): self.tests = [] def AddTest(self, test): self.tests.append(test) def RunAll(self): result = PASSED for test in self.tests: test.Run() if test.status != PASSED: result = FAILED test.PrintOutcome() print('Presubmit tests ' + result + '.') def PrintFailedTestOutput(self): for test in self.tests: if test.status == FAILED: test.PrintOutput(); if __name__ == '__main__': original_dir = os.path.abspath('.') # $ROOT/tools/presubmit.py root_dir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0]))) os.chdir(root_dir) args = BuildOptions() if not args.nolint and not git.is_git_repository_root(): print 'WARNING: This is not a Git repository. The linter will not run.' args.nolint = True tester = Tester() if not args.nolint: CPP_EXT_REGEXP = re.compile('\.(cc|h)$') SIM_TRACES_REGEXP = re.compile('test-simulator-traces-a64\.h$') def is_linter_input(filename): # Don't lint the simulator traces file; it takes a very long time to check # and it's (mostly) generated automatically anyway. if SIM_TRACES_REGEXP.search(filename): return False # Otherwise, lint all C++ files. return CPP_EXT_REGEXP.search(filename) != None lint_args = '--filter=-,+' + ',+'.join(CPP_LINTER_RULES) + ' ' tracked_files = git.get_tracked_files().split() tracked_files = filter(is_linter_input, tracked_files) tracked_files = ' '.join(tracked_files) lint = Test('cpp lint', 'cpplint.py ' + lint_args + tracked_files) tester.AddTest(lint) if not args.notest: if not args.noclean: CleanBuildSystem() BuildRequiredObjects() def command(*test_args): if args.verbose: return 'tools/test.py --verbose ' + ' '.join(test_args) else: return 'tools/test.py ' + ' '.join(test_args) if args.simulator == 'on': tester.AddTest(Test('cctest release (debugger)', command('--cctest=cctest_sim', '--debugger'))) tester.AddTest(Test('cctest debug (debugger)', command('--cctest=cctest_sim_g', '--debugger'))) tester.AddTest(Test('cctest release (simulator)', command('--cctest=cctest_sim'))) tester.AddTest(Test('cctest debug (simulator)', command('--cctest=cctest_sim_g'))) else: tester.AddTest(Test('cctest release', command('--cctest=cctest'))) tester.AddTest(Test('cctest debug', command('--cctest=cctest_g'))) tester.RunAll() # If tests failed, print their output. tester.PrintFailedTestOutput() if git.is_git_repository_root(): untracked_files = git.get_untracked_files() if untracked_files: print '\nWARNING: The following files are untracked:' for f in untracked_files: print f.lstrip('?')