## @file\r
# Check a patch for various format issues\r
#\r
-# Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.<BR>\r
#\r
-# This program and the accompanying materials are licensed and made\r
-# available under the terms and conditions of the BSD License which\r
-# accompanies this distribution. The full text of the license may be\r
-# found at http://opensource.org/licenses/bsd-license.php\r
-#\r
-# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS"\r
-# BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER\r
-# EXPRESS OR IMPLIED.\r
+# SPDX-License-Identifier: BSD-2-Clause-Patent\r
#\r
\r
from __future__ import print_function\r
\r
VersionNumber = '0.1'\r
-__copyright__ = "Copyright (c) 2015, Intel Corporation All rights reserved."\r
+__copyright__ = "Copyright (c) 2015 - 2016, Intel Corporation All rights reserved."\r
\r
import email\r
import argparse\r
print(prefix, line)\r
count += 1\r
\r
+ # Find 'contributed-under:' at the start of a line ignoring case and\r
+ # requires ':' to be present. Matches if there is white space before\r
+ # the tag or between the tag and the ':'.\r
+ contributed_under_re = \\r
+ re.compile(r'^\s*contributed-under\s*:', re.MULTILINE|re.IGNORECASE)\r
+\r
def check_contributed_under(self):\r
- cu_msg='Contributed-under: TianoCore Contribution Agreement 1.0'\r
- if self.msg.find(cu_msg) < 0:\r
- self.error('Missing Contributed-under! (Note: this must be ' +\r
- 'added by the code contributor!)')\r
+ match = self.contributed_under_re.search(self.msg)\r
+ if match is not None:\r
+ self.error('Contributed-under! (Note: this must be ' +\r
+ 'removed by the code contributor!)')\r
\r
@staticmethod\r
def make_signature_re(sig, re_input=False):\r
self.error('Empty commit message!')\r
return\r
\r
- if count >= 1 and len(lines[0]) > 76:\r
+ if count >= 1 and len(lines[0].rstrip()) >= 72:\r
self.error('First line of commit message (subject line) ' +\r
'is too long.')\r
\r
'empty.')\r
\r
for i in range(2, count):\r
- if (len(lines[i]) > 76 and\r
+ if (len(lines[i]) >= 76 and\r
len(lines[i].split()) > 1 and\r
not lines[i].startswith('git-svn-id:')):\r
self.error('Line %d of commit message is too long.' % (i + 1))\r
self.count = len(self.lines)\r
self.line_num = 0\r
self.state = START\r
+ self.new_bin = []\r
while self.line_num < self.count and self.format_ok:\r
line_num = self.line_num\r
self.run()\r
return\r
if self.ok:\r
print('The code passed all checks.')\r
+ if self.new_bin:\r
+ print('\nWARNING - The following binary files will be added ' +\r
+ 'into the repository:')\r
+ for binary in self.new_bin:\r
+ print(' ' + binary)\r
\r
def run(self):\r
line = self.lines[self.line_num]\r
if line.startswith('@@ '):\r
self.state = PRE_PATCH\r
elif len(line) >= 1 and line[0] not in ' -+' and \\r
- not line.startswith(r'\ No newline '):\r
+ not line.startswith('\r\n') and \\r
+ not line.startswith(r'\ No newline ') and not self.binary:\r
for line in self.lines[self.line_num + 1:]:\r
if line.startswith('diff --git'):\r
self.format_error('diff found after end of patch')\r
if self.state == START:\r
if line.startswith('diff --git'):\r
self.state = PRE_PATCH\r
- self.set_filename(None)\r
+ self.filename = line[13:].split(' ', 1)[0]\r
+ self.is_newfile = False\r
+ self.force_crlf = not self.filename.endswith('.sh')\r
elif len(line.rstrip()) != 0:\r
self.format_error("didn't find diff command")\r
self.line_num += 1\r
elif self.state == PRE_PATCH:\r
- if line.startswith('+++ b/'):\r
- self.set_filename(line[6:].rstrip())\r
if line.startswith('@@ '):\r
self.state = PATCH\r
+ self.binary = False\r
+ elif line.startswith('GIT binary patch') or \\r
+ line.startswith('Binary files'):\r
+ self.state = PATCH\r
+ self.binary = True\r
+ if self.is_newfile:\r
+ self.new_bin.append(self.filename)\r
else:\r
ok = False\r
+ self.is_newfile = self.newfile_prefix_re.match(line)\r
for pfx in self.pre_patch_prefixes:\r
if line.startswith(pfx):\r
ok = True\r
self.format_error("didn't find diff hunk marker (@@)")\r
self.line_num += 1\r
elif self.state == PATCH:\r
- if line.startswith('-'):\r
+ if self.binary:\r
+ pass\r
+ elif line.startswith('-'):\r
pass\r
elif line.startswith('+'):\r
self.check_added_line(line[1:])\r
+ elif line.startswith('\r\n'):\r
+ pass\r
elif line.startswith(r'\ No newline '):\r
pass\r
elif not line.startswith(' '):\r
'old mode ',\r
'new mode ',\r
'similarity index ',\r
+ 'copy from ',\r
+ 'copy to ',\r
'rename ',\r
- 'Binary files ',\r
)\r
\r
line_endings = ('\r\n', '\n\r', '\n', '\r')\r
\r
- def set_filename(self, filename):\r
- self.hunk_filename = filename\r
- if filename:\r
- self.force_crlf = not filename.endswith('.sh')\r
- else:\r
- self.force_crlf = True\r
+ newfile_prefix_re = \\r
+ re.compile(r'''^\r
+ index\ 0+\.\.\r
+ ''',\r
+ re.VERBOSE)\r
\r
def added_line_error(self, msg, line):\r
lines = [ msg ]\r
- if self.hunk_filename is not None:\r
- lines.append('File: ' + self.hunk_filename)\r
+ if self.filename is not None:\r
+ lines.append('File: ' + self.filename)\r
lines.append('Line: ' + line)\r
\r
self.error(*lines)\r
\r
+ old_debug_re = \\r
+ re.compile(r'''\r
+ DEBUG \s* \( \s* \( \s*\r
+ (?: DEBUG_[A-Z_]+ \s* \| \s*)*\r
+ EFI_D_ ([A-Z_]+)\r
+ ''',\r
+ re.VERBOSE)\r
+\r
def check_added_line(self, line):\r
eol = ''\r
for an_eol in self.line_endings:\r
if len(stripped) < len(line):\r
self.added_line_error('Trailing whitespace found', line)\r
\r
+ mo = self.old_debug_re.search(line)\r
+ if mo is not None:\r
+ self.added_line_error('EFI_D_' + mo.group(1) + ' was used, '\r
+ 'but DEBUG_' + mo.group(1) +\r
+ ' is now recommended', line)\r
+\r
split_diff_re = re.compile(r'''\r
(?P<cmd>\r
^ diff \s+ --git \s+ a/.+ \s+ b/.+ $\r
''',\r
re.IGNORECASE | re.VERBOSE | re.MULTILINE)\r
\r
+ subject_prefix_re = \\r
+ re.compile(r'''^\r
+ \s* (\[\r
+ [^\[\]]* # Allow all non-brackets\r
+ \])* \s*\r
+ ''',\r
+ re.VERBOSE)\r
+\r
def find_patch_pieces(self):\r
if sys.version_info < (3, 0):\r
patch = self.patch.encode('ascii', 'ignore')\r
\r
self.commit_subject = pmail['subject'].replace('\r\n', '')\r
self.commit_subject = self.commit_subject.replace('\n', '')\r
-\r
- pfx_start = self.commit_subject.find('[')\r
- if pfx_start >= 0:\r
- pfx_end = self.commit_subject.find(']')\r
- if pfx_end > pfx_start:\r
- self.commit_prefix = self.commit_subject[pfx_start + 1 : pfx_end]\r
- self.commit_subject = self.commit_subject[pfx_end + 1 :].lstrip()\r
-\r
+ self.commit_subject = self.subject_prefix_re.sub('', self.commit_subject, 1)\r
\r
class CheckGitCommits:\r
"""Reads patches from git based on the specified git revision range.\r
print('Checking git commit:', commit)\r
patch = self.read_patch_from_git(commit)\r
self.ok &= CheckOnePatch(commit, patch).ok\r
+ if not commits:\r
+ print("Couldn't find commit matching: '{}'".format(rev_spec))\r
\r
def read_commit_list_from_git(self, rev_spec, max_count):\r
# Run git to get the commit patch\r
cmd.append('--max-count=' + str(max_count))\r
cmd.append(rev_spec)\r
out = self.run_git(*cmd)\r
- return out.split()\r
+ return out.split() if out else []\r
\r
def read_patch_from_git(self, commit):\r
# Run git to get the commit patch\r
- return self.run_git('show', '--pretty=email', commit)\r
+ return self.run_git('show', '--pretty=email', '--no-textconv', commit)\r
\r
def run_git(self, *args):\r
cmd = [ 'git' ]\r
p = subprocess.Popen(cmd,\r
stdout=subprocess.PIPE,\r
stderr=subprocess.STDOUT)\r
- return p.communicate()[0].decode('utf-8', 'ignore')\r
+ Result = p.communicate()\r
+ return Result[0].decode('utf-8', 'ignore') if Result[0] and Result[0].find(b"fatal")!=0 else None\r
\r
class CheckOnePatchFile:\r
"""Performs a patch check for a single file.\r