1# Copyright (c) 2021, Google Inc. All rights reserved. 2# 3# Redistribution and use in source and binary forms, with or without 4# modification, are permitted provided that the following conditions are 5# met: 6# 7# * Redistributions of source code must retain the above copyright 8# notice, this list of conditions and the following disclaimer. 9# 10# * Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in 12# the documentation and/or other materials provided with the 13# distribution. 14# 15# * Neither the name of Google nor the names of its contributors may 16# be used to endorse or promote products derived from this software 17# without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30"""Top-level presubmit script for libwebm. 31 32See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for 33details on the presubmit API built into depot_tools. 34""" 35import re 36import subprocess2 37 38USE_PYTHON3 = True 39_BASH_INDENTATION = "2" 40_GIT_COMMIT_SUBJECT_LENGTH = 65 41_INCLUDE_BASH_FILES_ONLY = [r".*\.sh$"] 42_INCLUDE_SOURCE_FILES_ONLY = [r".*\.(c|cc|[hc]pp|h)$"] 43_LIBWEBM_MAX_LINE_LENGTH = 80 44 45 46def _CheckCommitSubjectLength(input_api, output_api): 47 """Ensures commit's subject length is no longer than 65 chars.""" 48 name = "git-commit subject" 49 cmd = ["git", "log", "-1", "--pretty=%s"] 50 start = input_api.time.time() 51 proc = subprocess2.Popen( 52 cmd, 53 stderr=subprocess2.PIPE, 54 stdout=subprocess2.PIPE, 55 universal_newlines=True) 56 57 stdout, _ = proc.communicate() 58 duration = input_api.time.time() - start 59 60 if not re.match(r"^Revert", 61 stdout) and (len(stdout) - 1) > _GIT_COMMIT_SUBJECT_LENGTH: 62 failure_msg = ( 63 "The commit subject: %s is too long (%d chars)\n" 64 "Try to keep this to 50 or less (up to 65 is permitted for " 65 "non-reverts).\n" 66 "https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-" 67 "Project#_commit_guidelines") % (stdout, len(stdout) - 1) 68 return output_api.PresubmitError("%s\n (%4.2fs) failed\n%s" % 69 (name, duration, failure_msg)) 70 71 return output_api.PresubmitResult("%s\n (%4.2fs) success" % (name, duration)) 72 73 74def _GetFilesToSkip(input_api): 75 """Skips libwebm-specific files.""" 76 return list(input_api.DEFAULT_FILES_TO_SKIP) + [ 77 r"\.pylintrc$", 78 ] 79 80 81def _CheckChangeLintsClean(input_api, output_api): 82 """Makes sure that libwebm/ code is cpplint clean.""" 83 sources = lambda x: input_api.FilterSourceFile( 84 x, files_to_check=_INCLUDE_SOURCE_FILES_ONLY, files_to_skip=None) 85 return input_api.canned_checks.CheckChangeLintsClean(input_api, output_api, 86 sources) 87 88 89def _RunShellCheckCmd(input_api, output_api, bash_file): 90 """shellcheck command wrapper.""" 91 cmd = ["shellcheck", "-x", "-oall", "-sbash", bash_file] 92 name = "Check %s file." % bash_file 93 start = input_api.time.time() 94 output, rc = subprocess2.communicate( 95 cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True) 96 duration = input_api.time.time() - start 97 if rc == 0: 98 return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" % 99 (name, " ".join(cmd), duration)) 100 return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" % 101 (name, " ".join(cmd), duration, output[1])) 102 103 104def _RunShfmtCheckCmd(input_api, output_api, bash_file): 105 """shfmt command wrapper.""" 106 cmd = [ 107 "shfmt", "-i", _BASH_INDENTATION, "-bn", "-ci", "-sr", "-kp", "-d", 108 bash_file 109 ] 110 name = "Check %s file." % bash_file 111 start = input_api.time.time() 112 output, rc = subprocess2.communicate( 113 cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True) 114 duration = input_api.time.time() - start 115 if rc == 0: 116 return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" % 117 (name, " ".join(cmd), duration)) 118 return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" % 119 (name, " ".join(cmd), duration, output[1])) 120 121 122def _RunCmdOnCheckedFiles(input_api, output_api, run_cmd, files_to_check): 123 """Ensure that libwebm/ files are clean.""" 124 file_filter = lambda x: input_api.FilterSourceFile( 125 x, files_to_check=files_to_check, files_to_skip=None) 126 127 affected_files = input_api.change.AffectedFiles(file_filter=file_filter) 128 results = [ 129 run_cmd(input_api, output_api, f.AbsoluteLocalPath()) 130 for f in affected_files 131 ] 132 return results 133 134 135def _CommonChecks(input_api, output_api): 136 results = [] 137 results.extend( 138 input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol( 139 input_api, output_api)) 140 results.extend( 141 input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api)) 142 results.extend( 143 input_api.canned_checks.CheckChangeHasNoStrayWhitespace( 144 input_api, output_api)) 145 results.append(_CheckCommitSubjectLength(input_api, output_api)) 146 147 source_file_filter = lambda x: input_api.FilterSourceFile( 148 x, files_to_skip=_GetFilesToSkip(input_api)) 149 results.extend( 150 input_api.canned_checks.CheckLongLines( 151 input_api, 152 output_api, 153 maxlen=_LIBWEBM_MAX_LINE_LENGTH, 154 source_file_filter=source_file_filter)) 155 156 results.extend( 157 input_api.canned_checks.CheckPatchFormatted( 158 input_api, 159 output_api, 160 check_clang_format=True, 161 check_python=True, 162 result_factory=output_api.PresubmitError)) 163 results.extend(_CheckChangeLintsClean(input_api, output_api)) 164 165 # Run pylint. 166 results.extend( 167 input_api.canned_checks.RunPylint( 168 input_api, 169 output_api, 170 files_to_skip=_GetFilesToSkip(input_api), 171 pylintrc=".pylintrc", 172 version="2.7")) 173 174 # Binaries shellcheck and shfmt are not installed in depot_tools. 175 # Installation is needed 176 try: 177 subprocess2.communicate(["shellcheck", "--version"]) 178 results.extend( 179 _RunCmdOnCheckedFiles(input_api, output_api, _RunShellCheckCmd, 180 _INCLUDE_BASH_FILES_ONLY)) 181 print("shfmt") 182 subprocess2.communicate(["shfmt", "-version"]) 183 results.extend( 184 _RunCmdOnCheckedFiles(input_api, output_api, _RunShfmtCheckCmd, 185 _INCLUDE_BASH_FILES_ONLY)) 186 except OSError as os_error: 187 results.append( 188 output_api.PresubmitPromptWarning( 189 "%s\nPlease install missing binaries locally." % os_error.args[0])) 190 return results 191 192 193def CheckChangeOnUpload(input_api, output_api): 194 results = [] 195 results.extend(_CommonChecks(input_api, output_api)) 196 return results 197 198 199def CheckChangeOnCommit(input_api, output_api): 200 results = [] 201 results.extend(_CommonChecks(input_api, output_api)) 202 return results 203